diff options
Diffstat (limited to 'xs/src/slic3r/GUI')
76 files changed, 0 insertions, 34982 deletions
diff --git a/xs/src/slic3r/GUI/2DBed.cpp b/xs/src/slic3r/GUI/2DBed.cpp deleted file mode 100644 index e19f839cd..000000000 --- a/xs/src/slic3r/GUI/2DBed.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "2DBed.hpp" - -#include <wx/dcbuffer.h> -#include "BoundingBox.hpp" -#include "Geometry.hpp" -#include "ClipperUtils.hpp" - -namespace Slic3r { -namespace GUI { - -void Bed_2D::repaint() -{ - wxAutoBufferedPaintDC dc(this); - auto cw = GetSize().GetWidth(); - auto ch = GetSize().GetHeight(); - // when canvas is not rendered yet, size is 0, 0 - if (cw == 0) return ; - - if (m_user_drawn_background) { - // On all systems the AutoBufferedPaintDC() achieves double buffering. - // On MacOS the background is erased, on Windows the background is not erased - // and on Linux / GTK the background is erased to gray color. - // Fill DC with the background on Windows & Linux / GTK. - auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour - dc.SetPen(*new wxPen(color, 1, wxPENSTYLE_SOLID)); - dc.SetBrush(*new wxBrush(color, wxBRUSHSTYLE_SOLID)); - auto rect = GetUpdateRegion().GetBox(); - dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight()); - } - - // turn cw and ch from sizes to max coordinates - cw--; - ch--; - - auto cbb = BoundingBoxf(Vec2d(0, 0),Vec2d(cw, ch)); - // leave space for origin point - cbb.min(0) += 4; - cbb.max -= Vec2d(4., 4.); - - // leave space for origin label - cbb.max(1) -= 13; - - // read new size - cw = cbb.size()(0); - ch = cbb.size()(1); - - auto ccenter = cbb.center(); - - // get bounding box of bed shape in G - code coordinates - auto bed_shape = m_bed_shape; - auto bed_polygon = Polygon::new_scale(m_bed_shape); - auto bb = BoundingBoxf(m_bed_shape); - bb.merge(Vec2d(0, 0)); // origin needs to be in the visible area - auto bw = bb.size()(0); - auto bh = bb.size()(1); - auto bcenter = bb.center(); - - // calculate the scaling factor for fitting bed shape in canvas area - auto sfactor = std::min(cw/bw, ch/bh); - auto shift = Vec2d( - ccenter(0) - bcenter(0) * sfactor, - ccenter(1) - bcenter(1) * sfactor - ); - m_scale_factor = sfactor; - m_shift = Vec2d(shift(0) + cbb.min(0), - shift(1) - (cbb.max(1) - GetSize().GetHeight())); - - // draw bed fill - dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxSOLID)); - wxPointList pt_list; - for (auto pt: m_bed_shape) - { - Point pt_pix = to_pixels(pt); - pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1))); - } - dc.DrawPolygon(&pt_list, 0, 0); - - // draw grid - auto step = 10; // 1cm grid - Polylines polylines; - for (auto x = bb.min(0) - fmod(bb.min(0), step) + step; x < bb.max(0); x += step) { - polylines.push_back(Polyline::new_scale({ Vec2d(x, bb.min(1)), Vec2d(x, bb.max(1)) })); - } - for (auto y = bb.min(1) - fmod(bb.min(1), step) + step; y < bb.max(1); y += step) { - polylines.push_back(Polyline::new_scale({ Vec2d(bb.min(0), y), Vec2d(bb.max(0), y) })); - } - polylines = intersection_pl(polylines, bed_polygon); - - dc.SetPen(wxPen(wxColour(230, 230, 230), 1, wxSOLID)); - for (auto pl : polylines) - { - for (size_t i = 0; i < pl.points.size()-1; i++){ - Point pt1 = to_pixels(unscale(pl.points[i])); - Point pt2 = to_pixels(unscale(pl.points[i+1])); - dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1)); - } - } - - // draw bed contour - dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID)); - dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxTRANSPARENT)); - dc.DrawPolygon(&pt_list, 0, 0); - - auto origin_px = to_pixels(Vec2d(0, 0)); - - // draw axes - auto axes_len = 50; - auto arrow_len = 6; - auto arrow_angle = Geometry::deg2rad(45.0); - dc.SetPen(wxPen(wxColour(255, 0, 0), 2, wxSOLID)); // red - auto x_end = Vec2d(origin_px(0) + axes_len, origin_px(1)); - dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(x_end(0), x_end(1))); - for (auto angle : { -arrow_angle, arrow_angle }){ - auto end = Eigen::Translation2d(x_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- x_end) * Eigen::Vector2d(x_end(0) - arrow_len, x_end(1)); - dc.DrawLine(wxPoint(x_end(0), x_end(1)), wxPoint(end(0), end(1))); - } - - dc.SetPen(wxPen(wxColour(0, 255, 0), 2, wxSOLID)); // green - auto y_end = Vec2d(origin_px(0), origin_px(1) - axes_len); - dc.DrawLine(wxPoint(origin_px(0), origin_px(1)), wxPoint(y_end(0), y_end(1))); - for (auto angle : { -arrow_angle, arrow_angle }) { - auto end = Eigen::Translation2d(y_end) * Eigen::Rotation2Dd(angle) * Eigen::Translation2d(- y_end) * Eigen::Vector2d(y_end(0), y_end(1) + arrow_len); - dc.DrawLine(wxPoint(y_end(0), y_end(1)), wxPoint(end(0), end(1))); - } - - // draw origin - dc.SetPen(wxPen(wxColour(0, 0, 0), 1, wxSOLID)); - dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxSOLID)); - dc.DrawCircle(origin_px(0), origin_px(1), 3); - - static const auto origin_label = wxString("(0,0)"); - dc.SetTextForeground(wxColour(0, 0, 0)); - dc.SetFont(wxFont(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - auto extent = dc.GetTextExtent(origin_label); - const auto origin_label_x = origin_px(0) <= cw / 2 ? origin_px(0) + 1 : origin_px(0) - 1 - extent.GetWidth(); - const auto origin_label_y = origin_px(1) <= ch / 2 ? origin_px(1) + 1 : origin_px(1) - 1 - extent.GetHeight(); - dc.DrawText(origin_label, origin_label_x, origin_label_y); - - // draw current position - if (m_pos!= Vec2d(0, 0)) { - auto pos_px = to_pixels(m_pos); - dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxSOLID)); - dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxTRANSPARENT)); - dc.DrawCircle(pos_px(0), pos_px(1), 5); - - dc.DrawLine(pos_px(0) - 15, pos_px(1), pos_px(0) + 15, pos_px(1)); - dc.DrawLine(pos_px(0), pos_px(1) - 15, pos_px(0), pos_px(1) + 15); - } - - m_painted = true; -} - -// convert G - code coordinates into pixels -Point Bed_2D::to_pixels(Vec2d point){ - auto p = point * m_scale_factor + m_shift; - return Point(p(0), GetSize().GetHeight() - p(1)); -} - -void Bed_2D::mouse_event(wxMouseEvent event){ - if (!m_interactive) return; - if (!m_painted) return; - - auto pos = event.GetPosition(); - auto point = to_units(Point(pos.x, pos.y)); - if (event.LeftDown() || event.Dragging()) { - if (m_on_move) - m_on_move(point) ; - Refresh(); - } -} - -// convert pixels into G - code coordinates -Vec2d Bed_2D::to_units(Point point){ - return (Vec2d(point(0), GetSize().GetHeight() - point(1)) - m_shift) * (1. / m_scale_factor); -} - -void Bed_2D::set_pos(Vec2d pos){ - m_pos = pos; - Refresh(); -} - -} // GUI -} // Slic3r
\ No newline at end of file diff --git a/xs/src/slic3r/GUI/2DBed.hpp b/xs/src/slic3r/GUI/2DBed.hpp deleted file mode 100644 index d7a7f4260..000000000 --- a/xs/src/slic3r/GUI/2DBed.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef slic3r_2DBed_hpp_ -#define slic3r_2DBed_hpp_ - -#include <wx/wx.h> -#include "Config.hpp" - -namespace Slic3r { -namespace GUI { - -class Bed_2D : public wxPanel -{ - bool m_user_drawn_background = true; - - bool m_painted = false; - bool m_interactive = false; - double m_scale_factor; - Vec2d m_shift = Vec2d::Zero(); - Vec2d m_pos = Vec2d::Zero(); - std::function<void(Vec2d)> m_on_move = nullptr; - - Point to_pixels(Vec2d point); - Vec2d to_units(Point point); - void repaint(); - void mouse_event(wxMouseEvent event); - void set_pos(Vec2d pos); - -public: - Bed_2D(wxWindow* parent) - { - Create(parent, wxID_ANY, wxDefaultPosition, wxSize(250, -1), wxTAB_TRAVERSAL); -// m_user_drawn_background = $^O ne 'darwin'; -#ifdef __APPLE__ - m_user_drawn_background = false; -#endif /*__APPLE__*/ - Bind(wxEVT_PAINT, ([this](wxPaintEvent e) { repaint(); })); -// EVT_ERASE_BACKGROUND($self, sub{}) if $self->{user_drawn_background}; -// Bind(EVT_MOUSE_EVENTS, ([this](wxMouseEvent event){/*mouse_event()*/; })); - Bind(wxEVT_LEFT_DOWN, ([this](wxMouseEvent event){ mouse_event(event); })); - Bind(wxEVT_MOTION, ([this](wxMouseEvent event){ mouse_event(event); })); - Bind(wxEVT_SIZE, ([this](wxSizeEvent e) { Refresh(); })); - } - ~Bed_2D(){} - - std::vector<Vec2d> m_bed_shape; - -}; - - -} // GUI -} // Slic3r - -#endif /* slic3r_2DBed_hpp_ */ diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp deleted file mode 100644 index e6f038042..000000000 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ /dev/null @@ -1,2205 +0,0 @@ -#include <GL/glew.h> - -#include "3DScene.hpp" - -#include "../../libslic3r/ExtrusionEntity.hpp" -#include "../../libslic3r/ExtrusionEntityCollection.hpp" -#include "../../libslic3r/Geometry.hpp" -#include "../../libslic3r/GCode/PreviewData.hpp" -#include "../../libslic3r/Print.hpp" -#include "../../libslic3r/Slicing.hpp" -#include "../../slic3r/GUI/PresetBundle.hpp" -#include "GCode/Analyzer.hpp" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <utility> -#include <assert.h> - -#include <boost/log/trivial.hpp> - -#include <tbb/parallel_for.h> -#include <tbb/spin_mutex.h> - -#include <Eigen/Dense> - -#include "GUI.hpp" - -namespace Slic3r { - -void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh) -{ - assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); - assert(quad_indices.empty() && triangle_indices_size == 0); - assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); - - this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); - - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) { - const stl_facet &facet = mesh.stl.facet_start[i]; - for (int j = 0; j < 3; ++ j) - this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); - } -} - -void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) -{ - assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); - assert(quad_indices.empty() && triangle_indices_size == 0); - assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size()); - - this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count()); - - unsigned int vertices_count = 0; - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { - const stl_facet &facet = mesh.stl.facet_start[i]; - for (int j = 0; j < 3; ++j) - this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2)); - - this->push_triangle(vertices_count, vertices_count + 1, vertices_count + 2); - vertices_count += 3; - } -} - -void GLIndexedVertexArray::finalize_geometry(bool use_VBOs) -{ - assert(this->vertices_and_normals_interleaved_VBO_id == 0); - assert(this->triangle_indices_VBO_id == 0); - assert(this->quad_indices_VBO_id == 0); - - this->setup_sizes(); - - if (use_VBOs) { - if (! empty()) { - glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id); - glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id); - glBufferData(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved.size() * 4, this->vertices_and_normals_interleaved.data(), GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - this->vertices_and_normals_interleaved.clear(); - } - if (! this->triangle_indices.empty()) { - glGenBuffers(1, &this->triangle_indices_VBO_id); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW); - this->triangle_indices.clear(); - } - if (! this->quad_indices.empty()) { - glGenBuffers(1, &this->quad_indices_VBO_id); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW); - this->quad_indices.clear(); - } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } - this->shrink_to_fit(); -} - -void GLIndexedVertexArray::release_geometry() -{ - if (this->vertices_and_normals_interleaved_VBO_id) - glDeleteBuffers(1, &this->vertices_and_normals_interleaved_VBO_id); - if (this->triangle_indices_VBO_id) - glDeleteBuffers(1, &this->triangle_indices_VBO_id); - if (this->quad_indices_VBO_id) - glDeleteBuffers(1, &this->quad_indices_VBO_id); - this->clear(); - this->shrink_to_fit(); -} - -void GLIndexedVertexArray::render() const -{ - if (this->vertices_and_normals_interleaved_VBO_id) { - glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id); - glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); - glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); - } else { - glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3); - glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()); - } - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - if (this->indexed()) { - if (this->vertices_and_normals_interleaved_VBO_id) { - // Render using the Vertex Buffer Objects. - if (this->triangle_indices_size > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id); - glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr); - } - if (this->quad_indices_size > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id); - glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr); - } - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } else { - // Render in an immediate mode. - if (! this->triangle_indices.empty()) - glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, this->triangle_indices.data()); - if (! this->quad_indices.empty()) - glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, this->quad_indices.data()); - } - } else - glDrawArrays(GL_TRIANGLES, 0, GLsizei(this->vertices_and_normals_interleaved_size / 6)); - - if (this->vertices_and_normals_interleaved_VBO_id) - glBindBuffer(GL_ARRAY_BUFFER, 0); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); -} - -void GLIndexedVertexArray::render( - const std::pair<size_t, size_t> &tverts_range, - const std::pair<size_t, size_t> &qverts_range) const -{ - assert(this->indexed()); - if (! this->indexed()) - return; - - if (this->vertices_and_normals_interleaved_VBO_id) { - // Render using the Vertex Buffer Objects. - glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id); - glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); - glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (this->triangle_indices_size > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id); - glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)); - } - if (this->quad_indices_size > 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id); - glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)); - } - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - } else { - // Render in an immediate mode. - glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3); - glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - if (! this->triangle_indices.empty()) - glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->triangle_indices.data() + tverts_range.first)); - if (! this->quad_indices.empty()) - glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->quad_indices.data() + qverts_range.first)); - } - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); -} - -const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; -const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; -const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; -const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; - -GLVolume::GLVolume(float r, float g, float b, float a) - : m_offset(Vec3d::Zero()) - , m_rotation(0.0) - , m_scaling_factor(1.0) - , m_world_matrix(Transform3f::Identity()) - , m_world_matrix_dirty(true) - , m_transformed_bounding_box_dirty(true) - , m_transformed_convex_hull_bounding_box_dirty(true) - , m_convex_hull(nullptr) - , composite_id(-1) - , select_group_id(-1) - , drag_group_id(-1) - , extruder_id(0) - , selected(false) - , is_active(true) - , zoom_to_volumes(true) - , shader_outside_printer_detection_enabled(false) - , is_outside(false) - , hover(false) - , is_modifier(false) - , is_wipe_tower(false) - , is_extrusion_path(false) - , tverts_range(0, size_t(-1)) - , qverts_range(0, size_t(-1)) -{ - color[0] = r; - color[1] = g; - color[2] = b; - color[3] = a; - set_render_color(r, g, b, a); -} - -void GLVolume::set_render_color(float r, float g, float b, float a) -{ - render_color[0] = r; - render_color[1] = g; - render_color[2] = b; - render_color[3] = a; -} - -void GLVolume::set_render_color(const float* rgba, unsigned int size) -{ - size = std::min((unsigned int)4, size); - for (int i = 0; i < size; ++i) - { - render_color[i] = rgba[i]; - } -} - -void GLVolume::set_render_color() -{ - if (selected) - set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); - else if (hover) - set_render_color(HOVER_COLOR, 4); - else if (is_outside && shader_outside_printer_detection_enabled) - set_render_color(OUTSIDE_COLOR, 4); - else - set_render_color(color, 4); -} - -double GLVolume::get_rotation() -{ - return m_rotation; -} - -void GLVolume::set_rotation(double rotation) -{ - if (m_rotation != rotation) - { - m_rotation = rotation; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_offset() const -{ - return m_offset; -} - -void GLVolume::set_offset(const Vec3d& offset) -{ - if (m_offset != offset) - { - m_offset = offset; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -void GLVolume::set_scaling_factor(double factor) -{ - if (m_scaling_factor != factor) - { - m_scaling_factor = factor; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -void GLVolume::set_convex_hull(const TriangleMesh& convex_hull) -{ - m_convex_hull = &convex_hull; -} - -void GLVolume::set_select_group_id(const std::string& select_by) -{ - if (select_by == "object") - select_group_id = object_idx() * 1000000; - else if (select_by == "volume") - select_group_id = object_idx() * 1000000 + volume_idx() * 1000; - else if (select_by == "instance") - select_group_id = composite_id; -} - -void GLVolume::set_drag_group_id(const std::string& drag_by) -{ - if (drag_by == "object") - drag_group_id = object_idx() * 1000; - else if (drag_by == "instance") - drag_group_id = object_idx() * 1000 + instance_idx(); -} - -const Transform3f& GLVolume::world_matrix() const -{ - if (m_world_matrix_dirty) - { - m_world_matrix = Transform3f::Identity(); - m_world_matrix.translate(m_offset.cast<float>()); - m_world_matrix.rotate(Eigen::AngleAxisf((float)m_rotation, Vec3f::UnitZ())); - m_world_matrix.scale((float)m_scaling_factor); - m_world_matrix_dirty = false; - } - return m_world_matrix; -} - -const BoundingBoxf3& GLVolume::transformed_bounding_box() const -{ - if (m_transformed_bounding_box_dirty) - { - m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast<double>()); - m_transformed_bounding_box_dirty = false; - } - - return m_transformed_bounding_box; -} - -const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const -{ - if (m_transformed_convex_hull_bounding_box_dirty) - { - if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) - m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast<double>()); - else - m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast<double>()); - - m_transformed_convex_hull_bounding_box_dirty = false; - } - - return m_transformed_convex_hull_bounding_box; -} - -void GLVolume::set_range(double min_z, double max_z) -{ - this->qverts_range.first = 0; - this->qverts_range.second = this->indexed_vertex_array.quad_indices_size; - this->tverts_range.first = 0; - this->tverts_range.second = this->indexed_vertex_array.triangle_indices_size; - if (! this->print_zs.empty()) { - // The Z layer range is specified. - // First test whether the Z span of this object is not out of (min_z, max_z) completely. - if (this->print_zs.front() > max_z || this->print_zs.back() < min_z) { - this->qverts_range.second = 0; - this->tverts_range.second = 0; - } else { - // Then find the lowest layer to be displayed. - size_t i = 0; - for (; i < this->print_zs.size() && this->print_zs[i] < min_z; ++ i); - if (i == this->print_zs.size()) { - // This shall not happen. - this->qverts_range.second = 0; - this->tverts_range.second = 0; - } else { - // Remember start of the layer. - this->qverts_range.first = this->offsets[i * 2]; - this->tverts_range.first = this->offsets[i * 2 + 1]; - // Some layers are above $min_z. Which? - for (; i < this->print_zs.size() && this->print_zs[i] <= max_z; ++ i); - if (i < this->print_zs.size()) { - this->qverts_range.second = this->offsets[i * 2]; - this->tverts_range.second = this->offsets[i * 2 + 1]; - } - } - } - } -} - -void GLVolume::render() const -{ - if (!is_active) - return; - - ::glCullFace(GL_BACK); - ::glPushMatrix(); - ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); - ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); - ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); - if (this->indexed_vertex_array.indexed()) - this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); - else - this->indexed_vertex_array.render(); - ::glPopMatrix(); -} - -void GLVolume::render_using_layer_height() const -{ - if (!is_active) - return; - - GLint current_program_id; - glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); - - if ((layer_height_texture_data.shader_id > 0) && (layer_height_texture_data.shader_id != current_program_id)) - glUseProgram(layer_height_texture_data.shader_id); - - GLint z_to_texture_row_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_to_texture_row") : -1; - GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1; - GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1; - GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1; - GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1; - - if (z_to_texture_row_id >= 0) - glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id()); - - if (z_texture_row_to_normalized_id >= 0) - glUniform1f(z_texture_row_to_normalized_id, (GLfloat)(1.0f / layer_height_texture_height())); - - if (z_cursor_id >= 0) - glUniform1f(z_cursor_id, (GLfloat)(layer_height_texture_data.print_object->model_object()->bounding_box().max(2) * layer_height_texture_data.z_cursor_relative)); - - if (z_cursor_band_width_id >= 0) - glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); - - if (world_matrix_id >= 0) - ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); - - GLsizei w = (GLsizei)layer_height_texture_width(); - GLsizei h = (GLsizei)layer_height_texture_height(); - GLsizei half_w = w / 2; - GLsizei half_h = h / 2; - - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0()); - glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1()); - - render(); - - glBindTexture(GL_TEXTURE_2D, 0); - - if ((current_program_id > 0) && (layer_height_texture_data.shader_id != current_program_id)) - glUseProgram(current_program_id); -} - -void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const -{ - if (!is_active) - return; - - if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id) - return; - - if (layer_height_texture_data.can_use()) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - render_using_layer_height(); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - return; - } - - GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); - GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); - if (n_triangles + n_quads == 0) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - if (color_id >= 0) - { - float color[4]; - ::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float)); - ::glUniform4fv(color_id, 1, (const GLfloat*)color); - } - else - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); - - if (detection_id != -1) - ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); - - if (worldmatrix_id != -1) - ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); - - render(); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - return; - } - - if (color_id >= 0) - ::glUniform4fv(color_id, 1, (const GLfloat*)render_color); - else - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); - - if (detection_id != -1) - ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); - - if (worldmatrix_id != -1) - ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); - - ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); - ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); - ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr); - - ::glPushMatrix(); - ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); - ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); - ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); - - if (n_triangles > 0) - { - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id); - ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)); - } - if (n_quads > 0) - { - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id); - ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)); - } - - ::glPopMatrix(); -} - -void GLVolume::render_legacy() const -{ - assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); - if (!is_active) - return; - - GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first)); - GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first)); - if (n_triangles + n_quads == 0) - { - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); - render(); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - return; - } - - ::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]); - ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3); - ::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()); - - ::glPushMatrix(); - ::glTranslated(m_offset(0), m_offset(1), m_offset(2)); - ::glRotated(m_rotation * 180.0 / (double)PI, 0.0, 0.0, 1.0); - ::glScaled(m_scaling_factor, m_scaling_factor, m_scaling_factor); - - if (n_triangles > 0) - ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); - - if (n_quads > 0) - ::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first); - - ::glPopMatrix(); -} - -double GLVolume::layer_height_texture_z_to_row_id() const -{ - return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max(2)); -} - -void GLVolume::generate_layer_height_texture(const PrintObject *print_object, bool force) -{ - LayersTexture *tex = this->layer_height_texture.get(); - if (tex == nullptr) - // No layer_height_texture is assigned to this GLVolume, therefore the layer height texture cannot be filled. - return; - - // Always try to update the layer height profile. - bool update = print_object->update_layer_height_profile(const_cast<ModelObject*>(print_object->model_object())->layer_height_profile) || force; - // Update if the layer height profile was changed, or when the texture is not valid. - if (! update && ! tex->data.empty() && tex->cells > 0) - // Texture is valid, don't update. - return; - - if (tex->data.empty()) { - tex->width = 1024; - tex->height = 1024; - tex->levels = 2; - tex->data.assign(tex->width * tex->height * 5, 0); - } - - SlicingParameters slicing_params = print_object->slicing_parameters(); - bool level_of_detail_2nd_level = true; - tex->cells = Slic3r::generate_layer_height_texture( - slicing_params, - Slic3r::generate_object_layers(slicing_params, print_object->model_object()->layer_height_profile), - tex->data.data(), tex->height, tex->width, level_of_detail_2nd_level); -} - -// 512x512 bitmaps are supported everywhere, but that may not be sufficent for super large print volumes. -#define LAYER_HEIGHT_TEXTURE_WIDTH 1024 -#define LAYER_HEIGHT_TEXTURE_HEIGHT 1024 - -std::vector<int> GLVolumeCollection::load_object( - const ModelObject *model_object, - int obj_idx, - const std::vector<int> &instance_idxs, - const std::string &color_by, - const std::string &select_by, - const std::string &drag_by, - bool use_VBOs) -{ - static float colors[4][4] = { - { 1.0f, 1.0f, 0.0f, 1.f }, - { 1.0f, 0.5f, 0.5f, 1.f }, - { 0.5f, 1.0f, 0.5f, 1.f }, - { 0.5f, 0.5f, 1.0f, 1.f } - }; - - // Object will have a single common layer height texture for all volumes. - std::shared_ptr<LayersTexture> layer_height_texture = std::make_shared<LayersTexture>(); - - std::vector<int> volumes_idx; - for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++ volume_idx) { - const ModelVolume *model_volume = model_object->volumes[volume_idx]; - - int extruder_id = -1; - if (model_volume->is_model_part()) - { - extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0; - if (extruder_id == 0) - extruder_id = model_object->config.has("extruder") ? model_object->config.option("extruder")->getInt() : 0; - } - - for (int instance_idx : instance_idxs) { - const ModelInstance *instance = model_object->instances[instance_idx]; - TriangleMesh mesh = model_volume->mesh; - volumes_idx.push_back(int(this->volumes.size())); - float color[4]; - memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); - if (model_volume->is_support_blocker()) { - color[0] = 1.0f; - color[1] = 0.2f; - color[2] = 0.2f; - } else if (model_volume->is_support_enforcer()) { - color[0] = 0.2f; - color[1] = 0.2f; - color[2] = 1.0f; - } - color[3] = model_volume->is_model_part() ? 1.f : 0.5f; - this->volumes.emplace_back(new GLVolume(color)); - GLVolume &v = *this->volumes.back(); - if (use_VBOs) - v.indexed_vertex_array.load_mesh_full_shading(mesh); - else - v.indexed_vertex_array.load_mesh_flat_shading(mesh); - - // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). - v.bounding_box = v.indexed_vertex_array.bounding_box(); - v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx; - v.set_select_group_id(select_by); - v.set_drag_group_id(drag_by); - if (model_volume->is_model_part()) - { - v.set_convex_hull(model_volume->get_convex_hull()); - v.layer_height_texture = layer_height_texture; - if (extruder_id != -1) - v.extruder_id = extruder_id; - } - v.is_modifier = ! model_volume->is_model_part(); - v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); -#if ENABLE_MODELINSTANCE_3D_OFFSET - v.set_offset(instance->get_offset()); -#else - v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0)); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - v.set_rotation(instance->rotation); - v.set_scaling_factor(instance->scaling_factor); - } - } - - return volumes_idx; -} - - -int GLVolumeCollection::load_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width) -{ - if (depth < 0.01f) - return int(this->volumes.size() - 1); - if (height == 0.0f) - height = 0.1f; - Point origin_of_rotation(0.f, 0.f); - TriangleMesh mesh; - float color[4] = { 0.5f, 0.5f, 0.0f, 1.f }; - - // In case we don't know precise dimensions of the wipe tower yet, we'll draw the box with different color with one side jagged: - if (size_unknown) { - color[0] = 0.9f; - color[1] = 0.6f; - - depth = std::max(depth, 10.f); // Too narrow tower would interfere with the teeth. The estimate is not precise anyway. - float min_width = 30.f; - // We'll now create the box with jagged edge. y-coordinates of the pre-generated model are shifted so that the front - // edge has y=0 and centerline of the back edge has y=depth: - Pointf3s points; - std::vector<Vec3crd> facets; - float out_points_idx[][3] = {{0, -depth, 0}, {0, 0, 0}, {38.453, 0, 0}, {61.547, 0, 0}, {100, 0, 0}, {100, -depth, 0}, {55.7735, -10, 0}, {44.2265, 10, 0}, - {38.453, 0, 1}, {0, 0, 1}, {0, -depth, 1}, {100, -depth, 1}, {100, 0, 1}, {61.547, 0, 1}, {55.7735, -10, 1}, {44.2265, 10, 1}}; - int out_facets_idx[][3] = {{0, 1, 2}, {3, 4, 5}, {6, 5, 0}, {3, 5, 6}, {6, 2, 7}, {6, 0, 2}, {8, 9, 10}, {11, 12, 13}, {10, 11, 14}, {14, 11, 13}, {15, 8, 14}, - {8, 10, 14}, {3, 12, 4}, {3, 13, 12}, {6, 13, 3}, {6, 14, 13}, {7, 14, 6}, {7, 15, 14}, {2, 15, 7}, {2, 8, 15}, {1, 8, 2}, {1, 9, 8}, - {0, 9, 1}, {0, 10, 9}, {5, 10, 0}, {5, 11, 10}, {4, 11, 5}, {4, 12, 11}}; - for (int i=0;i<16;++i) - points.push_back(Vec3d(out_points_idx[i][0] / (100.f/min_width), out_points_idx[i][1] + depth, out_points_idx[i][2])); - for (int i=0;i<28;++i) - facets.push_back(Vec3crd(out_facets_idx[i][0], out_facets_idx[i][1], out_facets_idx[i][2])); - TriangleMesh tooth_mesh(points, facets); - - // We have the mesh ready. It has one tooth and width of min_width. We will now append several of these together until we are close to - // the required width of the block. Than we can scale it precisely. - size_t n = std::max(1, int(width/min_width)); // How many shall be merged? - for (size_t i=0;i<n;++i) { - mesh.merge(tooth_mesh); - tooth_mesh.translate(min_width, 0.f, 0.f); - } - - mesh.scale(Vec3d(width/(n*min_width), 1.f, height)); // Scaling to proper width - } - else - mesh = make_cube(width, depth, height); - - // We'll make another mesh to show the brim (fixed layer height): - TriangleMesh brim_mesh = make_cube(width+2.f*brim_width, depth+2.f*brim_width, 0.2f); - brim_mesh.translate(-brim_width, -brim_width, 0.f); - mesh.merge(brim_mesh); - - mesh.rotate(rotation_angle, &origin_of_rotation); // rotates the box according to the config rotation setting - - this->volumes.emplace_back(new GLVolume(color)); - GLVolume &v = *this->volumes.back(); - - if (use_VBOs) - v.indexed_vertex_array.load_mesh_full_shading(mesh); - else - v.indexed_vertex_array.load_mesh_flat_shading(mesh); - - v.set_offset(Vec3d(pos_x, pos_y, 0.0)); - - // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). - v.bounding_box = v.indexed_vertex_array.bounding_box(); - v.indexed_vertex_array.finalize_geometry(use_VBOs); - v.composite_id = obj_idx * 1000000; - v.select_group_id = obj_idx * 1000000; - v.drag_group_id = obj_idx * 1000; - v.is_wipe_tower = true; - v.shader_outside_printer_detection_enabled = ! size_unknown; - return int(this->volumes.size() - 1); -} - -void GLVolumeCollection::render_VBOs() const -{ - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glCullFace(GL_BACK); - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - GLint current_program_id; - ::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); - GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; - GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1; - GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1; - GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; - GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1; - - if (print_box_min_id != -1) - ::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min); - - if (print_box_max_id != -1) - ::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max); - - for (GLVolume *volume : this->volumes) - { - if (volume->layer_height_texture_data.can_use()) - volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false); - else - volume->set_render_color(); - - volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); - } - - ::glBindBuffer(GL_ARRAY_BUFFER, 0); - ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisableClientState(GL_NORMAL_ARRAY); - - ::glDisable(GL_BLEND); -} - -void GLVolumeCollection::render_legacy() const -{ - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glCullFace(GL_BACK); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_NORMAL_ARRAY); - - for (GLVolume *volume : this->volumes) - { - volume->set_render_color(); - volume->render_legacy(); - } - - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - - glDisable(GL_BLEND); -} - -bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state) -{ - if (config == nullptr) - return false; - - const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape")); - if (opt == nullptr) - return false; - - BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height"))); - // Allow the objects to protrude below the print bed - print_volume.min(2) = -1e10; - - ModelInstance::EPrintVolumeState state = ModelInstance::PVS_Inside; - bool all_contained = true; - - for (GLVolume* volume : this->volumes) - { - if ((volume != nullptr) && !volume->is_modifier && (!volume->is_wipe_tower || (volume->is_wipe_tower && volume->shader_outside_printer_detection_enabled))) - { - const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box(); - bool contained = print_volume.contains(bb); - all_contained &= contained; - - volume->is_outside = !contained; - - if ((state == ModelInstance::PVS_Inside) && volume->is_outside) - state = ModelInstance::PVS_Fully_Outside; - - if ((state == ModelInstance::PVS_Fully_Outside) && volume->is_outside && print_volume.intersects(bb)) - state = ModelInstance::PVS_Partly_Outside; - } - } - - if (out_state != nullptr) - *out_state = state; - - return all_contained; -} - -void GLVolumeCollection::reset_outside_state() -{ - for (GLVolume* volume : this->volumes) - { - if (volume != nullptr) - volume->is_outside = false; - } -} - -void GLVolumeCollection::update_colors_by_extruder(const DynamicPrintConfig* config) -{ - static const float inv_255 = 1.0f / 255.0f; - - struct Color - { - std::string text; - unsigned char rgb[3]; - - Color() - : text("") - { - rgb[0] = 255; - rgb[1] = 255; - rgb[2] = 255; - } - - void set(const std::string& text, unsigned char* rgb) - { - this->text = text; - ::memcpy((void*)this->rgb, (const void*)rgb, 3 * sizeof(unsigned char)); - } - }; - - if (config == nullptr) - return; - - const ConfigOptionStrings* extruders_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("extruder_colour")); - if (extruders_opt == nullptr) - return; - - const ConfigOptionStrings* filamemts_opt = dynamic_cast<const ConfigOptionStrings*>(config->option("filament_colour")); - if (filamemts_opt == nullptr) - return; - - unsigned int colors_count = std::max((unsigned int)extruders_opt->values.size(), (unsigned int)filamemts_opt->values.size()); - if (colors_count == 0) - return; - - std::vector<Color> colors(colors_count); - - unsigned char rgb[3]; - for (unsigned int i = 0; i < colors_count; ++i) - { - const std::string& txt_color = config->opt_string("extruder_colour", i); - if (PresetBundle::parse_color(txt_color, rgb)) - { - colors[i].set(txt_color, rgb); - } - else - { - const std::string& txt_color = config->opt_string("filament_colour", i); - if (PresetBundle::parse_color(txt_color, rgb)) - colors[i].set(txt_color, rgb); - } - } - - for (GLVolume* volume : volumes) - { - if ((volume == nullptr) || volume->is_modifier || volume->is_wipe_tower) - continue; - - int extruder_id = volume->extruder_id - 1; - if ((extruder_id < 0) || ((unsigned int)colors.size() <= extruder_id)) - extruder_id = 0; - - const Color& color = colors[extruder_id]; - if (!color.text.empty()) - { - for (int i = 0; i < 3; ++i) - { - volume->color[i] = (float)color.rgb[i] * inv_255; - } - } - } -} - -void GLVolumeCollection::set_select_by(const std::string& select_by) -{ - for (GLVolume *vol : this->volumes) - { - if (vol != nullptr) - vol->set_select_group_id(select_by); - } -} - -void GLVolumeCollection::set_drag_by(const std::string& drag_by) -{ - for (GLVolume *vol : this->volumes) - { - if (vol != nullptr) - vol->set_drag_group_id(drag_by); - } -} - -std::vector<double> GLVolumeCollection::get_current_print_zs(bool active_only) const -{ - // Collect layer top positions of all volumes. - std::vector<double> print_zs; - for (GLVolume *vol : this->volumes) - { - if (!active_only || vol->is_active) - append(print_zs, vol->print_zs); - } - std::sort(print_zs.begin(), print_zs.end()); - - // Replace intervals of layers with similar top positions with their average value. - int n = int(print_zs.size()); - int k = 0; - for (int i = 0; i < n;) { - int j = i + 1; - coordf_t zmax = print_zs[i] + EPSILON; - for (; j < n && print_zs[j] <= zmax; ++ j) ; - print_zs[k ++] = (j > i + 1) ? (0.5 * (print_zs[i] + print_zs[j - 1])) : print_zs[i]; - i = j; - } - if (k < n) - print_zs.erase(print_zs.begin() + k, print_zs.end()); - - return print_zs; -} - -// caller is responsible for supplying NO lines with zero length -static void thick_lines_to_indexed_vertex_array( - const Lines &lines, - const std::vector<double> &widths, - const std::vector<double> &heights, - bool closed, - double top_z, - GLIndexedVertexArray &volume) -{ - assert(! lines.empty()); - if (lines.empty()) - return; - -#define LEFT 0 -#define RIGHT 1 -#define TOP 2 -#define BOTTOM 3 - - // right, left, top, bottom - int idx_prev[4] = { -1, -1, -1, -1 }; - double bottom_z_prev = 0.; - Vec2d b1_prev(Vec2d::Zero()); - Vec2d v_prev(Vec2d::Zero()); - int idx_initial[4] = { -1, -1, -1, -1 }; - double width_initial = 0.; - double bottom_z_initial = 0.0; - - // loop once more in case of closed loops - size_t lines_end = closed ? (lines.size() + 1) : lines.size(); - for (size_t ii = 0; ii < lines_end; ++ ii) { - size_t i = (ii == lines.size()) ? 0 : ii; - const Line &line = lines[i]; - double len = unscale<double>(line.length()); - double inv_len = 1.0 / len; - double bottom_z = top_z - heights[i]; - double middle_z = 0.5 * (top_z + bottom_z); - double width = widths[i]; - - bool is_first = (ii == 0); - bool is_last = (ii == lines_end - 1); - bool is_closing = closed && is_last; - - Vec2d v = unscale(line.vector()); - v *= inv_len; - - Vec2d a = unscale(line.a); - Vec2d b = unscale(line.b); - Vec2d a1 = a; - Vec2d a2 = a; - Vec2d b1 = b; - Vec2d b2 = b; - { - double dist = 0.5 * width; // scaled - double dx = dist * v(0); - double dy = dist * v(1); - a1 += Vec2d(+dy, -dx); - a2 += Vec2d(-dy, +dx); - b1 += Vec2d(+dy, -dx); - b2 += Vec2d(-dy, +dx); - } - - // calculate new XY normals - Vector n = line.normal(); - Vec3d xy_right_normal = unscale(n(0), n(1), 0); - xy_right_normal *= inv_len; - - int idx_a[4]; - int idx_b[4]; - int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); - - bool bottom_z_different = bottom_z_prev != bottom_z; - bottom_z_prev = bottom_z; - - if (!is_first && bottom_z_different) - { - // Found a change of the layer thickness -> Add a cap at the end of the previous segment. - volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); - } - - // Share top / bottom vertices if possible. - if (is_first) { - idx_a[TOP] = idx_last++; - volume.push_geometry(a(0), a(1), top_z , 0., 0., 1.); - } else { - idx_a[TOP] = idx_prev[TOP]; - } - - if (is_first || bottom_z_different) { - // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. - idx_a[BOTTOM] = idx_last ++; - volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.); - idx_a[LEFT ] = idx_last ++; - volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); - idx_a[RIGHT] = idx_last ++; - volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); - } - else { - idx_a[BOTTOM] = idx_prev[BOTTOM]; - } - - if (is_first) { - // Start of the 1st line segment. - width_initial = width; - bottom_z_initial = bottom_z; - memcpy(idx_initial, idx_a, sizeof(int) * 4); - } else { - // Continuing a previous segment. - // Share left / right vertices if possible. - double v_dot = v_prev.dot(v); - bool sharp = v_dot < 0.707; // sin(45 degrees) - if (sharp) { - if (!bottom_z_different) - { - // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. - idx_a[RIGHT] = idx_last++; - volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); - idx_a[LEFT] = idx_last++; - volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); - } - } - if (v_dot > 0.9) { - if (!bottom_z_different) - { - // The two successive segments are nearly collinear. - idx_a[LEFT ] = idx_prev[LEFT]; - idx_a[RIGHT] = idx_prev[RIGHT]; - } - } - else if (!sharp) { - if (!bottom_z_different) - { - // Create a sharp corner with an overshot and average the left / right normals. - // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. - Vec2d intersection(Vec2d::Zero()); - Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection); - a1 = intersection; - a2 = 2. * a - intersection; - assert((a - a1).norm() < width); - assert((a - a2).norm() < width); - float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6; - float *p_left_prev = n_left_prev + 3; - float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; - float *p_right_prev = n_right_prev + 3; - p_left_prev [0] = float(a2(0)); - p_left_prev [1] = float(a2(1)); - p_right_prev[0] = float(a1(0)); - p_right_prev[1] = float(a1(1)); - xy_right_normal(0) += n_right_prev[0]; - xy_right_normal(1) += n_right_prev[1]; - xy_right_normal *= 1. / xy_right_normal.norm(); - n_left_prev [0] = float(-xy_right_normal(0)); - n_left_prev [1] = float(-xy_right_normal(1)); - n_right_prev[0] = float( xy_right_normal(0)); - n_right_prev[1] = float( xy_right_normal(1)); - idx_a[LEFT ] = idx_prev[LEFT ]; - idx_a[RIGHT] = idx_prev[RIGHT]; - } - } - else if (cross2(v_prev, v) > 0.) { - // Right turn. Fill in the right turn wedge. - volume.push_triangle(idx_prev[RIGHT], idx_a [RIGHT], idx_prev[TOP] ); - volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a [RIGHT] ); - } else { - // Left turn. Fill in the left turn wedge. - volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a [LEFT] ); - volume.push_triangle(idx_prev[LEFT], idx_a [LEFT], idx_prev[BOTTOM]); - } - if (is_closing) { - if (!sharp) { - if (!bottom_z_different) - { - // Closing a loop with smooth transition. Unify the closing left / right vertices. - memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6); - memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); - volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); - // Replace the left / right vertex indices to point to the start of the loop. - for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) { - if (volume.quad_indices[u] == idx_prev[LEFT]) - volume.quad_indices[u] = idx_initial[LEFT]; - else if (volume.quad_indices[u] == idx_prev[RIGHT]) - volume.quad_indices[u] = idx_initial[RIGHT]; - } - } - } - // This is the last iteration, only required to solve the transition. - break; - } - } - - // Only new allocate top / bottom vertices, if not closing a loop. - if (is_closing) { - idx_b[TOP] = idx_initial[TOP]; - } else { - idx_b[TOP] = idx_last ++; - volume.push_geometry(b(0), b(1), top_z , 0., 0., 1.); - } - - if (is_closing && (width == width_initial) && (bottom_z == bottom_z_initial)) { - idx_b[BOTTOM] = idx_initial[BOTTOM]; - } else { - idx_b[BOTTOM] = idx_last ++; - volume.push_geometry(b(0), b(1), bottom_z, 0., 0., -1.); - } - // Generate new vertices for the end of this line segment. - idx_b[LEFT ] = idx_last ++; - volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); - idx_b[RIGHT ] = idx_last ++; - volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); - - memcpy(idx_prev, idx_b, 4 * sizeof(int)); - bottom_z_prev = bottom_z; - b1_prev = b1; - v_prev = v; - - if (bottom_z_different && (closed || (!is_first && !is_last))) - { - // Found a change of the layer thickness -> Add a cap at the beginning of this segment. - volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); - } - - if (! closed) { - // Terminate open paths with caps. - if (is_first) - volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); - // We don't use 'else' because both cases are true if we have only one line. - if (is_last) - volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); - } - - // Add quads for a straight hollow tube-like segment. - // bottom-right face - volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]); - // top-right face - volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]); - // top-left face - volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]); - // bottom-left face - volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]); - } - -#undef LEFT -#undef RIGHT -#undef TOP -#undef BOTTOM -} - -// caller is responsible for supplying NO lines with zero length -static void thick_lines_to_indexed_vertex_array(const Lines3& lines, - const std::vector<double>& widths, - const std::vector<double>& heights, - bool closed, - GLIndexedVertexArray& volume) -{ - assert(!lines.empty()); - if (lines.empty()) - return; - -#define LEFT 0 -#define RIGHT 1 -#define TOP 2 -#define BOTTOM 3 - - // left, right, top, bottom - int idx_initial[4] = { -1, -1, -1, -1 }; - int idx_prev[4] = { -1, -1, -1, -1 }; - double z_prev = 0.0; - Vec3d n_right_prev = Vec3d::Zero(); - Vec3d n_top_prev = Vec3d::Zero(); - Vec3d unit_v_prev = Vec3d::Zero(); - double width_initial = 0.0; - - // new vertices around the line endpoints - // left, right, top, bottom - Vec3d a[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; - Vec3d b[4] = { Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero(), Vec3d::Zero() }; - - // loop once more in case of closed loops - size_t lines_end = closed ? (lines.size() + 1) : lines.size(); - for (size_t ii = 0; ii < lines_end; ++ii) - { - size_t i = (ii == lines.size()) ? 0 : ii; - - const Line3& line = lines[i]; - double height = heights[i]; - double width = widths[i]; - - Vec3d unit_v = unscale(line.vector()).normalized(); - - Vec3d n_top = Vec3d::Zero(); - Vec3d n_right = Vec3d::Zero(); - Vec3d unit_positive_z(0.0, 0.0, 1.0); - - if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1))) - { - // vertical segment - n_right = (line.a(2) < line.b(2)) ? Vec3d(-1.0, 0.0, 0.0) : Vec3d(1.0, 0.0, 0.0); - n_top = Vec3d(0.0, 1.0, 0.0); - } - else - { - // generic segment - n_right = unit_v.cross(unit_positive_z).normalized(); - n_top = n_right.cross(unit_v).normalized(); - } - - Vec3d rl_displacement = 0.5 * width * n_right; - Vec3d tb_displacement = 0.5 * height * n_top; - Vec3d l_a = unscale(line.a); - Vec3d l_b = unscale(line.b); - - a[RIGHT] = l_a + rl_displacement; - a[LEFT] = l_a - rl_displacement; - a[TOP] = l_a + tb_displacement; - a[BOTTOM] = l_a - tb_displacement; - b[RIGHT] = l_b + rl_displacement; - b[LEFT] = l_b - rl_displacement; - b[TOP] = l_b + tb_displacement; - b[BOTTOM] = l_b - tb_displacement; - - Vec3d n_bottom = -n_top; - Vec3d n_left = -n_right; - - int idx_a[4]; - int idx_b[4]; - int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); - - bool z_different = (z_prev != l_a(2)); - z_prev = l_b(2); - - // Share top / bottom vertices if possible. - if (ii == 0) - { - idx_a[TOP] = idx_last++; - volume.push_geometry(a[TOP], n_top); - } - else - idx_a[TOP] = idx_prev[TOP]; - - if ((ii == 0) || z_different) - { - // Start of the 1st line segment or a change of the layer thickness while maintaining the print_z. - idx_a[BOTTOM] = idx_last++; - volume.push_geometry(a[BOTTOM], n_bottom); - idx_a[LEFT] = idx_last++; - volume.push_geometry(a[LEFT], n_left); - idx_a[RIGHT] = idx_last++; - volume.push_geometry(a[RIGHT], n_right); - } - else - idx_a[BOTTOM] = idx_prev[BOTTOM]; - - if (ii == 0) - { - // Start of the 1st line segment. - width_initial = width; - ::memcpy(idx_initial, idx_a, sizeof(int) * 4); - } - else - { - // Continuing a previous segment. - // Share left / right vertices if possible. - double v_dot = unit_v_prev.dot(unit_v); - bool is_sharp = v_dot < 0.707; // sin(45 degrees) - bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; - - if (is_sharp) - { - // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. - idx_a[RIGHT] = idx_last++; - volume.push_geometry(a[RIGHT], n_right); - idx_a[LEFT] = idx_last++; - volume.push_geometry(a[LEFT], n_left); - } - - if (v_dot > 0.9) - { - // The two successive segments are nearly collinear. - idx_a[LEFT] = idx_prev[LEFT]; - idx_a[RIGHT] = idx_prev[RIGHT]; - } - else if (!is_sharp) - { - // Create a sharp corner with an overshot and average the left / right normals. - // At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc. - - // averages normals - Vec3d average_n_right = 0.5 * (n_right + n_right_prev).normalized(); - Vec3d average_n_left = -average_n_right; - Vec3d average_rl_displacement = 0.5 * width * average_n_right; - - // updates vertices around a - a[RIGHT] = l_a + average_rl_displacement; - a[LEFT] = l_a - average_rl_displacement; - - // updates previous line normals - float* normal_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6; - normal_left_prev[0] = float(average_n_left(0)); - normal_left_prev[1] = float(average_n_left(1)); - normal_left_prev[2] = float(average_n_left(2)); - - float* normal_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6; - normal_right_prev[0] = float(average_n_right(0)); - normal_right_prev[1] = float(average_n_right(1)); - normal_right_prev[2] = float(average_n_right(2)); - - // updates previous line's vertices around b - float* b_left_prev = normal_left_prev + 3; - b_left_prev[0] = float(a[LEFT](0)); - b_left_prev[1] = float(a[LEFT](1)); - b_left_prev[2] = float(a[LEFT](2)); - - float* b_right_prev = normal_right_prev + 3; - b_right_prev[0] = float(a[RIGHT](0)); - b_right_prev[1] = float(a[RIGHT](1)); - b_right_prev[2] = float(a[RIGHT](2)); - - idx_a[LEFT] = idx_prev[LEFT]; - idx_a[RIGHT] = idx_prev[RIGHT]; - } - else if (is_right_turn) - { - // Right turn. Fill in the right turn wedge. - volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]); - volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]); - } - else - { - // Left turn. Fill in the left turn wedge. - volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]); - volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]); - } - - if (ii == lines.size()) - { - if (!is_sharp) - { - // Closing a loop with smooth transition. Unify the closing left / right vertices. - ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6, sizeof(float) * 6); - ::memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6); - volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end()); - // Replace the left / right vertex indices to point to the start of the loop. - for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++u) - { - if (volume.quad_indices[u] == idx_prev[LEFT]) - volume.quad_indices[u] = idx_initial[LEFT]; - else if (volume.quad_indices[u] == idx_prev[RIGHT]) - volume.quad_indices[u] = idx_initial[RIGHT]; - } - } - - // This is the last iteration, only required to solve the transition. - break; - } - } - - // Only new allocate top / bottom vertices, if not closing a loop. - if (closed && (ii + 1 == lines.size())) - idx_b[TOP] = idx_initial[TOP]; - else - { - idx_b[TOP] = idx_last++; - volume.push_geometry(b[TOP], n_top); - } - - if (closed && (ii + 1 == lines.size()) && (width == width_initial)) - idx_b[BOTTOM] = idx_initial[BOTTOM]; - else - { - idx_b[BOTTOM] = idx_last++; - volume.push_geometry(b[BOTTOM], n_bottom); - } - - // Generate new vertices for the end of this line segment. - idx_b[LEFT] = idx_last++; - volume.push_geometry(b[LEFT], n_left); - idx_b[RIGHT] = idx_last++; - volume.push_geometry(b[RIGHT], n_right); - - ::memcpy(idx_prev, idx_b, 4 * sizeof(int)); - n_right_prev = n_right; - n_top_prev = n_top; - unit_v_prev = unit_v; - - if (!closed) - { - // Terminate open paths with caps. - if (i == 0) - volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]); - - // We don't use 'else' because both cases are true if we have only one line. - if (i + 1 == lines.size()) - volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]); - } - - // Add quads for a straight hollow tube-like segment. - // bottom-right face - volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]); - // top-right face - volume.push_quad(idx_a[RIGHT], idx_b[RIGHT], idx_b[TOP], idx_a[TOP]); - // top-left face - volume.push_quad(idx_a[TOP], idx_b[TOP], idx_b[LEFT], idx_a[LEFT]); - // bottom-left face - volume.push_quad(idx_a[LEFT], idx_b[LEFT], idx_b[BOTTOM], idx_a[BOTTOM]); - } - -#undef LEFT -#undef RIGHT -#undef TOP -#undef BOTTOM -} - -static void point_to_indexed_vertex_array(const Vec3crd& point, - double width, - double height, - GLIndexedVertexArray& volume) -{ - // builds a double piramid, with vertices on the local axes, around the point - - Vec3d center = unscale(point); - - double scale_factor = 1.0; - double w = scale_factor * width; - double h = scale_factor * height; - - // new vertices ids - int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); - int idxs[6]; - for (int i = 0; i < 6; ++i) - { - idxs[i] = idx_last + i; - } - - Vec3d displacement_x(w, 0.0, 0.0); - Vec3d displacement_y(0.0, w, 0.0); - Vec3d displacement_z(0.0, 0.0, h); - - Vec3d unit_x(1.0, 0.0, 0.0); - Vec3d unit_y(0.0, 1.0, 0.0); - Vec3d unit_z(0.0, 0.0, 1.0); - - // vertices - volume.push_geometry(center - displacement_x, -unit_x); // idxs[0] - volume.push_geometry(center + displacement_x, unit_x); // idxs[1] - volume.push_geometry(center - displacement_y, -unit_y); // idxs[2] - volume.push_geometry(center + displacement_y, unit_y); // idxs[3] - volume.push_geometry(center - displacement_z, -unit_z); // idxs[4] - volume.push_geometry(center + displacement_z, unit_z); // idxs[5] - - // top piramid faces - volume.push_triangle(idxs[0], idxs[2], idxs[5]); - volume.push_triangle(idxs[2], idxs[1], idxs[5]); - volume.push_triangle(idxs[1], idxs[3], idxs[5]); - volume.push_triangle(idxs[3], idxs[0], idxs[5]); - - // bottom piramid faces - volume.push_triangle(idxs[2], idxs[0], idxs[4]); - volume.push_triangle(idxs[1], idxs[2], idxs[4]); - volume.push_triangle(idxs[3], idxs[1], idxs[4]); - volume.push_triangle(idxs[0], idxs[3], idxs[4]); -} - -void _3DScene::thick_lines_to_verts( - const Lines &lines, - const std::vector<double> &widths, - const std::vector<double> &heights, - bool closed, - double top_z, - GLVolume &volume) -{ - thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, top_z, volume.indexed_vertex_array); -} - -void _3DScene::thick_lines_to_verts(const Lines3& lines, - const std::vector<double>& widths, - const std::vector<double>& heights, - bool closed, - GLVolume& volume) -{ - thick_lines_to_indexed_vertex_array(lines, widths, heights, closed, volume.indexed_vertex_array); -} - -static void thick_point_to_verts(const Vec3crd& point, - double width, - double height, - GLVolume& volume) -{ - point_to_indexed_vertex_array(point, width, height, volume.indexed_vertex_array); -} - -// Fill in the qverts and tverts with quads and triangles for the extrusion_path. -void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, GLVolume &volume) -{ - Lines lines = extrusion_path.polyline.lines(); - std::vector<double> widths(lines.size(), extrusion_path.width); - std::vector<double> heights(lines.size(), extrusion_path.height); - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); -} - -// Fill in the qverts and tverts with quads and triangles for the extrusion_path. -void _3DScene::extrusionentity_to_verts(const ExtrusionPath &extrusion_path, float print_z, const Point ©, GLVolume &volume) -{ - Polyline polyline = extrusion_path.polyline; - polyline.remove_duplicate_points(); - polyline.translate(copy); - Lines lines = polyline.lines(); - std::vector<double> widths(lines.size(), extrusion_path.width); - std::vector<double> heights(lines.size(), extrusion_path.height); - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); -} - -// Fill in the qverts and tverts with quads and triangles for the extrusion_loop. -void _3DScene::extrusionentity_to_verts(const ExtrusionLoop &extrusion_loop, float print_z, const Point ©, GLVolume &volume) -{ - Lines lines; - std::vector<double> widths; - std::vector<double> heights; - for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) { - Polyline polyline = extrusion_path.polyline; - polyline.remove_duplicate_points(); - polyline.translate(copy); - Lines lines_this = polyline.lines(); - append(lines, lines_this); - widths.insert(widths.end(), lines_this.size(), extrusion_path.width); - heights.insert(heights.end(), lines_this.size(), extrusion_path.height); - } - thick_lines_to_verts(lines, widths, heights, true, print_z, volume); -} - -// Fill in the qverts and tverts with quads and triangles for the extrusion_multi_path. -void _3DScene::extrusionentity_to_verts(const ExtrusionMultiPath &extrusion_multi_path, float print_z, const Point ©, GLVolume &volume) -{ - Lines lines; - std::vector<double> widths; - std::vector<double> heights; - for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) { - Polyline polyline = extrusion_path.polyline; - polyline.remove_duplicate_points(); - polyline.translate(copy); - Lines lines_this = polyline.lines(); - append(lines, lines_this); - widths.insert(widths.end(), lines_this.size(), extrusion_path.width); - heights.insert(heights.end(), lines_this.size(), extrusion_path.height); - } - thick_lines_to_verts(lines, widths, heights, false, print_z, volume); -} - -void _3DScene::extrusionentity_to_verts(const ExtrusionEntityCollection &extrusion_entity_collection, float print_z, const Point ©, GLVolume &volume) -{ - for (const ExtrusionEntity *extrusion_entity : extrusion_entity_collection.entities) - extrusionentity_to_verts(extrusion_entity, print_z, copy, volume); -} - -void _3DScene::extrusionentity_to_verts(const ExtrusionEntity *extrusion_entity, float print_z, const Point ©, GLVolume &volume) -{ - if (extrusion_entity != nullptr) { - auto *extrusion_path = dynamic_cast<const ExtrusionPath*>(extrusion_entity); - if (extrusion_path != nullptr) - extrusionentity_to_verts(*extrusion_path, print_z, copy, volume); - else { - auto *extrusion_loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity); - if (extrusion_loop != nullptr) - extrusionentity_to_verts(*extrusion_loop, print_z, copy, volume); - else { - auto *extrusion_multi_path = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity); - if (extrusion_multi_path != nullptr) - extrusionentity_to_verts(*extrusion_multi_path, print_z, copy, volume); - else { - auto *extrusion_entity_collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity); - if (extrusion_entity_collection != nullptr) - extrusionentity_to_verts(*extrusion_entity_collection, print_z, copy, volume); - else { - throw std::runtime_error("Unexpected extrusion_entity type in to_verts()"); - } - } - } - } - } -} - -void _3DScene::polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume) -{ - Lines3 lines = polyline.lines(); - std::vector<double> widths(lines.size(), width); - std::vector<double> heights(lines.size(), height); - thick_lines_to_verts(lines, widths, heights, false, volume); -} - -void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume) -{ - thick_point_to_verts(point, width, height, volume); -} - -GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; - -void _3DScene::init_gl() -{ - s_canvas_mgr.init_gl(); -} - -std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) -{ - return s_canvas_mgr.get_gl_info(format_as_html, extensions); -} - -bool _3DScene::use_VBOs() -{ - return s_canvas_mgr.use_VBOs(); -} - -bool _3DScene::add_canvas(wxGLCanvas* canvas) -{ - return s_canvas_mgr.add(canvas); -} - -bool _3DScene::remove_canvas(wxGLCanvas* canvas) -{ - return s_canvas_mgr.remove(canvas); -} - -void _3DScene::remove_all_canvases() -{ - s_canvas_mgr.remove_all(); -} - -bool _3DScene::init(wxGLCanvas* canvas) -{ - return s_canvas_mgr.init(canvas); -} - -void _3DScene::set_as_dirty(wxGLCanvas* canvas) -{ - s_canvas_mgr.set_as_dirty(canvas); -} - -unsigned int _3DScene::get_volumes_count(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_volumes_count(canvas); -} - -void _3DScene::reset_volumes(wxGLCanvas* canvas) -{ - s_canvas_mgr.reset_volumes(canvas); -} - -void _3DScene::deselect_volumes(wxGLCanvas* canvas) -{ - s_canvas_mgr.deselect_volumes(canvas); -} - -void _3DScene::select_volume(wxGLCanvas* canvas, unsigned int id) -{ - s_canvas_mgr.select_volume(canvas, id); -} - -void _3DScene::update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections) -{ - s_canvas_mgr.update_volumes_selection(canvas, selections); -} - -int _3DScene::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) -{ - return s_canvas_mgr.check_volumes_outside_state(canvas, config); -} - -bool _3DScene::move_volume_up(wxGLCanvas* canvas, unsigned int id) -{ - return s_canvas_mgr.move_volume_up(canvas, id); -} - -bool _3DScene::move_volume_down(wxGLCanvas* canvas, unsigned int id) -{ - return s_canvas_mgr.move_volume_down(canvas, id); -} - -void _3DScene::set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections) -{ - s_canvas_mgr.set_objects_selections(canvas, selections); -} - -void _3DScene::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) -{ - s_canvas_mgr.set_config(canvas, config); -} - -void _3DScene::set_print(wxGLCanvas* canvas, Print* print) -{ - s_canvas_mgr.set_print(canvas, print); -} - -void _3DScene::set_model(wxGLCanvas* canvas, Model* model) -{ - s_canvas_mgr.set_model(canvas, model); -} - -void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) -{ - s_canvas_mgr.set_bed_shape(canvas, shape); -} - -void _3DScene::set_auto_bed_shape(wxGLCanvas* canvas) -{ - s_canvas_mgr.set_auto_bed_shape(canvas); -} - -BoundingBoxf3 _3DScene::get_volumes_bounding_box(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_volumes_bounding_box(canvas); -} - -void _3DScene::set_axes_length(wxGLCanvas* canvas, float length) -{ - s_canvas_mgr.set_axes_length(canvas, length); -} - -void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) -{ - s_canvas_mgr.set_cutting_plane(canvas, z, polygons); -} - -void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value) -{ - s_canvas_mgr.set_color_by(canvas, value); -} - -void _3DScene::set_select_by(wxGLCanvas* canvas, const std::string& value) -{ - s_canvas_mgr.set_select_by(canvas, value); -} - -void _3DScene::set_drag_by(wxGLCanvas* canvas, const std::string& value) -{ - s_canvas_mgr.set_drag_by(canvas, value); -} - -std::string _3DScene::get_select_by(wxGLCanvas* canvas) -{ - return s_canvas_mgr.get_select_by(canvas); -} - -bool _3DScene::is_layers_editing_enabled(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_layers_editing_enabled(canvas); -} - -bool _3DScene::is_layers_editing_allowed(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_layers_editing_allowed(canvas); -} - -bool _3DScene::is_shader_enabled(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_shader_enabled(canvas); -} - -bool _3DScene::is_reload_delayed(wxGLCanvas* canvas) -{ - return s_canvas_mgr.is_reload_delayed(canvas); -} - -void _3DScene::enable_layers_editing(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_layers_editing(canvas, enable); -} - -void _3DScene::enable_warning_texture(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_warning_texture(canvas, enable); -} - -void _3DScene::enable_legend_texture(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_legend_texture(canvas, enable); -} - -void _3DScene::enable_picking(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_picking(canvas, enable); -} - -void _3DScene::enable_moving(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_moving(canvas, enable); -} - -void _3DScene::enable_gizmos(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_gizmos(canvas, enable); -} - -void _3DScene::enable_toolbar(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_toolbar(canvas, enable); -} - -void _3DScene::enable_shader(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_shader(canvas, enable); -} - -void _3DScene::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_force_zoom_to_bed(canvas, enable); -} - -void _3DScene::enable_dynamic_background(wxGLCanvas* canvas, bool enable) -{ - s_canvas_mgr.enable_dynamic_background(canvas, enable); -} - -void _3DScene::allow_multisample(wxGLCanvas* canvas, bool allow) -{ - s_canvas_mgr.allow_multisample(canvas, allow); -} - -void _3DScene::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) -{ - s_canvas_mgr.enable_toolbar_item(canvas, name, enable); -} - -bool _3DScene::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) -{ - return s_canvas_mgr.is_toolbar_item_pressed(canvas, name); -} - -void _3DScene::zoom_to_bed(wxGLCanvas* canvas) -{ - s_canvas_mgr.zoom_to_bed(canvas); -} - -void _3DScene::zoom_to_volumes(wxGLCanvas* canvas) -{ - s_canvas_mgr.zoom_to_volumes(canvas); -} - -void _3DScene::select_view(wxGLCanvas* canvas, const std::string& direction) -{ - s_canvas_mgr.select_view(canvas, direction); -} - -void _3DScene::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other) -{ - s_canvas_mgr.set_viewport_from_scene(canvas, other); -} - -void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas) -{ - s_canvas_mgr.update_volumes_colors_by_extruder(canvas); -} - -void _3DScene::update_gizmos_data(wxGLCanvas* canvas) -{ - s_canvas_mgr.update_gizmos_data(canvas); -} - -void _3DScene::render(wxGLCanvas* canvas) -{ - s_canvas_mgr.render(canvas); -} - -std::vector<double> _3DScene::get_current_print_zs(wxGLCanvas* canvas, bool active_only) -{ - return s_canvas_mgr.get_current_print_zs(canvas, active_only); -} - -void _3DScene::set_toolpaths_range(wxGLCanvas* canvas, double low, double high) -{ - s_canvas_mgr.set_toolpaths_range(canvas, low, high); -} - -void _3DScene::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_viewport_changed_callback(canvas, callback); -} - -void _3DScene::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_double_click_callback(canvas, callback); -} - -void _3DScene::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_right_click_callback(canvas, callback); -} - -void _3DScene::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_select_object_callback(canvas, callback); -} - -void _3DScene::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_model_update_callback(canvas, callback); -} - -void _3DScene::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_remove_object_callback(canvas, callback); -} - -void _3DScene::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_arrange_callback(canvas, callback); -} - -void _3DScene::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_rotate_object_left_callback(canvas, callback); -} - -void _3DScene::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_rotate_object_right_callback(canvas, callback); -} - -void _3DScene::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_scale_object_uniformly_callback(canvas, callback); -} - -void _3DScene::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_increase_objects_callback(canvas, callback); -} - -void _3DScene::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_decrease_objects_callback(canvas, callback); -} - -void _3DScene::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_instance_moved_callback(canvas, callback); -} - -void _3DScene::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_wipe_tower_moved_callback(canvas, callback); -} - -void _3DScene::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_enable_action_buttons_callback(canvas, callback); -} - -void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback); -} - -void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback); -} - -void _3DScene::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_gizmo_flatten_callback(canvas, callback); -} - -void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback); -} - -void _3DScene::register_action_add_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_add_callback(canvas, callback); -} - -void _3DScene::register_action_delete_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_delete_callback(canvas, callback); -} - -void _3DScene::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_deleteall_callback(canvas, callback); -} - -void _3DScene::register_action_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_arrange_callback(canvas, callback); -} - -void _3DScene::register_action_more_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_more_callback(canvas, callback); -} - -void _3DScene::register_action_fewer_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_fewer_callback(canvas, callback); -} - -void _3DScene::register_action_split_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_split_callback(canvas, callback); -} - -void _3DScene::register_action_cut_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_cut_callback(canvas, callback); -} - -void _3DScene::register_action_settings_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_settings_callback(canvas, callback); -} - -void _3DScene::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_layersediting_callback(canvas, callback); -} - -void _3DScene::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) -{ - s_canvas_mgr.register_action_selectbyparts_callback(canvas, callback); -} - -static inline int hex_digit_to_int(const char c) -{ - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - -static inline std::vector<float> parse_colors(const std::vector<std::string> &scolors) -{ - std::vector<float> output(scolors.size() * 4, 1.f); - for (size_t i = 0; i < scolors.size(); ++ i) { - const std::string &scolor = scolors[i]; - const char *c = scolor.data() + 1; - if (scolor.size() == 7 && scolor.front() == '#') { - for (size_t j = 0; j < 3; ++j) { - int digit1 = hex_digit_to_int(*c ++); - int digit2 = hex_digit_to_int(*c ++); - if (digit1 == -1 || digit2 == -1) - break; - output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.f; - } - } - } - return output; -} - -std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs) -{ - return s_canvas_mgr.load_object(canvas, model_object, obj_idx, instance_idxs); -} - -std::vector<int> _3DScene::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx) -{ - return s_canvas_mgr.load_object(canvas, model, obj_idx); -} - -int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) -{ - return s_canvas_mgr.get_first_volume_id(canvas, obj_idx); -} - -int _3DScene::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) -{ - return s_canvas_mgr.get_in_object_volume_id(canvas, scene_vol_idx); -} - -void _3DScene::reload_scene(wxGLCanvas* canvas, bool force) -{ - s_canvas_mgr.reload_scene(canvas, force); -} - -void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors) -{ - s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors); -} - -void _3DScene::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors) -{ - s_canvas_mgr.load_preview(canvas, str_tool_colors); -} - -void _3DScene::reset_legend_texture() -{ - s_canvas_mgr.reset_legend_texture(); -} - -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp deleted file mode 100644 index f2d1c0786..000000000 --- a/xs/src/slic3r/GUI/3DScene.hpp +++ /dev/null @@ -1,603 +0,0 @@ -#ifndef slic3r_3DScene_hpp_ -#define slic3r_3DScene_hpp_ - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Point.hpp" -#include "../../libslic3r/Line.hpp" -#include "../../libslic3r/TriangleMesh.hpp" -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/Model.hpp" -#include "../../slic3r/GUI/GLCanvas3DManager.hpp" - -class wxBitmap; -class wxWindow; - -namespace Slic3r { - -class Print; -class PrintObject; -class Model; -class ModelObject; -class GCodePreviewData; -class DynamicPrintConfig; -class ExtrusionPath; -class ExtrusionMultiPath; -class ExtrusionLoop; -class ExtrusionEntity; -class ExtrusionEntityCollection; - -// A container for interleaved arrays of 3D vertices and normals, -// possibly indexed by triangles and / or quads. -class GLIndexedVertexArray { -public: - GLIndexedVertexArray() : - vertices_and_normals_interleaved_VBO_id(0), - triangle_indices_VBO_id(0), - quad_indices_VBO_id(0) - { this->setup_sizes(); } - GLIndexedVertexArray(const GLIndexedVertexArray &rhs) : - vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved), - triangle_indices(rhs.triangle_indices), - quad_indices(rhs.quad_indices), - vertices_and_normals_interleaved_VBO_id(0), - triangle_indices_VBO_id(0), - quad_indices_VBO_id(0) - { this->setup_sizes(); } - GLIndexedVertexArray(GLIndexedVertexArray &&rhs) : - vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)), - triangle_indices(std::move(rhs.triangle_indices)), - quad_indices(std::move(rhs.quad_indices)), - vertices_and_normals_interleaved_VBO_id(0), - triangle_indices_VBO_id(0), - quad_indices_VBO_id(0) - { this->setup_sizes(); } - - GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs) - { - assert(vertices_and_normals_interleaved_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved; - this->triangle_indices = rhs.triangle_indices; - this->quad_indices = rhs.quad_indices; - this->setup_sizes(); - return *this; - } - - GLIndexedVertexArray& operator=(GLIndexedVertexArray &&rhs) - { - assert(vertices_and_normals_interleaved_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - assert(triangle_indices_VBO_id == 0); - this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved); - this->triangle_indices = std::move(rhs.triangle_indices); - this->quad_indices = std::move(rhs.quad_indices); - this->setup_sizes(); - return *this; - } - - // Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x) - std::vector<float> vertices_and_normals_interleaved; - std::vector<int> triangle_indices; - std::vector<int> quad_indices; - - // When the geometry data is loaded into the graphics card as Vertex Buffer Objects, - // the above mentioned std::vectors are cleared and the following variables keep their original length. - size_t vertices_and_normals_interleaved_size; - size_t triangle_indices_size; - size_t quad_indices_size; - - // IDs of the Vertex Array Objects, into which the geometry has been loaded. - // Zero if the VBOs are not used. - unsigned int vertices_and_normals_interleaved_VBO_id; - unsigned int triangle_indices_VBO_id; - unsigned int quad_indices_VBO_id; - - void load_mesh_flat_shading(const TriangleMesh &mesh); - void load_mesh_full_shading(const TriangleMesh &mesh); - - inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } - - inline void reserve(size_t sz) { - this->vertices_and_normals_interleaved.reserve(sz * 6); - this->triangle_indices.reserve(sz * 3); - this->quad_indices.reserve(sz * 4); - } - - inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) { - if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) - this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); - this->vertices_and_normals_interleaved.push_back(nx); - this->vertices_and_normals_interleaved.push_back(ny); - this->vertices_and_normals_interleaved.push_back(nz); - this->vertices_and_normals_interleaved.push_back(x); - this->vertices_and_normals_interleaved.push_back(y); - this->vertices_and_normals_interleaved.push_back(z); - }; - - inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) { - push_geometry(float(x), float(y), float(z), float(nx), float(ny), float(nz)); - } - - inline void push_geometry(const Vec3d& p, const Vec3d& n) { - push_geometry(p(0), p(1), p(2), n(0), n(1), n(2)); - } - - inline void push_triangle(int idx1, int idx2, int idx3) { - if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) - this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); - this->triangle_indices.push_back(idx1); - this->triangle_indices.push_back(idx2); - this->triangle_indices.push_back(idx3); - }; - - inline void push_quad(int idx1, int idx2, int idx3, int idx4) { - if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity()) - this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4)); - this->quad_indices.push_back(idx1); - this->quad_indices.push_back(idx2); - this->quad_indices.push_back(idx3); - this->quad_indices.push_back(idx4); - }; - - // Finalize the initialization of the geometry & indices, - // upload the geometry and indices to OpenGL VBO objects - // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. - void finalize_geometry(bool use_VBOs); - // Release the geometry data, release OpenGL VBOs. - void release_geometry(); - // Render either using an immediate mode, or the VBOs. - void render() const; - void render(const std::pair<size_t, size_t> &tverts_range, const std::pair<size_t, size_t> &qverts_range) const; - - // Is there any geometry data stored? - bool empty() const { return vertices_and_normals_interleaved_size == 0; } - - // Is this object indexed, or is it just a set of triangles? - bool indexed() const { return ! this->empty() && this->triangle_indices_size + this->quad_indices_size > 0; } - - void clear() { - this->vertices_and_normals_interleaved.clear(); - this->triangle_indices.clear(); - this->quad_indices.clear(); - this->setup_sizes(); - } - - // Shrink the internal storage to tighly fit the data stored. - void shrink_to_fit() { - if (! this->has_VBOs()) - this->setup_sizes(); - this->vertices_and_normals_interleaved.shrink_to_fit(); - this->triangle_indices.shrink_to_fit(); - this->quad_indices.shrink_to_fit(); - } - - BoundingBoxf3 bounding_box() const { - BoundingBoxf3 bbox; - if (! this->vertices_and_normals_interleaved.empty()) { - bbox.defined = true; - bbox.min(0) = bbox.max(0) = this->vertices_and_normals_interleaved[3]; - bbox.min(1) = bbox.max(1) = this->vertices_and_normals_interleaved[4]; - bbox.min(2) = bbox.max(2) = this->vertices_and_normals_interleaved[5]; - for (size_t i = 9; i < this->vertices_and_normals_interleaved.size(); i += 6) { - const float *verts = this->vertices_and_normals_interleaved.data() + i; - bbox.min(0) = std::min<coordf_t>(bbox.min(0), verts[0]); - bbox.min(1) = std::min<coordf_t>(bbox.min(1), verts[1]); - bbox.min(2) = std::min<coordf_t>(bbox.min(2), verts[2]); - bbox.max(0) = std::max<coordf_t>(bbox.max(0), verts[0]); - bbox.max(1) = std::max<coordf_t>(bbox.max(1), verts[1]); - bbox.max(2) = std::max<coordf_t>(bbox.max(2), verts[2]); - } - } - return bbox; - } - -private: - inline void setup_sizes() { - vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size(); - triangle_indices_size = this->triangle_indices.size(); - quad_indices_size = this->quad_indices.size(); - } -}; - -class LayersTexture -{ -public: - LayersTexture() : width(0), height(0), levels(0), cells(0) {} - - // Texture data - std::vector<char> data; - // Width of the texture, top level. - size_t width; - // Height of the texture, top level. - size_t height; - // For how many levels of detail is the data allocated? - size_t levels; - // Number of texture cells allocated for the height texture. - size_t cells; -}; - -class GLVolume { - struct LayerHeightTextureData - { - // ID of the layer height texture - unsigned int texture_id; - // ID of the shader used to render with the layer height texture - unsigned int shader_id; - // The print object to update when generating the layer height texture - const PrintObject* print_object; - - float z_cursor_relative; - float edit_band_width; - - LayerHeightTextureData() { reset(); } - - void reset() - { - texture_id = 0; - shader_id = 0; - print_object = nullptr; - z_cursor_relative = 0.0f; - edit_band_width = 0.0f; - } - - bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); } - }; - -public: - static const float SELECTED_COLOR[4]; - static const float HOVER_COLOR[4]; - static const float OUTSIDE_COLOR[4]; - static const float SELECTED_OUTSIDE_COLOR[4]; - - GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f); - GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {} - -private: - // Offset of the volume to be rendered. - Vec3d m_offset; - // Rotation around Z axis of the volume to be rendered. - double m_rotation; - // Scale factor of the volume to be rendered. - double m_scaling_factor; - // World matrix of the volume to be rendered. - mutable Transform3f m_world_matrix; - // Whether or not is needed to recalculate the world matrix. - mutable bool m_world_matrix_dirty; - // Bounding box of this volume, in unscaled coordinates. - mutable BoundingBoxf3 m_transformed_bounding_box; - // Whether or not is needed to recalculate the transformed bounding box. - mutable bool m_transformed_bounding_box_dirty; - // Pointer to convex hull of the original mesh, if any. - const TriangleMesh* m_convex_hull; - // Bounding box of this volume, in unscaled coordinates. - mutable BoundingBoxf3 m_transformed_convex_hull_bounding_box; - // Whether or not is needed to recalculate the transformed convex hull bounding box. - mutable bool m_transformed_convex_hull_bounding_box_dirty; - -public: - - // Bounding box of this volume, in unscaled coordinates. - BoundingBoxf3 bounding_box; - // Color of the triangles / quads held by this volume. - float color[4]; - // Color used to render this volume. - float render_color[4]; - // An ID containing the object ID, volume ID and instance ID. - int composite_id; - // An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance. - int select_group_id; - // An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance. - int drag_group_id; - // An ID containing the extruder ID (used to select color). - int extruder_id; - // Is this object selected? - bool selected; - // Whether or not this volume is active for rendering - bool is_active; - // Whether or not to use this volume when applying zoom_to_volumes() - bool zoom_to_volumes; - // Wheter or not this volume is enabled for outside print volume detection in shader. - bool shader_outside_printer_detection_enabled; - // Wheter or not this volume is outside print volume. - bool is_outside; - // Boolean: Is mouse over this object? - bool hover; - // Wheter or not this volume has been generated from a modifier - bool is_modifier; - // Wheter or not this volume has been generated from the wipe tower - bool is_wipe_tower; - // Wheter or not this volume has been generated from an extrusion path - bool is_extrusion_path; - - // Interleaved triangles & normals with indexed triangles & quads. - GLIndexedVertexArray indexed_vertex_array; - // Ranges of triangle and quad indices to be rendered. - std::pair<size_t, size_t> tverts_range; - std::pair<size_t, size_t> qverts_range; - - // If the qverts or tverts contain thick extrusions, then offsets keeps pointers of the starts - // of the extrusions per layer. - std::vector<coordf_t> print_zs; - // Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer. - std::vector<size_t> offsets; - - void set_render_color(float r, float g, float b, float a); - void set_render_color(const float* rgba, unsigned int size); - // Sets render color in dependence of current state - void set_render_color(); - - double get_rotation(); - void set_rotation(double rotation); - - const Vec3d& get_offset() const; - void set_offset(const Vec3d& offset); - - void set_scaling_factor(double factor); - - void set_convex_hull(const TriangleMesh& convex_hull); - - void set_select_group_id(const std::string& select_by); - void set_drag_group_id(const std::string& drag_by); - - int object_idx() const { return this->composite_id / 1000000; } - int volume_idx() const { return (this->composite_id / 1000) % 1000; } - int instance_idx() const { return this->composite_id % 1000; } - - const Transform3f& world_matrix() const; - const BoundingBoxf3& transformed_bounding_box() const; - const BoundingBoxf3& transformed_convex_hull_bounding_box() const; - - bool empty() const { return this->indexed_vertex_array.empty(); } - bool indexed() const { return this->indexed_vertex_array.indexed(); } - - void set_range(coordf_t low, coordf_t high); - void render() const; - void render_using_layer_height() const; - void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const; - void render_legacy() const; - - void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); } - void release_geometry() { this->indexed_vertex_array.release_geometry(); } - - /************************************************ Layer height texture ****************************************************/ - std::shared_ptr<LayersTexture> layer_height_texture; - // Data to render this volume using the layer height texture - LayerHeightTextureData layer_height_texture_data; - - bool has_layer_height_texture() const - { return this->layer_height_texture.get() != nullptr; } - size_t layer_height_texture_width() const - { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->width; } - size_t layer_height_texture_height() const - { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->height; } - size_t layer_height_texture_cells() const - { return (this->layer_height_texture.get() == nullptr) ? 0 : this->layer_height_texture->cells; } - void* layer_height_texture_data_ptr_level0() const { - return (layer_height_texture.get() == nullptr) ? 0 : - (void*)layer_height_texture->data.data(); - } - void* layer_height_texture_data_ptr_level1() const { - return (layer_height_texture.get() == nullptr) ? 0 : - (void*)(layer_height_texture->data.data() + layer_height_texture->width * layer_height_texture->height * 4); - } - double layer_height_texture_z_to_row_id() const; - void generate_layer_height_texture(const PrintObject *print_object, bool force); - - void set_layer_height_texture_data(unsigned int texture_id, unsigned int shader_id, const PrintObject* print_object, float z_cursor_relative, float edit_band_width) - { - layer_height_texture_data.texture_id = texture_id; - layer_height_texture_data.shader_id = shader_id; - layer_height_texture_data.print_object = print_object; - layer_height_texture_data.z_cursor_relative = z_cursor_relative; - layer_height_texture_data.edit_band_width = edit_band_width; - } - - void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } -}; - -class GLVolumeCollection -{ - // min and max vertex of the print box volume - float print_box_min[3]; - float print_box_max[3]; - -public: - std::vector<GLVolume*> volumes; - - GLVolumeCollection() {}; - ~GLVolumeCollection() { clear(); }; - - std::vector<int> load_object( - const ModelObject *model_object, - int obj_idx, - const std::vector<int> &instance_idxs, - const std::string &color_by, - const std::string &select_by, - const std::string &drag_by, - bool use_VBOs); - - int load_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width); - - // Render the volumes by OpenGL. - void render_VBOs() const; - void render_legacy() const; - - // Finalize the initialization of the geometry & indices, - // upload the geometry and indices to OpenGL VBO objects - // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. - void finalize_geometry(bool use_VBOs) { for (auto *v : volumes) v->finalize_geometry(use_VBOs); } - // Release the geometry data assigned to the volumes. - // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. - void release_geometry() { for (auto *v : volumes) v->release_geometry(); } - // Clear the geometry - void clear() { for (auto *v : volumes) delete v; volumes.clear(); } - - bool empty() const { return volumes.empty(); } - void set_range(double low, double high) { for (GLVolume *vol : this->volumes) vol->set_range(low, high); } - - void set_print_box(float min_x, float min_y, float min_z, float max_x, float max_y, float max_z) { - print_box_min[0] = min_x; print_box_min[1] = min_y; print_box_min[2] = min_z; - print_box_max[0] = max_x; print_box_max[1] = max_y; print_box_max[2] = max_z; - } - - // returns true if all the volumes are completely contained in the print volume - // returns the containment state in the given out_state, if non-null - bool check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state); - void reset_outside_state(); - - void update_colors_by_extruder(const DynamicPrintConfig* config); - - void set_select_by(const std::string& select_by); - void set_drag_by(const std::string& drag_by); - - // Returns a vector containing the sorted list of all the print_zs of the volumes contained in this collection - std::vector<double> get_current_print_zs(bool active_only) const; - -private: - GLVolumeCollection(const GLVolumeCollection &other); - GLVolumeCollection& operator=(const GLVolumeCollection &); -}; - -class _3DScene -{ - static GUI::GLCanvas3DManager s_canvas_mgr; - -public: - static void init_gl(); - static std::string get_gl_info(bool format_as_html, bool extensions); - static bool use_VBOs(); - - static bool add_canvas(wxGLCanvas* canvas); - static bool remove_canvas(wxGLCanvas* canvas); - static void remove_all_canvases(); - - static bool init(wxGLCanvas* canvas); - - static void set_as_dirty(wxGLCanvas* canvas); - - static unsigned int get_volumes_count(wxGLCanvas* canvas); - static void reset_volumes(wxGLCanvas* canvas); - static void deselect_volumes(wxGLCanvas* canvas); - static void select_volume(wxGLCanvas* canvas, unsigned int id); - static void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections); - static int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config); - static bool move_volume_up(wxGLCanvas* canvas, unsigned int id); - static bool move_volume_down(wxGLCanvas* canvas, unsigned int id); - - static void set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections); - - static void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); - static void set_print(wxGLCanvas* canvas, Print* print); - static void set_model(wxGLCanvas* canvas, Model* model); - - static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); - static void set_auto_bed_shape(wxGLCanvas* canvas); - - static BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); - - static void set_axes_length(wxGLCanvas* canvas, float length); - - static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); - - static void set_color_by(wxGLCanvas* canvas, const std::string& value); - static void set_select_by(wxGLCanvas* canvas, const std::string& value); - static void set_drag_by(wxGLCanvas* canvas, const std::string& value); - - static std::string get_select_by(wxGLCanvas* canvas); - - static bool is_layers_editing_enabled(wxGLCanvas* canvas); - static bool is_layers_editing_allowed(wxGLCanvas* canvas); - static bool is_shader_enabled(wxGLCanvas* canvas); - - static bool is_reload_delayed(wxGLCanvas* canvas); - - static void enable_layers_editing(wxGLCanvas* canvas, bool enable); - static void enable_warning_texture(wxGLCanvas* canvas, bool enable); - static void enable_legend_texture(wxGLCanvas* canvas, bool enable); - static void enable_picking(wxGLCanvas* canvas, bool enable); - static void enable_moving(wxGLCanvas* canvas, bool enable); - static void enable_gizmos(wxGLCanvas* canvas, bool enable); - static void enable_toolbar(wxGLCanvas* canvas, bool enable); - static void enable_shader(wxGLCanvas* canvas, bool enable); - static void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); - static void enable_dynamic_background(wxGLCanvas* canvas, bool enable); - static void allow_multisample(wxGLCanvas* canvas, bool allow); - - static void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable); - static bool is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name); - - static void zoom_to_bed(wxGLCanvas* canvas); - static void zoom_to_volumes(wxGLCanvas* canvas); - static void select_view(wxGLCanvas* canvas, const std::string& direction); - static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); - - static void update_volumes_colors_by_extruder(wxGLCanvas* canvas); - static void update_gizmos_data(wxGLCanvas* canvas); - - static void render(wxGLCanvas* canvas); - - static std::vector<double> get_current_print_zs(wxGLCanvas* canvas, bool active_only); - static void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); - - static void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - static void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); - static void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); - static void register_on_select_object_callback(wxGLCanvas* canvas, void* callback); - static void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); - static void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); - static void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); - static void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); - static void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback); - static void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); - static void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); - static void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); - static void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); - static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); - static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); - static void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); - static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); - - static void register_action_add_callback(wxGLCanvas* canvas, void* callback); - static void register_action_delete_callback(wxGLCanvas* canvas, void* callback); - static void register_action_deleteall_callback(wxGLCanvas* canvas, void* callback); - static void register_action_arrange_callback(wxGLCanvas* canvas, void* callback); - static void register_action_more_callback(wxGLCanvas* canvas, void* callback); - static void register_action_fewer_callback(wxGLCanvas* canvas, void* callback); - static void register_action_split_callback(wxGLCanvas* canvas, void* callback); - static void register_action_cut_callback(wxGLCanvas* canvas, void* callback); - static void register_action_settings_callback(wxGLCanvas* canvas, void* callback); - static void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); - static void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback); - - static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs); - static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); - - static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx); - static int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx); - - static void reload_scene(wxGLCanvas* canvas, bool force); - - static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors); - static void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors); - - static void reset_legend_texture(); - - static void thick_lines_to_verts(const Lines& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, double top_z, GLVolume& volume); - static void thick_lines_to_verts(const Lines3& lines, const std::vector<double>& widths, const std::vector<double>& heights, bool closed, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionPath& extrusion_path, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionLoop& extrusion_loop, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionMultiPath& extrusion_multi_path, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionEntityCollection& extrusion_entity_collection, float print_z, const Point& copy, GLVolume& volume); - static void extrusionentity_to_verts(const ExtrusionEntity* extrusion_entity, float print_z, const Point& copy, GLVolume& volume); - static void polyline3_to_verts(const Polyline3& polyline, double width, double height, GLVolume& volume); - static void point3_to_verts(const Vec3crd& point, double width, double height, GLVolume& volume); -}; - -} - -#endif diff --git a/xs/src/slic3r/GUI/AboutDialog.cpp b/xs/src/slic3r/GUI/AboutDialog.cpp deleted file mode 100644 index 0fed8d175..000000000 --- a/xs/src/slic3r/GUI/AboutDialog.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "AboutDialog.hpp" - -#include "../../libslic3r/Utils.hpp" - -namespace Slic3r { -namespace GUI { - -AboutDialogLogo::AboutDialogLogo(wxWindow* parent) - : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize) -{ - this->SetBackgroundColour(*wxWHITE); - this->logo = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG); - this->SetMinSize(this->logo.GetSize()); - - this->Bind(wxEVT_PAINT, &AboutDialogLogo::onRepaint, this); -} - -void AboutDialogLogo::onRepaint(wxEvent &event) -{ - wxPaintDC dc(this); - dc.SetBackgroundMode(wxTRANSPARENT); - - wxSize size = this->GetSize(); - int logo_w = this->logo.GetWidth(); - int logo_h = this->logo.GetHeight(); - dc.DrawBitmap(this->logo, (size.GetWidth() - logo_w)/2, (size.GetHeight() - logo_h)/2, true); - - event.Skip(); -} - -AboutDialog::AboutDialog() - : wxDialog(NULL, wxID_ANY, _(L("About Slic3r")), wxDefaultPosition, wxDefaultSize, wxCAPTION) -{ - wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - SetBackgroundColour(bgr_clr); - wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); - - auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(hsizer, 0, wxEXPAND | wxALL, 20); - - // logo - wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG); - auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(logo_bmp)); - hsizer->Add(logo, 1, wxALIGN_CENTRE_VERTICAL | wxEXPAND | wxTOP | wxBOTTOM, 35); - - wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); -#ifdef __WXMSW__ - int proportion = 2; -#else - int proportion = 3; -#endif - hsizer->Add(vsizer, proportion, wxEXPAND|wxLEFT, 20); - - // title - { - wxStaticText* title = new wxStaticText(this, wxID_ANY, "Slic3r Prusa Edition", wxDefaultPosition, wxDefaultSize); - wxFont title_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - title_font.SetWeight(wxFONTWEIGHT_BOLD); - title_font.SetFamily(wxFONTFAMILY_ROMAN); - title_font.SetPointSize(24); - title->SetFont(title_font); - vsizer->Add(title, 0, wxALIGN_LEFT | wxTOP, 10); - } - - // version - { - auto version_string = _(L("Version"))+ " " + std::string(SLIC3R_VERSION); - wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize); - wxFont version_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - #ifdef __WXMSW__ - version_font.SetPointSize(9); - #else - version_font.SetPointSize(11); - #endif - version->SetFont(version_font); - vsizer->Add(version, 0, wxALIGN_LEFT | wxBOTTOM, 10); - } - - // text - wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO/*NEVER*/); - { - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - const auto text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue()); - auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()); - - const int fs = font.GetPointSize()-1; - int size[] = {fs,fs,fs,fs,fs,fs,fs}; - html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); - html->SetBorders(2); - const auto text = wxString::Format( - "<html>" - "<body bgcolor= %s link= %s>" - "<font color=%s>" - "Copyright © 2016-2018 Prusa Research. <br />" - "Copyright © 2011-2017 Alessandro Ranellucci. <br />" - "<a href=\"http://slic3r.org/\">Slic3r</a> is licensed under the " - "<a href=\"http://www.gnu.org/licenses/agpl-3.0.html\">GNU Affero General Public License, version 3</a>." - "<br /><br />" - "Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. " - "Manual by Gary Hodgson. Inspired by the RepRap community. <br />" - "Slic3r logo designed by Corey Daniels, <a href=\"http://www.famfamfam.com/lab/icons/silk/\">Silk Icon Set</a> designed by Mark James. " - "</font>" - "</body>" - "</html>", bgr_clr_str, text_clr_str, text_clr_str); - html->SetPage(text); - vsizer->Add(html, 1, wxEXPAND | wxBOTTOM, 10); - html->Bind(wxEVT_HTML_LINK_CLICKED, &AboutDialog::onLinkClicked, this); - } - - wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE); - this->SetEscapeId(wxID_CLOSE); - this->Bind(wxEVT_BUTTON, &AboutDialog::onCloseDialog, this, wxID_CLOSE); - vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); - - this->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this); - logo->Bind(wxEVT_LEFT_DOWN, &AboutDialog::onCloseDialog, this); - - SetSizer(main_sizer); - main_sizer->SetSizeHints(this); -} - -void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event) -{ - wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref()); - event.Skip(false); -} - -void AboutDialog::onCloseDialog(wxEvent &) -{ - this->EndModal(wxID_CLOSE); - this->Close(); -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/AboutDialog.hpp b/xs/src/slic3r/GUI/AboutDialog.hpp deleted file mode 100644 index 01f7564c5..000000000 --- a/xs/src/slic3r/GUI/AboutDialog.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef slic3r_GUI_AboutDialog_hpp_ -#define slic3r_GUI_AboutDialog_hpp_ - -#include "GUI.hpp" - -#include <wx/wx.h> -#include <wx/intl.h> -#include <wx/html/htmlwin.h> - -namespace Slic3r { -namespace GUI { - -class AboutDialogLogo : public wxPanel -{ -public: - AboutDialogLogo(wxWindow* parent); - -private: - wxBitmap logo; - void onRepaint(wxEvent &event); -}; - -class AboutDialog : public wxDialog -{ -public: - AboutDialog(); - -private: - void onLinkClicked(wxHtmlLinkEvent &event); - void onCloseDialog(wxEvent &); -}; - -} // namespace GUI -} // namespace Slic3r - -#endif diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp deleted file mode 100644 index d7307cc32..000000000 --- a/xs/src/slic3r/GUI/AppConfig.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Utils.hpp" -#include "AppConfig.hpp" - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <utility> -#include <assert.h> -#include <vector> -#include <stdexcept> - -#include <boost/filesystem.hpp> -#include <boost/nowide/cenv.hpp> -#include <boost/nowide/fstream.hpp> -#include <boost/property_tree/ini_parser.hpp> -#include <boost/property_tree/ptree.hpp> -#include <boost/algorithm/string/predicate.hpp> - -namespace Slic3r { - -static const std::string VENDOR_PREFIX = "vendor:"; -static const std::string MODEL_PREFIX = "model:"; -static const std::string VERSION_CHECK_URL = "https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/Slic3rPE.version"; - -void AppConfig::reset() -{ - m_storage.clear(); - set_defaults(); -}; - -// Override missing or keys with their defaults. -void AppConfig::set_defaults() -{ - // Reset the empty fields to defaults. - if (get("autocenter").empty()) - set("autocenter", "0"); - // Disable background processing by default as it is not stable. - if (get("background_processing").empty()) - set("background_processing", "0"); - // If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. - // By default, Prusa has the controller hidden. - if (get("no_controller").empty()) - set("no_controller", "1"); - // If set, the "- default -" selections of print/filament/printer are suppressed, if there is a valid preset available. - if (get("no_defaults").empty()) - set("no_defaults", "1"); - if (get("show_incompatible_presets").empty()) - set("show_incompatible_presets", "0"); - - if (get("version_check").empty()) - set("version_check", "1"); - if (get("preset_update").empty()) - set("preset_update", "1"); - - // Use OpenGL 1.1 even if OpenGL 2.0 is available. This is mainly to support some buggy Intel HD Graphics drivers. - // https://github.com/prusa3d/Slic3r/issues/233 - if (get("use_legacy_opengl").empty()) - set("use_legacy_opengl", "0"); - - if (get("remember_output_path").empty()) - set("remember_output_path", "1"); - - // Remove legacy window positions/sizes - erase("", "main_frame_maximized"); - erase("", "main_frame_pos"); - erase("", "main_frame_size"); - erase("", "object_settings_maximized"); - erase("", "object_settings_pos"); - erase("", "object_settings_size"); -} - -void AppConfig::load() -{ - // 1) Read the complete config file into a boost::property_tree. - namespace pt = boost::property_tree; - pt::ptree tree; - boost::nowide::ifstream ifs(AppConfig::config_path()); - pt::read_ini(ifs, tree); - - // 2) Parse the property_tree, extract the sections and key / value pairs. - for (const auto §ion : tree) { - if (section.second.empty()) { - // This may be a top level (no section) entry, or an empty section. - std::string data = section.second.data(); - if (! data.empty()) - // If there is a non-empty data, then it must be a top-level (without a section) config entry. - m_storage[""][section.first] = data; - } else if (boost::starts_with(section.first, VENDOR_PREFIX)) { - // This is a vendor section listing enabled model / variants - const auto vendor_name = section.first.substr(VENDOR_PREFIX.size()); - auto &vendor = m_vendors[vendor_name]; - for (const auto &kvp : section.second) { - if (! boost::starts_with(kvp.first, MODEL_PREFIX)) { continue; } - const auto model_name = kvp.first.substr(MODEL_PREFIX.size()); - std::vector<std::string> variants; - if (! unescape_strings_cstyle(kvp.second.data(), variants)) { continue; } - for (const auto &variant : variants) { - vendor[model_name].insert(variant); - } - } - } else { - // This must be a section name. Read the entries of a section. - std::map<std::string, std::string> &storage = m_storage[section.first]; - for (auto &kvp : section.second) - storage[kvp.first] = kvp.second.data(); - } - } - - // Figure out if datadir has legacy presets - auto ini_ver = Semver::parse(get("version")); - m_legacy_datadir = false; - if (ini_ver) { - m_orig_version = *ini_ver; - // Make 1.40.0 alphas compare well - ini_ver->set_metadata(boost::none); - ini_ver->set_prerelease(boost::none); - m_legacy_datadir = ini_ver < Semver(1, 40, 0); - } - - // Override missing or keys with their defaults. - this->set_defaults(); - m_dirty = false; -} - -void AppConfig::save() -{ - boost::nowide::ofstream c; - c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc); - c << "# " << Slic3r::header_slic3r_generated() << std::endl; - // Make sure the "no" category is written first. - for (const std::pair<std::string, std::string> &kvp : m_storage[""]) - c << kvp.first << " = " << kvp.second << std::endl; - // Write the other categories. - for (const auto category : m_storage) { - if (category.first.empty()) - continue; - c << std::endl << "[" << category.first << "]" << std::endl; - for (const std::pair<std::string, std::string> &kvp : category.second) - c << kvp.first << " = " << kvp.second << std::endl; - } - // Write vendor sections - for (const auto &vendor : m_vendors) { - size_t size_sum = 0; - for (const auto &model : vendor.second) { size_sum += model.second.size(); } - if (size_sum == 0) { continue; } - - c << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl; - - for (const auto &model : vendor.second) { - if (model.second.size() == 0) { continue; } - const std::vector<std::string> variants(model.second.begin(), model.second.end()); - const auto escaped = escape_strings_cstyle(variants); - c << MODEL_PREFIX << model.first << " = " << escaped << std::endl; - } - } - c.close(); - m_dirty = false; -} - -bool AppConfig::get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const -{ - const auto it_v = m_vendors.find(vendor); - if (it_v == m_vendors.end()) { return false; } - const auto it_m = it_v->second.find(model); - return it_m == it_v->second.end() ? false : it_m->second.find(variant) != it_m->second.end(); -} - -void AppConfig::set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable) -{ - if (enable) { - if (get_variant(vendor, model, variant)) { return; } - m_vendors[vendor][model].insert(variant); - } else { - auto it_v = m_vendors.find(vendor); - if (it_v == m_vendors.end()) { return; } - auto it_m = it_v->second.find(model); - if (it_m == it_v->second.end()) { return; } - auto it_var = it_m->second.find(variant); - if (it_var == it_m->second.end()) { return; } - it_m->second.erase(it_var); - } - // If we got here, there was an update - m_dirty = true; -} - -void AppConfig::set_vendors(const AppConfig &from) -{ - m_vendors = from.m_vendors; - m_dirty = true; -} - -std::string AppConfig::get_last_dir() const -{ - const auto it = m_storage.find("recent"); - if (it != m_storage.end()) { - { - const auto it2 = it->second.find("skein_directory"); - if (it2 != it->second.end() && ! it2->second.empty()) - return it2->second; - } - { - const auto it2 = it->second.find("config_directory"); - if (it2 != it->second.end() && ! it2->second.empty()) - return it2->second; - } - } - return std::string(); -} - -void AppConfig::update_config_dir(const std::string &dir) -{ - this->set("recent", "config_directory", dir); -} - -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"); - const auto it3 = it->second.find("remember_output_path"); - 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) -{ - this->set("", "last_output_path", dir); -} - -void AppConfig::reset_selections() -{ - auto it = m_storage.find("presets"); - if (it != m_storage.end()) { - it->second.erase("print"); - it->second.erase("filament"); - it->second.erase("sla_material"); - it->second.erase("printer"); - m_dirty = true; - } -} - -std::string AppConfig::config_path() -{ - return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); -} - -std::string AppConfig::version_check_url() const -{ - auto from_settings = get("version_check_url"); - return from_settings.empty() ? VERSION_CHECK_URL : from_settings; -} - -bool AppConfig::exists() -{ - return boost::filesystem::exists(AppConfig::config_path()); -} - -}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp deleted file mode 100644 index 5af635a12..000000000 --- a/xs/src/slic3r/GUI/AppConfig.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef slic3r_AppConfig_hpp_ -#define slic3r_AppConfig_hpp_ - -#include <set> -#include <map> -#include <string> - -#include "libslic3r/Config.hpp" -#include "slic3r/Utils/Semver.hpp" - -namespace Slic3r { - -class AppConfig -{ -public: - AppConfig() : - m_dirty(false), - m_orig_version(Semver::invalid()), - m_legacy_datadir(false) - { - this->reset(); - } - - // Clear and reset to defaults. - void reset(); - // Override missing or keys with their defaults. - void set_defaults(); - - // Load the slic3r.ini from a user profile directory (or a datadir, if configured). - void load(); - // Store the slic3r.ini into a user profile directory (or a datadir, if configured). - void save(); - - // Does this config need to be saved? - bool dirty() const { return m_dirty; } - - // Const accessor, it will return false if a section or a key does not exist. - bool get(const std::string §ion, const std::string &key, std::string &value) const - { - value.clear(); - auto it = m_storage.find(section); - if (it == m_storage.end()) - return false; - auto it2 = it->second.find(key); - if (it2 == it->second.end()) - return false; - value = it2->second; - return true; - } - std::string get(const std::string §ion, const std::string &key) const - { std::string value; this->get(section, key, value); return value; } - std::string get(const std::string &key) const - { std::string value; this->get("", key, value); return value; } - void set(const std::string §ion, const std::string &key, const std::string &value) - { - std::string &old = m_storage[section][key]; - if (old != value) { - old = value; - m_dirty = true; - } - } - void set(const std::string &key, const std::string &value) - { this->set("", key, value); } - bool has(const std::string §ion, const std::string &key) const - { - auto it = m_storage.find(section); - if (it == m_storage.end()) - return false; - auto it2 = it->second.find(key); - return it2 != it->second.end() && ! it2->second.empty(); - } - bool has(const std::string &key) const - { return this->has("", key); } - - void erase(const std::string §ion, const std::string &key) - { - auto it = m_storage.find(section); - if (it != m_storage.end()) { - it->second.erase(key); - } - } - - void clear_section(const std::string §ion) - { m_storage[section].clear(); } - - typedef std::map<std::string, std::map<std::string, std::set<std::string>>> VendorMap; - bool get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const; - void set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable); - void set_vendors(const AppConfig &from); - void set_vendors(const VendorMap &vendors) { m_vendors = vendors; m_dirty = true; } - void set_vendors(VendorMap &&vendors) { m_vendors = std::move(vendors); m_dirty = true; } - const VendorMap& vendors() const { return m_vendors; } - - // return recent/skein_directory or recent/config_directory or empty string. - std::string get_last_dir() const; - 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); - - // reset the current print / filament / printer selections, so that - // the PresetBundle::load_selections(const AppConfig &config) call will select - // the first non-default preset when called. - void reset_selections(); - - // Get the default config path from Slic3r::data_dir(). - static std::string config_path(); - - // Returns true if the user's data directory comes from before Slic3r 1.40.0 (no updating) - bool legacy_datadir() const { return m_legacy_datadir; } - void set_legacy_datadir(bool value) { m_legacy_datadir = value; } - - // Get the Slic3r version check url. - // This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file. - std::string version_check_url() const; - - // Returns the original Slic3r version found in the ini file before it was overwritten - // by the current version - Semver orig_version() const { return m_orig_version; } - - // Does the config file exist? - static bool exists(); - -private: - // Map of section, name -> value - std::map<std::string, std::map<std::string, std::string>> m_storage; - // Map of enabled vendors / models / variants - VendorMap m_vendors; - // Has any value been modified since the config.ini has been last saved or loaded? - bool m_dirty; - // Original version found in the ini file before it was overwritten - Semver m_orig_version; - // Whether the existing version is before system profiles & configuration updating - bool m_legacy_datadir; -}; - -}; // namespace Slic3r - -#endif /* slic3r_AppConfig_hpp_ */ diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp deleted file mode 100644 index 99997e390..000000000 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "BackgroundSlicingProcess.hpp" -#include "GUI.hpp" - -#include <wx/event.h> -#include <wx/panel.h> -#include <wx/stdpaths.h> - -// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. -#include "../../libslic3r/Print.hpp" -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/GCode/PostProcessor.hpp" - -//#undef NDEBUG -#include <cassert> -#include <stdexcept> - -#include <boost/format.hpp> -#include <boost/nowide/cstdio.hpp> - -namespace Slic3r { - -namespace GUI { - extern wxPanel *g_wxPlater; -}; - -BackgroundSlicingProcess::BackgroundSlicingProcess() -{ - m_temp_output_path = wxStandardPaths::Get().GetTempDir().utf8_str().data(); - m_temp_output_path += (boost::format(".%1%.gcode") % get_current_pid()).str(); -} - -BackgroundSlicingProcess::~BackgroundSlicingProcess() -{ - this->stop(); - this->join_background_thread(); - boost::nowide::remove(m_temp_output_path.c_str()); -} - -void BackgroundSlicingProcess::thread_proc() -{ - std::unique_lock<std::mutex> lck(m_mutex); - // Let the caller know we are ready to run the background processing task. - m_state = STATE_IDLE; - lck.unlock(); - m_condition.notify_one(); - for (;;) { - assert(m_state == STATE_IDLE || m_state == STATE_CANCELED || m_state == STATE_FINISHED); - // Wait until a new task is ready to be executed, or this thread should be finished. - lck.lock(); - m_condition.wait(lck, [this](){ return m_state == STATE_STARTED || m_state == STATE_EXIT; }); - if (m_state == STATE_EXIT) - // Exiting this thread. - break; - // Process the background slicing task. - m_state = STATE_RUNNING; - lck.unlock(); - std::string error; - try { - assert(m_print != nullptr); - m_print->process(); - if (! m_print->canceled()) { - wxQueueEvent(GUI::g_wxPlater, new wxCommandEvent(m_event_sliced_id)); - m_print->export_gcode(m_temp_output_path, m_gcode_preview_data); - if (! m_print->canceled() && ! m_output_path.empty()) { - if (copy_file(m_temp_output_path, m_output_path) != 0) - throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); - m_print->set_status(95, "Running post-processing scripts"); - run_post_process_scripts(m_output_path, m_print->config()); - } - } - } catch (CanceledException &ex) { - // Canceled, this is all right. - assert(m_print->canceled()); - } catch (std::exception &ex) { - error = ex.what(); - } catch (...) { - error = "Unknown C++ exception."; - } - lck.lock(); - m_state = m_print->canceled() ? STATE_CANCELED : STATE_FINISHED; - wxCommandEvent evt(m_event_finished_id); - evt.SetString(error); - evt.SetInt(m_print->canceled() ? -1 : (error.empty() ? 1 : 0)); - wxQueueEvent(GUI::g_wxPlater, evt.Clone()); - m_print->restart(); - lck.unlock(); - // Let the UI thread wake up if it is waiting for the background task to finish. - m_condition.notify_one(); - // Let the UI thread see the result. - } - m_state = STATE_EXITED; - lck.unlock(); - // End of the background processing thread. The UI thread should join m_thread now. -} - -void BackgroundSlicingProcess::join_background_thread() -{ - std::unique_lock<std::mutex> lck(m_mutex); - if (m_state == STATE_INITIAL) { - // Worker thread has not been started yet. - assert(! m_thread.joinable()); - } else { - assert(m_state == STATE_IDLE); - assert(m_thread.joinable()); - // Notify the worker thread to exit. - m_state = STATE_EXIT; - lck.unlock(); - m_condition.notify_one(); - // Wait until the worker thread exits. - m_thread.join(); - } -} - -bool BackgroundSlicingProcess::start() -{ - std::unique_lock<std::mutex> lck(m_mutex); - if (m_state == STATE_INITIAL) { - // The worker thread is not running yet. Start it. - assert(! m_thread.joinable()); - m_thread = std::thread([this]{this->thread_proc();}); - // Wait until the worker thread is ready to execute the background processing task. - m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; }); - } - assert(m_state == STATE_IDLE || this->running()); - if (this->running()) - // The background processing thread is already running. - return false; - if (! this->idle()) - throw std::runtime_error("Cannot start a background task, the worker thread is not idle."); - m_state = STATE_STARTED; - lck.unlock(); - m_condition.notify_one(); - return true; -} - -bool BackgroundSlicingProcess::stop() -{ - std::unique_lock<std::mutex> lck(m_mutex); - if (m_state == STATE_INITIAL) { - this->m_output_path.clear(); - return false; - } - assert(this->running()); - if (m_state == STATE_STARTED || m_state == STATE_RUNNING) { - m_print->cancel(); - // Wait until the background processing stops by being canceled. - m_condition.wait(lck, [this](){ return m_state == STATE_CANCELED; }); - // In the "Canceled" state. Reset the state to "Idle". - m_state = STATE_IDLE; - } else if (m_state == STATE_FINISHED || m_state == STATE_CANCELED) { - // In the "Finished" or "Canceled" state. Reset the state to "Idle". - m_state = STATE_IDLE; - } - this->m_output_path.clear(); - return true; -} - -// Apply config over the print. Returns false, if the new config values caused any of the already -// processed steps to be invalidated, therefore the task will need to be restarted. -bool BackgroundSlicingProcess::apply_config(const DynamicPrintConfig &config) -{ - this->stop(); - bool invalidated = m_print->apply_config(config); - return invalidated; -} - -}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp deleted file mode 100644 index cc7a6db30..000000000 --- a/xs/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#ifndef slic3r_GUI_BackgroundSlicingProcess_hpp_ -#define slic3r_GUI_BackgroundSlicingProcess_hpp_ - -#include <string> -#include <condition_variable> -#include <mutex> -#include <thread> - -namespace Slic3r { - -class DynamicPrintConfig; -class GCodePreviewData; -class Print; - -// Support for the GUI background processing (Slicing and G-code generation). -// As of now this class is not declared in Slic3r::GUI due to the Perl bindings limits. -class BackgroundSlicingProcess -{ -public: - BackgroundSlicingProcess(); - // Stop the background processing and finalize the bacgkround processing thread, remove temp files. - ~BackgroundSlicingProcess(); - - void set_print(Print *print) { m_print = print; } - void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; } - // The following wxCommandEvent will be sent to the UI thread / Platter 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_sliced_event(int event_id) { m_event_sliced_id = event_id; } - // The following wxCommandEvent will be sent to the UI thread / Platter 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; } - - // Set the output path of the G-code. - void set_output_path(const std::string &path) { m_output_path = path; } - // Start the background processing. Returns false if the background processing was already running. - bool start(); - // Cancel the background processing. Returns false if the background processing was not running. - // A stopped background processing may be restarted with start(). - bool stop(); - - // Apply config over the print. Returns false, if the new config values caused any of the already - // processed steps to be invalidated, therefore the task will need to be restarted. - bool apply_config(const DynamicPrintConfig &config); - - enum State { - // m_thread is not running yet, or it did not reach the STATE_IDLE yet (it does not wait on the condition yet). - STATE_INITIAL = 0, - // m_thread is waiting for the task to execute. - STATE_IDLE, - STATE_STARTED, - // m_thread is executing a task. - STATE_RUNNING, - // m_thread finished executing a task, and it is waiting until the UI thread picks up the results. - STATE_FINISHED, - // m_thread finished executing a task, the task has been canceled by the UI thread, therefore the UI thread will not be notified. - STATE_CANCELED, - // m_thread exited the loop and it is going to finish. The UI thread should join on m_thread. - STATE_EXIT, - STATE_EXITED, - }; - State state() const { return m_state; } - bool idle() const { return m_state == STATE_IDLE; } - bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; } - -private: - void thread_proc(); - void join_background_thread(); - - Print *m_print = nullptr; - // Data structure, to which the G-code export writes its annotations. - GCodePreviewData *m_gcode_preview_data = nullptr; - std::string m_temp_output_path; - std::string m_output_path; - // Thread, on which the background processing is executed. The thread will always be present - // and ready to execute the slicing process. - std::thread m_thread; - // Mutex and condition variable to synchronize m_thread with the UI thread. - std::mutex m_mutex; - std::condition_variable m_condition; - State m_state = STATE_INITIAL; - - // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue. - int m_event_sliced_id = 0; - // wxWidgets command ID to be sent to the platter to inform that the task finished. - int m_event_finished_id = 0; -}; - -}; // namespace Slic3r - -#endif /* slic3r_GUI_BackgroundSlicingProcess_hpp_ */ diff --git a/xs/src/slic3r/GUI/BedShapeDialog.cpp b/xs/src/slic3r/GUI/BedShapeDialog.cpp deleted file mode 100644 index e04f2b370..000000000 --- a/xs/src/slic3r/GUI/BedShapeDialog.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "BedShapeDialog.hpp" - -#include <wx/sizer.h> -#include <wx/statbox.h> -#include <wx/wx.h> -#include "Polygon.hpp" -#include "BoundingBox.hpp" -#include <wx/numformatter.h> -#include "Model.hpp" -#include "boost/nowide/iostream.hpp" - -#include <algorithm> - -namespace Slic3r { -namespace GUI { - -void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt) -{ - m_panel = new BedShapePanel(this); - m_panel->build_panel(default_pt); - - auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(m_panel, 1, wxEXPAND); - main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); - - SetSizer(main_sizer); - SetMinSize(GetSize()); - main_sizer->SetSizeHints(this); - - // needed to actually free memory - this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e){ - EndModal(wxID_OK); - Destroy(); - })); -} - -void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) -{ -// on_change(nullptr); - - auto box = new wxStaticBox(this, wxID_ANY, _(L("Shape"))); - auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL); - - // shape options - m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(300, -1), wxCHB_TOP); - sbsizer->Add(m_shape_options_book); - - auto optgroup = init_shape_options_page(_(L("Rectangular"))); - ConfigOptionDef def; - def.type = coPoints; - def.default_value = new ConfigOptionPoints{ Vec2d(200, 200) }; - def.label = L("Size"); - def.tooltip = L("Size in X and Y of the rectangular plate."); - Option option(def, "rect_size"); - optgroup->append_single_option_line(option); - - def.type = coPoints; - def.default_value = new ConfigOptionPoints{ Vec2d(0, 0) }; - def.label = L("Origin"); - def.tooltip = L("Distance of the 0,0 G-code coordinate from the front left corner of the rectangle."); - option = Option(def, "rect_origin"); - optgroup->append_single_option_line(option); - - optgroup = init_shape_options_page(_(L("Circular"))); - def.type = coFloat; - def.default_value = new ConfigOptionFloat(200); - def.sidetext = L("mm"); - def.label = L("Diameter"); - def.tooltip = L("Diameter of the print bed. It is assumed that origin (0,0) is located in the center."); - option = Option(def, "diameter"); - optgroup->append_single_option_line(option); - - optgroup = init_shape_options_page(_(L("Custom"))); - Line line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")), wxDefaultPosition, wxDefaultSize); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) - { - load_stl(); - })); - - return sizer; - }; - optgroup->append_line(line); - - Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e) - { - update_shape(); - })); - - // right pane with preview canvas - m_canvas = new Bed_2D(this); - m_canvas->m_bed_shape = default_pt->values; - - // main sizer - auto top_sizer = new wxBoxSizer(wxHORIZONTAL); - top_sizer->Add(sbsizer, 0, wxEXPAND | wxLeft | wxTOP | wxBOTTOM, 10); - if (m_canvas) - top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ; - - SetSizerAndFit(top_sizer); - - set_shape(default_pt); - update_preview(); -} - -#define SHAPE_RECTANGULAR 0 -#define SHAPE_CIRCULAR 1 -#define SHAPE_CUSTOM 2 - -// Called from the constructor. -// Create a panel for a rectangular / circular / custom bed shape. -ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title){ - - auto panel = new wxPanel(m_shape_options_book); - ConfigOptionsGroupShp optgroup; - optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings"))); - - optgroup->label_width = 100; - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ - update_shape(); - }; - - m_optgroups.push_back(optgroup); - panel->SetSizerAndFit(optgroup->sizer); - m_shape_options_book->AddPage(panel, title); - - return optgroup; -} - -// Called from the constructor. -// Set the initial bed shape from a list of points. -// Deduce the bed shape type(rect, circle, custom) -// This routine shall be smart enough if the user messes up -// with the list of points in the ini file directly. -void BedShapePanel::set_shape(ConfigOptionPoints* points) -{ - auto polygon = Polygon::new_scale(points->values); - - // is this a rectangle ? - if (points->size() == 4) { - auto lines = polygon.lines(); - if (lines[0].parallel_to(lines[2]) && lines[1].parallel_to(lines[3])) { - // okay, it's a rectangle - // find origin - coordf_t x_min, x_max, y_min, y_max; - x_max = x_min = points->values[0](0); - y_max = y_min = points->values[0](1); - for (auto pt : points->values) - { - x_min = std::min(x_min, pt(0)); - x_max = std::max(x_max, pt(0)); - y_min = std::min(y_min, pt(1)); - y_max = std::max(y_max, pt(1)); - } - - auto origin = new ConfigOptionPoints{ Vec2d(-x_min, -y_min) }; - - m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); - auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(x_max - x_min, y_max - y_min) });//[x_max - x_min, y_max - y_min]); - optgroup->set_value("rect_origin", origin); - update_shape(); - return; - } - } - - // is this a circle ? - { - // Analyze the array of points.Do they reside on a circle ? - auto center = polygon.bounding_box().center(); - std::vector<double> vertex_distances; - double avg_dist = 0; - for (auto pt: polygon.points) - { - double distance = (pt - center).cast<double>().norm(); - vertex_distances.push_back(distance); - avg_dist += distance; - } - - avg_dist /= vertex_distances.size(); - bool defined_value = true; - for (auto el: vertex_distances) - { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) - defined_value = false; - break; - } - if (defined_value) { - // all vertices are equidistant to center - m_shape_options_book->SetSelection(SHAPE_CIRCULAR); - auto optgroup = m_optgroups[SHAPE_CIRCULAR]; - boost::any ret = wxNumberFormatter::ToString(unscale<double>(avg_dist * 2), 0); - optgroup->set_value("diameter", ret); - update_shape(); - return; - } - } - - if (points->size() < 3) { - // Invalid polygon.Revert to default bed dimensions. - m_shape_options_book->SetSelection(SHAPE_RECTANGULAR); - auto optgroup = m_optgroups[SHAPE_RECTANGULAR]; - optgroup->set_value("rect_size", new ConfigOptionPoints{ Vec2d(200, 200) }); - optgroup->set_value("rect_origin", new ConfigOptionPoints{ Vec2d(0, 0) }); - update_shape(); - return; - } - - // This is a custom bed shape, use the polygon provided. - m_shape_options_book->SetSelection(SHAPE_CUSTOM); - // Copy the polygon to the canvas, make a copy of the array. - m_canvas->m_bed_shape = points->values; - update_shape(); -} - -void BedShapePanel::update_preview() -{ - if (m_canvas) m_canvas->Refresh(); - Refresh(); -} - -// Update the bed shape from the dialog fields. -void BedShapePanel::update_shape() -{ - auto page_idx = m_shape_options_book->GetSelection(); - if (page_idx == SHAPE_RECTANGULAR) { - Vec2d rect_size(Vec2d::Zero()); - Vec2d rect_origin(Vec2d::Zero()); - try{ - rect_size = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_size")); } - catch (const std::exception &e){ - return; - } - try{ - rect_origin = boost::any_cast<Vec2d>(m_optgroups[SHAPE_RECTANGULAR]->get_value("rect_origin")); - } - catch (const std::exception &e){ - return;} - - auto x = rect_size(0); - auto y = rect_size(1); - // empty strings or '-' or other things - if (x == 0 || y == 0) return; - double x0 = 0.0; - double y0 = 0.0; - double x1 = x; - double y1 = y; - - auto dx = rect_origin(0); - auto dy = rect_origin(1); - - x0 -= dx; - x1 -= dx; - y0 -= dy; - y1 -= dy; - m_canvas->m_bed_shape = { Vec2d(x0, y0), - Vec2d(x1, y0), - Vec2d(x1, y1), - Vec2d(x0, y1)}; - } - else if(page_idx == SHAPE_CIRCULAR) { - double diameter; - try{ - diameter = boost::any_cast<double>(m_optgroups[SHAPE_CIRCULAR]->get_value("diameter")); - } - catch (const std::exception &e){ - return; - } - if (diameter == 0.0) return ; - auto r = diameter / 2; - auto twopi = 2 * PI; - auto edges = 60; - std::vector<Vec2d> points; - for (size_t i = 1; i <= 60; ++i){ - auto angle = i * twopi / edges; - points.push_back(Vec2d(r*cos(angle), r*sin(angle))); - } - m_canvas->m_bed_shape = points; - } - -// $self->{on_change}->(); - update_preview(); -} - -// Loads an stl file, projects it to the XY plane and calculates a polygon. -void BedShapePanel::load_stl() -{ - t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card(); - std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "3mf", "prusa" }; - wxString MODEL_WILDCARD; - for (auto file_type: file_types) - MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|"; - - auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (dialog->ShowModal() != wxID_OK) { - dialog->Destroy(); - return; - } - wxArrayString input_file; - dialog->GetPaths(input_file); - dialog->Destroy(); - - std::string file_name = input_file[0].ToStdString(); - - Model model; - try { - model = Model::read_from_file(file_name); - } - catch (std::exception &e) { - auto msg = _(L("Error! ")) + file_name + " : " + e.what() + "."; - show_error(this, msg); - exit(1); - } - - auto mesh = model.mesh(); - auto expolygons = mesh.horizontal_projection(); - - if (expolygons.size() == 0) { - show_error(this, _(L("The selected file contains no geometry."))); - return; - } - if (expolygons.size() > 1) { - show_error(this, _(L("The selected file contains several disjoint areas. This is not supported."))); - return; - } - - auto polygon = expolygons[0].contour; - std::vector<Vec2d> points; - for (auto pt : polygon.points) - points.push_back(unscale(pt)); - m_canvas->m_bed_shape = points; - update_preview(); -} - -} // GUI -} // Slic3r diff --git a/xs/src/slic3r/GUI/BedShapeDialog.hpp b/xs/src/slic3r/GUI/BedShapeDialog.hpp deleted file mode 100644 index d8ba5a912..000000000 --- a/xs/src/slic3r/GUI/BedShapeDialog.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef slic3r_BedShapeDialog_hpp_ -#define slic3r_BedShapeDialog_hpp_ -// The bed shape dialog. -// The dialog opens from Print Settins tab->Bed Shape : Set... - -#include "OptionsGroup.hpp" -#include "2DBed.hpp" - - -#include <wx/dialog.h> -#include <wx/choicebk.h> - -namespace Slic3r { -namespace GUI { - -using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>; -class BedShapePanel : public wxPanel -{ - wxChoicebook* m_shape_options_book; - Bed_2D* m_canvas; - - std::vector <ConfigOptionsGroupShp> m_optgroups; - -public: - BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY){} - ~BedShapePanel(){} - - void build_panel(ConfigOptionPoints* default_pt); - - ConfigOptionsGroupShp init_shape_options_page(wxString title); - void set_shape(ConfigOptionPoints* points); - void update_preview(); - void update_shape(); - void load_stl(); - - // Returns the resulting bed shape polygon. This value will be stored to the ini file. - std::vector<Vec2d> GetValue() { return m_canvas->m_bed_shape; } -}; - -class BedShapeDialog : public wxDialog -{ - BedShapePanel* m_panel; -public: - BedShapeDialog(wxWindow* parent) : wxDialog(parent, wxID_ANY, _(L("Bed Shape")), - wxDefaultPosition, wxSize(350, 700), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER){} - ~BedShapeDialog(){ } - - void build_dialog(ConfigOptionPoints* default_pt); - std::vector<Vec2d> GetValue() { return m_panel->GetValue(); } -}; - -} // GUI -} // Slic3r - - -#endif /* slic3r_BedShapeDialog_hpp_ */ diff --git a/xs/src/slic3r/GUI/BitmapCache.cpp b/xs/src/slic3r/GUI/BitmapCache.cpp deleted file mode 100644 index 93853458e..000000000 --- a/xs/src/slic3r/GUI/BitmapCache.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "BitmapCache.hpp" - -#if ! defined(WIN32) && ! defined(__APPLE__) -#define BROKEN_ALPHA -#endif - -#ifdef BROKEN_ALPHA - #include <wx/mstream.h> - #include <wx/rawbmp.h> -#endif /* BROKEN_ALPHA */ - -namespace Slic3r { namespace GUI { - -void BitmapCache::clear() -{ - for (std::pair<const std::string, wxBitmap*> &bitmap : m_map) - delete bitmap.second; -} - -static wxBitmap wxImage_to_wxBitmap_with_alpha(wxImage &&image) -{ -#ifdef BROKEN_ALPHA - wxMemoryOutputStream stream; - image.SaveFile(stream, wxBITMAP_TYPE_PNG); - wxStreamBuffer *buf = stream.GetOutputStreamBuffer(); - return wxBitmap::NewFromPNGData(buf->GetBufferStart(), buf->GetBufferSize()); -#else - return wxBitmap(std::move(image)); -#endif -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_t height) -{ - wxBitmap *bitmap = nullptr; - auto it = m_map.find(bitmap_key); - if (it == m_map.end()) { - bitmap = new wxBitmap(width, height); - m_map[bitmap_key] = bitmap; - } else { - bitmap = it->second; - if (bitmap->GetWidth() != width || bitmap->GetHeight() != height) - bitmap->Create(width, height); - } -#ifndef BROKEN_ALPHA - bitmap->UseAlpha(); -#endif - return bitmap; -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp) -{ - wxBitmap *bitmap = nullptr; - auto it = m_map.find(bitmap_key); - if (it == m_map.end()) { - bitmap = new wxBitmap(bmp); - m_map[bitmap_key] = bitmap; - } else { - bitmap = it->second; - *bitmap = bmp; - } - return bitmap; -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[2] = { bmp, bmp2 }; - return this->insert(bitmap_key, bmps, bmps + 2); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3) -{ - // Copying the wxBitmaps is cheap as the bitmap's content is reference counted. - const wxBitmap bmps[3] = { bmp, bmp2, bmp3 }; - return this->insert(bitmap_key, bmps, bmps + 3); -} - -wxBitmap* BitmapCache::insert(const std::string &bitmap_key, const wxBitmap *begin, const wxBitmap *end) -{ - size_t width = 0; - size_t height = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - width += bmp->GetWidth(); - height = std::max<size_t>(height, bmp->GetHeight()); - } - -#ifdef BROKEN_ALPHA - - wxImage image(width, height); - image.InitAlpha(); - // Fill in with a white color. - memset(image.GetData(), 0x0ff, width * height * 3); - // Fill in with full transparency. - memset(image.GetAlpha(), 0, width * height); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) { - if (bmp->GetDepth() == 32) { - wxAlphaPixelData data(*const_cast<wxBitmap*>(bmp)); - data.UseAlpha(); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxAlphaPixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = src.Alpha(); - } - } - } - } else if (bmp->GetDepth() == 24) { - wxNativePixelData data(*const_cast<wxBitmap*>(bmp)); - if (data) { - for (int r = 0; r < bmp->GetHeight(); ++ r) { - wxNativePixelData::Iterator src(data); - src.Offset(data, 0, r); - unsigned char *dst_pixels = image.GetData() + (x + r * width) * 3; - unsigned char *dst_alpha = image.GetAlpha() + x + r * width; - for (int c = 0; c < bmp->GetWidth(); ++ c, ++ src) { - *dst_pixels ++ = src.Red(); - *dst_pixels ++ = src.Green(); - *dst_pixels ++ = src.Blue(); - *dst_alpha ++ = wxALPHA_OPAQUE; - } - } - } - } - } - x += bmp->GetWidth(); - } - return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image))); - -#else - - wxBitmap *bitmap = this->insert(bitmap_key, width, height); - wxMemoryDC memDC; - memDC.SelectObject(*bitmap); - memDC.SetBackground(*wxTRANSPARENT_BRUSH); - memDC.Clear(); - size_t x = 0; - for (const wxBitmap *bmp = begin; bmp != end; ++ bmp) { - if (bmp->GetWidth() > 0) - memDC.DrawBitmap(*bmp, x, 0, true); - x += bmp->GetWidth(); - } - memDC.SelectObject(wxNullBitmap); - return bitmap; - -#endif -} - -wxBitmap BitmapCache::mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency) -{ - wxImage image(width, height); - image.InitAlpha(); - unsigned char* imgdata = image.GetData(); - unsigned char* imgalpha = image.GetAlpha(); - for (size_t i = 0; i < width * height; ++ i) { - *imgdata ++ = r; - *imgdata ++ = g; - *imgdata ++ = b; - *imgalpha ++ = transparency; - } - return wxImage_to_wxBitmap_with_alpha(std::move(image)); -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/BitmapCache.hpp b/xs/src/slic3r/GUI/BitmapCache.hpp deleted file mode 100644 index bec9a7ad2..000000000 --- a/xs/src/slic3r/GUI/BitmapCache.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef SLIC3R_GUI_BITMAP_CACHE_HPP -#define SLIC3R_GUI_BITMAP_CACHE_HPP - -#include <wx/wxprec.h> -#ifndef WX_PRECOMP - #include <wx/wx.h> -#endif - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Config.hpp" - -#include "GUI.hpp" - -namespace Slic3r { namespace GUI { - -class BitmapCache -{ -public: - BitmapCache() {} - ~BitmapCache() { clear(); } - void clear(); - - wxBitmap* find(const std::string &name) { auto it = m_map.find(name); return (it == m_map.end()) ? nullptr : it->second; } - const wxBitmap* find(const std::string &name) const { return const_cast<BitmapCache*>(this)->find(name); } - - wxBitmap* insert(const std::string &name, size_t width, size_t height); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2); - wxBitmap* insert(const std::string &name, const wxBitmap &bmp, const wxBitmap &bmp2, const wxBitmap &bmp3); - wxBitmap* insert(const std::string &name, const std::vector<wxBitmap> &bmps) { return this->insert(name, &bmps.front(), &bmps.front() + bmps.size()); } - wxBitmap* insert(const std::string &name, const wxBitmap *begin, const wxBitmap *end); - - 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); } - static wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } - -private: - std::map<std::string, wxBitmap*> m_map; -}; - -} // GUI -} // Slic3r - -#endif /* SLIC3R_GUI_BITMAP_CACHE_HPP */ diff --git a/xs/src/slic3r/GUI/BonjourDialog.cpp b/xs/src/slic3r/GUI/BonjourDialog.cpp deleted file mode 100644 index 11cfea642..000000000 --- a/xs/src/slic3r/GUI/BonjourDialog.cpp +++ /dev/null @@ -1,200 +0,0 @@ -#include "slic3r/Utils/Bonjour.hpp" // On Windows, boost needs to be included before wxWidgets headers - -#include "BonjourDialog.hpp" - -#include <set> -#include <mutex> - -#include <wx/sizer.h> -#include <wx/button.h> -#include <wx/listctrl.h> -#include <wx/stattext.h> -#include <wx/timer.h> - -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/Utils/Bonjour.hpp" - - -namespace Slic3r { - - -struct BonjourReplyEvent : public wxEvent -{ - BonjourReply reply; - - BonjourReplyEvent(wxEventType eventType, int winid, BonjourReply &&reply) : - wxEvent(winid, eventType), - reply(std::move(reply)) - {} - - virtual wxEvent *Clone() const - { - return new BonjourReplyEvent(*this); - } -}; - -wxDEFINE_EVENT(EVT_BONJOUR_REPLY, BonjourReplyEvent); - -wxDECLARE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent); -wxDEFINE_EVENT(EVT_BONJOUR_COMPLETE, wxCommandEvent); - -class ReplySet: public std::set<BonjourReply> {}; - -struct LifetimeGuard -{ - std::mutex mutex; - BonjourDialog *dialog; - - LifetimeGuard(BonjourDialog *dialog) : dialog(dialog) {} -}; - - -BonjourDialog::BonjourDialog(wxWindow *parent) : - wxDialog(parent, wxID_ANY, _(L("Network lookup"))), - list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxSize(800, 300))), - replies(new ReplySet), - label(new wxStaticText(this, wxID_ANY, "")), - timer(new wxTimer()), - timer_state(0) -{ - wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); - - vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); - - list->SetSingleStyle(wxLC_SINGLE_SEL); - list->SetSingleStyle(wxLC_SORT_DESCENDING); - list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 50); - list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 100); - list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 200); - list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 50); - - vsizer->Add(list, 1, wxEXPAND | wxALL, 10); - - wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL); - button_sizer->Add(new wxButton(this, wxID_OK, "OK"), 0, wxALL, 10); - button_sizer->Add(new wxButton(this, wxID_CANCEL, "Cancel"), 0, wxALL, 10); - // ^ Note: The Ok/Cancel labels are translated by wxWidgets - - vsizer->Add(button_sizer, 0, wxALIGN_CENTER); - SetSizerAndFit(vsizer); - - Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this); - - Bind(EVT_BONJOUR_COMPLETE, [this](wxCommandEvent &) { - this->timer_state = 0; - }); - - Bind(wxEVT_TIMER, &BonjourDialog::on_timer, this); -} - -BonjourDialog::~BonjourDialog() -{ - // Needed bacuse of forward defs -} - -bool BonjourDialog::show_and_lookup() -{ - Show(); // Because we need GetId() to work before ShowModal() - - timer->Stop(); - timer->SetOwner(this); - timer_state = 1; - timer->Start(1000); - wxTimerEvent evt_dummy; - on_timer(evt_dummy); - - // The background thread needs to queue messages for this dialog - // and for that it needs a valid pointer to it (mandated by the wxWidgets API). - // Here we put the pointer under a shared_ptr and protect it by a mutex, - // so that both threads can access it safely. - auto dguard = std::make_shared<LifetimeGuard>(this); - - bonjour = std::move(Bonjour("octoprint") - .set_retries(3) - .set_timeout(4) - .on_reply([dguard](BonjourReply &&reply) { - std::lock_guard<std::mutex> lock_guard(dguard->mutex); - auto dialog = dguard->dialog; - if (dialog != nullptr) { - auto evt = new BonjourReplyEvent(EVT_BONJOUR_REPLY, dialog->GetId(), std::move(reply)); - wxQueueEvent(dialog, evt); - } - }) - .on_complete([dguard]() { - std::lock_guard<std::mutex> lock_guard(dguard->mutex); - auto dialog = dguard->dialog; - if (dialog != nullptr) { - auto evt = new wxCommandEvent(EVT_BONJOUR_COMPLETE, dialog->GetId()); - wxQueueEvent(dialog, evt); - } - }) - .lookup() - ); - - bool res = ShowModal() == wxID_OK && list->GetFirstSelected() >= 0; - { - // Tell the background thread the dialog is going away... - std::lock_guard<std::mutex> lock_guard(dguard->mutex); - dguard->dialog = nullptr; - } - return res; -} - -wxString BonjourDialog::get_selected() const -{ - auto sel = list->GetFirstSelected(); - return sel >= 0 ? list->GetItemText(sel) : wxString(); -} - - -// Private - -void BonjourDialog::on_reply(BonjourReplyEvent &e) -{ - if (replies->find(e.reply) != replies->end()) { - // We already have this reply - return; - } - - replies->insert(std::move(e.reply)); - - auto selected = get_selected(); - list->DeleteAllItems(); - - // The whole list is recreated so that we benefit from it already being sorted in the set. - // (And also because wxListView's sorting API is bananas.) - for (const auto &reply : *replies) { - auto item = list->InsertItem(0, reply.full_address); - list->SetItem(item, 1, reply.hostname); - list->SetItem(item, 2, reply.service_name); - list->SetItem(item, 3, reply.version); - } - - for (int i = 0; i < 4; i++) { - this->list->SetColumnWidth(i, wxLIST_AUTOSIZE); - if (this->list->GetColumnWidth(i) < 100) { this->list->SetColumnWidth(i, 100); } - } - - if (!selected.IsEmpty()) { - // Attempt to preserve selection - auto hit = list->FindItem(-1, selected); - if (hit >= 0) { list->SetItemState(hit, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED); } - } -} - -void BonjourDialog::on_timer(wxTimerEvent &) -{ - const auto search_str = _(L("Searching for devices")); - - if (timer_state > 0) { - const std::string dots(timer_state, '.'); - label->SetLabel(wxString::Format("%s %s", search_str, dots)); - timer_state = (timer_state) % 3 + 1; - } else { - label->SetLabel(wxString::Format("%s: %s", search_str, _(L("Finished"))+".")); - timer->Stop(); - } -} - - -} diff --git a/xs/src/slic3r/GUI/BonjourDialog.hpp b/xs/src/slic3r/GUI/BonjourDialog.hpp deleted file mode 100644 index e3f53790b..000000000 --- a/xs/src/slic3r/GUI/BonjourDialog.hpp +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef slic3r_BonjourDialog_hpp_ -#define slic3r_BonjourDialog_hpp_ - -#include <memory> - -#include <wx/dialog.h> - -class wxListView; -class wxStaticText; -class wxTimer; -class wxTimerEvent; - - -namespace Slic3r { - -class Bonjour; -class BonjourReplyEvent; -class ReplySet; - - -class BonjourDialog: public wxDialog -{ -public: - BonjourDialog(wxWindow *parent); - BonjourDialog(BonjourDialog &&) = delete; - BonjourDialog(const BonjourDialog &) = delete; - BonjourDialog &operator=(BonjourDialog &&) = delete; - BonjourDialog &operator=(const BonjourDialog &) = delete; - ~BonjourDialog(); - - bool show_and_lookup(); - wxString get_selected() const; -private: - wxListView *list; - std::unique_ptr<ReplySet> replies; - wxStaticText *label; - std::shared_ptr<Bonjour> bonjour; - std::unique_ptr<wxTimer> timer; - unsigned timer_state; - - void on_reply(BonjourReplyEvent &); - void on_timer(wxTimerEvent &); -}; - - - -} - -#endif diff --git a/xs/src/slic3r/GUI/ButtonsDescription.cpp b/xs/src/slic3r/GUI/ButtonsDescription.cpp deleted file mode 100644 index 5739fc90e..000000000 --- a/xs/src/slic3r/GUI/ButtonsDescription.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include "ButtonsDescription.hpp" -#include <wx/sizer.h> -#include <wx/stattext.h> -#include <wx/statbmp.h> -#include <wx/clrpicker.h> - -#include "GUI.hpp" - -namespace Slic3r { -namespace GUI { - -ButtonsDescription::ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions) : - wxDialog(parent, wxID_ANY, _(L("Buttons And Text Colors Description")), wxDefaultPosition, wxDefaultSize), - m_icon_descriptions(icon_descriptions) -{ - auto grid_sizer = new wxFlexGridSizer(3, 20, 20); - - auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(grid_sizer, 0, wxEXPAND | wxALL, 20); - - // Icon description - for (auto pair : *m_icon_descriptions) - { - auto icon = new wxStaticBitmap(this, wxID_ANY, *pair.first); - grid_sizer->Add(icon, -1, wxALIGN_CENTRE_VERTICAL); - - std::istringstream f(pair.second); - std::string s; - getline(f, s, ';'); - auto description = new wxStaticText(this, wxID_ANY, _(s)); - grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL); - getline(f, s, ';'); - description = new wxStaticText(this, wxID_ANY, _(s)); - grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - } - - // Text color description - auto sys_label = new wxStaticText(this, wxID_ANY, _(L("Value is the same as the system value"))); - sys_label->SetForegroundColour(get_label_clr_sys()); - auto sys_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_sys()); - sys_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([sys_colour, sys_label](wxCommandEvent e) - { - sys_label->SetForegroundColour(sys_colour->GetColour()); - sys_label->Refresh(); - })); - size_t t= 0; - while (t < 3){ - grid_sizer->Add(new wxStaticText(this, wxID_ANY, ""), -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - ++t; - } - grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(sys_colour, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(sys_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - - auto mod_label = new wxStaticText(this, wxID_ANY, _(L("Value was changed and is not equal to the system value or the last saved preset"))); - mod_label->SetForegroundColour(get_label_clr_modified()); - auto mod_colour = new wxColourPickerCtrl(this, wxID_ANY, get_label_clr_modified()); - mod_colour->Bind(wxEVT_COLOURPICKER_CHANGED, ([mod_colour, mod_label](wxCommandEvent e) - { - mod_label->SetForegroundColour(mod_colour->GetColour()); - mod_label->Refresh(); - })); - grid_sizer->Add(0, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(mod_colour, -1, wxALIGN_CENTRE_VERTICAL); - grid_sizer->Add(mod_label, -1, wxALIGN_CENTRE_VERTICAL | wxEXPAND); - - - auto buttons = CreateStdDialogButtonSizer(wxOK|wxCANCEL); - main_sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); - - wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this)); - btn->Bind(wxEVT_BUTTON, [sys_colour, mod_colour, this](wxCommandEvent&) { - set_label_clr_sys(sys_colour->GetColour()); - set_label_clr_modified(mod_colour->GetColour()); - EndModal(wxID_OK); - }); - - SetSizer(main_sizer); - main_sizer->SetSizeHints(this); -} - -} // GUI -} // Slic3r - diff --git a/xs/src/slic3r/GUI/ButtonsDescription.hpp b/xs/src/slic3r/GUI/ButtonsDescription.hpp deleted file mode 100644 index 4858eaaea..000000000 --- a/xs/src/slic3r/GUI/ButtonsDescription.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef slic3r_ButtonsDescription_hpp -#define slic3r_ButtonsDescription_hpp - -#include <wx/dialog.h> -#include <vector> - -namespace Slic3r { -namespace GUI { - -using t_icon_descriptions = std::vector<std::pair<wxBitmap*, std::string>>; - -class ButtonsDescription : public wxDialog -{ - t_icon_descriptions* m_icon_descriptions; -public: - ButtonsDescription(wxWindow* parent, t_icon_descriptions* icon_descriptions); - ~ButtonsDescription(){} - - -}; - -} // GUI -} // Slic3r - - -#endif - diff --git a/xs/src/slic3r/GUI/ConfigExceptions.hpp b/xs/src/slic3r/GUI/ConfigExceptions.hpp deleted file mode 100644 index 9038d3445..000000000 --- a/xs/src/slic3r/GUI/ConfigExceptions.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#include <exception> -namespace Slic3r { - -class ConfigError : public std::runtime_error { -using std::runtime_error::runtime_error; -}; - -namespace GUI { - -class ConfigGUITypeError : public ConfigError { -using ConfigError::ConfigError; -}; -} - -} diff --git a/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp b/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp deleted file mode 100644 index efcbf05bd..000000000 --- a/xs/src/slic3r/GUI/ConfigSnapshotDialog.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "ConfigSnapshotDialog.hpp" - -#include "../Config/Snapshot.hpp" -#include "../Utils/Time.hpp" - -#include "../../libslic3r/Utils.hpp" - -namespace Slic3r { -namespace GUI { - -static wxString format_reason(const Config::Snapshot::Reason reason) -{ - switch (reason) { - case Config::Snapshot::SNAPSHOT_UPGRADE: - return wxString(_(L("Upgrade"))); - case Config::Snapshot::SNAPSHOT_DOWNGRADE: - return wxString(_(L("Downgrade"))); - case Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK: - return wxString(_(L("Before roll back"))); - case Config::Snapshot::SNAPSHOT_USER: - return wxString(_(L("User"))); - case Config::Snapshot::SNAPSHOT_UNKNOWN: - default: - return wxString(_(L("Unknown"))); - } -} - -static wxString generate_html_row(const Config::Snapshot &snapshot, bool row_even, bool snapshot_active) -{ - // Start by declaring a row with an alternating background color. - wxString text = "<tr bgcolor=\""; - text += snapshot_active ? "#B3FFCB" : (row_even ? "#FFFFFF" : "#D5D5D5"); - text += "\">"; - text += "<td>"; - // Format the row header. - text += wxString("<font size=\"5\"><b>") + (snapshot_active ? _(L("Active: ")) : "") + - Utils::format_local_date_time(snapshot.time_captured) + ": " + format_reason(snapshot.reason); - if (! snapshot.comment.empty()) - text += " (" + snapshot.comment + ")"; - text += "</b></font><br>"; - // End of row header. - text += _(L("slic3r version")) + ": " + snapshot.slic3r_version_captured.to_string() + "<br>"; - text += _(L("print")) + ": " + snapshot.print + "<br>"; - text += _(L("filaments")) + ": " + snapshot.filaments.front() + "<br>"; - text += _(L("printer")) + ": " + snapshot.printer + "<br>"; - - bool compatible = true; - for (const Config::Snapshot::VendorConfig &vc : snapshot.vendor_configs) { - text += _(L("vendor")) + ": " + vc.name +", " + _(L("version")) + ": " + vc.version.config_version.to_string() + - ", " + _(L("min slic3r version")) + ": " + vc.version.min_slic3r_version.to_string(); - if (vc.version.max_slic3r_version != Semver::inf()) - text += ", " + _(L("max slic3r version")) + ": " + vc.version.max_slic3r_version.to_string(); - text += "<br>"; - for (const std::pair<std::string, std::set<std::string>> &model : vc.models_variants_installed) { - text += _(L("model")) + ": " + model.first + ", " + _(L("variants")) + ": "; - for (const std::string &variant : model.second) { - if (&variant != &*model.second.begin()) - text += ", "; - text += variant; - } - text += "<br>"; - } - if (! vc.version.is_current_slic3r_supported()) { compatible = false; } - } - - if (! compatible) { - text += "<p align=\"right\">" + _(L("Incompatible with this Slic3r")) + "</p>"; - } - else if (! snapshot_active) - text += "<p align=\"right\"><a href=\"" + snapshot.id + "\">" + _(L("Activate")) + "</a></p>"; - text += "</td>"; - text += "</tr>"; - return text; -} - -static wxString generate_html_page(const Config::SnapshotDB &snapshot_db, const wxString &on_snapshot) -{ - wxString text = - "<html>" - "<body bgcolor=\"#ffffff\" cellspacing=\"2\" cellpadding=\"0\" border=\"0\" link=\"#800000\">" - "<font color=\"#000000\">"; - text += "<table style=\"width:100%\">"; - for (size_t i_row = 0; i_row < snapshot_db.snapshots().size(); ++ i_row) { - const Config::Snapshot &snapshot = snapshot_db.snapshots()[snapshot_db.snapshots().size() - i_row - 1]; - text += generate_html_row(snapshot, i_row & 1, snapshot.id == on_snapshot); - } - text += - "</table>" - "</font>" - "</body>" - "</html>"; - return text; -} - -ConfigSnapshotDialog::ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const wxString &on_snapshot) - : wxDialog(NULL, wxID_ANY, _(L("Configuration Snapshots")), wxDefaultPosition, wxSize(600, 500), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX) -{ - this->SetBackgroundColour(*wxWHITE); - - wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL); - this->SetSizer(vsizer); - - // text - wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO); - { - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - #ifdef __WXMSW__ - int size[] = {8,8,8,8,11,11,11}; - #else - int size[] = {11,11,11,11,14,14,14}; - #endif - html->SetFonts(font.GetFaceName(), font.GetFaceName(), size); - html->SetBorders(2); - wxString text = generate_html_page(snapshot_db, on_snapshot); - html->SetPage(text); - vsizer->Add(html, 1, wxEXPAND | wxALIGN_LEFT | wxRIGHT | wxBOTTOM, 0); - html->Bind(wxEVT_HTML_LINK_CLICKED, &ConfigSnapshotDialog::onLinkClicked, this); - } - - wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxCLOSE); - this->SetEscapeId(wxID_CLOSE); - this->Bind(wxEVT_BUTTON, &ConfigSnapshotDialog::onCloseDialog, this, wxID_CLOSE); - vsizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); -} - -void ConfigSnapshotDialog::onLinkClicked(wxHtmlLinkEvent &event) -{ - m_snapshot_to_activate = event.GetLinkInfo().GetHref(); - this->EndModal(wxID_CLOSE); - this->Close(); -} - -void ConfigSnapshotDialog::onCloseDialog(wxEvent &) -{ - this->EndModal(wxID_CLOSE); - this->Close(); -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp b/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp deleted file mode 100644 index f43fb8ed1..000000000 --- a/xs/src/slic3r/GUI/ConfigSnapshotDialog.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef slic3r_GUI_ConfigSnapshotDialog_hpp_ -#define slic3r_GUI_ConfigSnapshotDialog_hpp_ - -#include "GUI.hpp" - -#include <wx/wx.h> -#include <wx/intl.h> -#include <wx/html/htmlwin.h> - -namespace Slic3r { -namespace GUI { - -namespace Config { - class SnapshotDB; -} - -class ConfigSnapshotDialog : public wxDialog -{ -public: - ConfigSnapshotDialog(const Config::SnapshotDB &snapshot_db, const wxString &id); - const std::string& snapshot_to_activate() const { return m_snapshot_to_activate; } - -private: - void onLinkClicked(wxHtmlLinkEvent &event); - void onCloseDialog(wxEvent &); - - // If set, it contains a snapshot ID to be restored after the dialog closes. - std::string m_snapshot_to_activate; -}; - -} // namespace GUI -} // namespace Slic3r - -#endif /* slic3r_GUI_ConfigSnapshotDialog_hpp_ */ diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp deleted file mode 100644 index e784d8525..000000000 --- a/xs/src/slic3r/GUI/ConfigWizard.cpp +++ /dev/null @@ -1,915 +0,0 @@ -#include "ConfigWizard_private.hpp" - -#include <algorithm> -#include <utility> -#include <unordered_map> -#include <boost/format.hpp> -#include <boost/log/trivial.hpp> - -#include <wx/settings.h> -#include <wx/stattext.h> -#include <wx/textctrl.h> -#include <wx/dcclient.h> -#include <wx/statbmp.h> -#include <wx/checkbox.h> -#include <wx/statline.h> - -#include "libslic3r/Utils.hpp" -#include "PresetBundle.hpp" -#include "GUI.hpp" -#include "slic3r/Utils/PresetUpdater.hpp" - - -namespace Slic3r { -namespace GUI { - - -// Printer model picker GUI control - -struct PrinterPickerEvent : public wxEvent -{ - std::string vendor_id; - std::string model_id; - std::string variant_name; - bool enable; - - PrinterPickerEvent(wxEventType eventType, int winid, std::string vendor_id, std::string model_id, std::string variant_name, bool enable) : - wxEvent(winid, eventType), - vendor_id(std::move(vendor_id)), - model_id(std::move(model_id)), - variant_name(std::move(variant_name)), - enable(enable) - {} - - virtual wxEvent *Clone() const - { - return new PrinterPickerEvent(*this); - } -}; - -wxDEFINE_EVENT(EVT_PRINTER_PICK, PrinterPickerEvent); - -PrinterPicker::PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors) : - wxPanel(parent), - vendor_id(vendor.id), - variants_checked(0) -{ - const auto &models = vendor.models; - - auto *sizer = new wxBoxSizer(wxVERTICAL); - - auto *printer_grid = new wxFlexGridSizer(models.size(), 0, 20); - printer_grid->SetFlexibleDirection(wxVERTICAL | wxHORIZONTAL); - sizer->Add(printer_grid); - - auto namefont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - namefont.SetWeight(wxFONTWEIGHT_BOLD); - - // wxGrid appends widgets by rows, but we need to construct them in columns. - // These vectors are used to hold the elements so that they can be appended in the right order. - std::vector<wxStaticText*> titles; - std::vector<wxStaticBitmap*> bitmaps; - std::vector<wxPanel*> variants_panels; - - for (const auto &model : models) { - auto bitmap_file = wxString::Format("printers/%s_%s.png", vendor.id, model.id); - wxBitmap bitmap(GUI::from_u8(Slic3r::var(bitmap_file.ToStdString())), wxBITMAP_TYPE_PNG); - - auto *title = new wxStaticText(this, wxID_ANY, model.name, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - title->SetFont(namefont); - title->Wrap(std::max((int)MODEL_MIN_WRAP, bitmap.GetWidth())); - titles.push_back(title); - - auto *bitmap_widget = new wxStaticBitmap(this, wxID_ANY, bitmap); - bitmaps.push_back(bitmap_widget); - - auto *variants_panel = new wxPanel(this); - auto *variants_sizer = new wxBoxSizer(wxVERTICAL); - variants_panel->SetSizer(variants_sizer); - const auto model_id = model.id; - - bool default_variant = true; // Mark the first variant as default in the GUI - for (const auto &variant : model.variants) { - const auto label = wxString::Format("%s %s %s %s", variant.name, _(L("mm")), _(L("nozzle")), - (default_variant ? _(L("(default)")) : wxString())); - default_variant = false; - auto *cbox = new Checkbox(variants_panel, label, model_id, variant.name); - const size_t idx = cboxes.size(); - cboxes.push_back(cbox); - bool enabled = appconfig_vendors.get_variant("PrusaResearch", model_id, variant.name); - variants_checked += enabled; - cbox->SetValue(enabled); - variants_sizer->Add(cbox, 0, wxBOTTOM, 3); - cbox->Bind(wxEVT_CHECKBOX, [this, idx](wxCommandEvent &event) { - if (idx >= this->cboxes.size()) { return; } - this->on_checkbox(this->cboxes[idx], event.IsChecked()); - }); - } - - variants_panels.push_back(variants_panel); - } - - for (auto title : titles) { printer_grid->Add(title, 0, wxBOTTOM, 3); } - for (auto bitmap : bitmaps) { printer_grid->Add(bitmap, 0, wxBOTTOM, 20); } - for (auto vp : variants_panels) { printer_grid->Add(vp); } - - auto *all_none_sizer = new wxBoxSizer(wxHORIZONTAL); - auto *sel_all = new wxButton(this, wxID_ANY, _(L("Select all"))); - auto *sel_none = new wxButton(this, wxID_ANY, _(L("Select none"))); - sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(true); }); - sel_none->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->select_all(false); }); - all_none_sizer->AddStretchSpacer(); - all_none_sizer->Add(sel_all); - all_none_sizer->Add(sel_none); - sizer->AddStretchSpacer(); - sizer->Add(all_none_sizer, 0, wxEXPAND); - - SetSizer(sizer); -} - -void PrinterPicker::select_all(bool select) -{ - for (const auto &cb : cboxes) { - if (cb->GetValue() != select) { - cb->SetValue(select); - on_checkbox(cb, select); - } - } -} - -void PrinterPicker::select_one(size_t i, bool select) -{ - if (i < cboxes.size() && cboxes[i]->GetValue() != select) { - cboxes[i]->SetValue(select); - on_checkbox(cboxes[i], select); - } -} - -void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) -{ - variants_checked += checked ? 1 : -1; - PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); - AddPendingEvent(evt); -} - - -// Wizard page base - -ConfigWizardPage::ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname) : - wxPanel(parent->p->hscroll), - parent(parent), - shortname(std::move(shortname)), - p_prev(nullptr), - p_next(nullptr) -{ - auto *sizer = new wxBoxSizer(wxVERTICAL); - - auto *text = new wxStaticText(this, wxID_ANY, std::move(title), wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - auto font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - font.SetWeight(wxFONTWEIGHT_BOLD); - font.SetPointSize(14); - text->SetFont(font); - sizer->Add(text, 0, wxALIGN_LEFT, 0); - sizer->AddSpacer(10); - - content = new wxBoxSizer(wxVERTICAL); - sizer->Add(content, 1); - - SetSizer(sizer); - - this->Hide(); - - Bind(wxEVT_SIZE, [this](wxSizeEvent &event) { - this->Layout(); - event.Skip(); - }); -} - -ConfigWizardPage::~ConfigWizardPage() {} - -ConfigWizardPage* ConfigWizardPage::chain(ConfigWizardPage *page) -{ - if (p_next != nullptr) { p_next->p_prev = nullptr; } - p_next = page; - if (page != nullptr) { - if (page->p_prev != nullptr) { page->p_prev->p_next = nullptr; } - page->p_prev = this; - } - - return page; -} - -void ConfigWizardPage::append_text(wxString text) -{ - auto *widget = new wxStaticText(this, wxID_ANY, text, wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT); - widget->Wrap(WRAP_WIDTH); - widget->SetMinSize(wxSize(WRAP_WIDTH, -1)); - append(widget); -} - -void ConfigWizardPage::append_spacer(int space) -{ - content->AddSpacer(space); -} - -bool ConfigWizardPage::Show(bool show) -{ - if (extra_buttons() != nullptr) { extra_buttons()->Show(show); } - return wxPanel::Show(show); -} - -void ConfigWizardPage::enable_next(bool enable) { parent->p->enable_next(enable); } - - -// Wizard pages - -PageWelcome::PageWelcome(ConfigWizard *parent, bool check_first_variant) : - ConfigWizardPage(parent, wxString::Format(_(L("Welcome to the Slic3r %s")), ConfigWizard::name()), _(L("Welcome"))), - printer_picker(nullptr), - others_buttons(new wxPanel(parent)), - cbox_reset(nullptr) -{ - if (wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY) { - wxString::Format(_(L("Run %s")), ConfigWizard::name()); - append_text(wxString::Format( - _(L("Hello, welcome to Slic3r Prusa Edition! This %s helps you with the initial configuration; just a few settings and you will be ready to print.")), - ConfigWizard::name()) - ); - } else { - cbox_reset = new wxCheckBox(this, wxID_ANY, _(L("Remove user profiles - install from scratch (a snapshot will be taken beforehand)"))); - append(cbox_reset); - } - - const auto &vendors = wizard_p()->vendors; - const auto vendor_prusa = vendors.find("PrusaResearch"); - - if (vendor_prusa != vendors.cend()) { - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - - printer_picker = new PrinterPicker(this, vendor_prusa->second, appconfig_vendors); - if (check_first_variant) { - // Select the default (first) model/variant on the Prusa vendor - printer_picker->select_one(0, true); - } - printer_picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - this->on_variant_checked(); - }); - - append(printer_picker); - } - - const size_t num_other_vendors = vendors.size() - (vendor_prusa != vendors.cend()); - auto *sizer = new wxBoxSizer(wxHORIZONTAL); - auto *other_vendors = new wxButton(others_buttons, wxID_ANY, _(L("Other vendors"))); - other_vendors->Enable(num_other_vendors > 0); - auto *custom_setup = new wxButton(others_buttons, wxID_ANY, _(L("Custom setup"))); - - sizer->Add(other_vendors); - sizer->AddSpacer(BTN_SPACING); - sizer->Add(custom_setup); - - other_vendors->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_other_vendors(); }); - custom_setup->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &event) { this->wizard_p()->on_custom_setup(); }); - - others_buttons->SetSizer(sizer); -} - -void PageWelcome::on_page_set() -{ - chain(wizard_p()->page_update); - on_variant_checked(); -} - -void PageWelcome::on_variant_checked() -{ - enable_next(printer_picker != nullptr ? printer_picker->variants_checked > 0 : false); -} - -PageUpdate::PageUpdate(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Automatic updates")), _(L("Updates"))), - version_check(true), - preset_update(true) -{ - const AppConfig *app_config = GUI::get_app_config(); - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); - - auto *box_slic3r = new wxCheckBox(this, wxID_ANY, _(L("Check for application updates"))); - box_slic3r->SetValue(app_config->get("version_check") == "1"); - append(box_slic3r); - append_text(_(L("If enabled, Slic3r checks for new versions of Slic3r PE 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."))); - - append_spacer(VERTICAL_SPACING); - - auto *box_presets = new wxCheckBox(this, wxID_ANY, _(L("Update built-in Presets automatically"))); - box_presets->SetValue(app_config->get("preset_update") == "1"); - append(box_presets); - append_text(_(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."))); - const auto text_bold = _(L("Updates are never applied without user's consent and never overwrite user's customized settings.")); - auto *label_bold = new wxStaticText(this, wxID_ANY, text_bold); - label_bold->SetFont(boldfont); - label_bold->Wrap(WRAP_WIDTH); - append(label_bold); - append_text(_(L("Additionally a backup snapshot of the whole configuration is created before an update is applied."))); - - box_slic3r->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->version_check = event.IsChecked(); }); - box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); -} - -PageVendors::PageVendors(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Other Vendors")), _(L("Other Vendors"))) -{ - append_text(_(L("Pick another vendor supported by Slic3r PE:"))); - - auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - boldfont.SetWeight(wxFONTWEIGHT_BOLD); - - AppConfig &appconfig_vendors = this->wizard_p()->appconfig_vendors; - wxArrayString choices_vendors; - - for (const auto vendor_pair : wizard_p()->vendors) { - const auto &vendor = vendor_pair.second; - if (vendor.id == "PrusaResearch") { continue; } - - auto *picker = new PrinterPicker(this, vendor, appconfig_vendors); - picker->Hide(); - pickers.push_back(picker); - choices_vendors.Add(vendor.name); - - picker->Bind(EVT_PRINTER_PICK, [this, &appconfig_vendors](const PrinterPickerEvent &evt) { - appconfig_vendors.set_variant(evt.vendor_id, evt.model_id, evt.variant_name, evt.enable); - this->on_variant_checked(); - }); - } - - auto *vendor_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices_vendors); - if (choices_vendors.GetCount() > 0) { - vendor_picker->SetSelection(0); - on_vendor_pick(0); - } - - vendor_picker->Bind(wxEVT_CHOICE, [this](wxCommandEvent &evt) { - this->on_vendor_pick(evt.GetInt()); - }); - - append(vendor_picker); - for (PrinterPicker *picker : pickers) { this->append(picker); } -} - -void PageVendors::on_page_set() -{ - on_variant_checked(); -} - -void PageVendors::on_vendor_pick(size_t i) -{ - for (PrinterPicker *picker : pickers) { picker->Hide(); } - if (i < pickers.size()) { - pickers[i]->Show(); - wizard_p()->layout_fit(); - } -} - -void PageVendors::on_variant_checked() -{ - size_t variants_checked = 0; - for (const PrinterPicker *picker : pickers) { variants_checked += picker->variants_checked; } - enable_next(variants_checked > 0); -} - -PageFirmware::PageFirmware(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Firmware Type")), _(L("Firmware"))), - gcode_opt(print_config_def.options["gcode_flavor"]), - gcode_picker(nullptr) -{ - append_text(_(L("Choose the type of firmware used by your printer."))); - append_text(gcode_opt.tooltip); - - wxArrayString choices; - choices.Alloc(gcode_opt.enum_labels.size()); - for (const auto &label : gcode_opt.enum_labels) { - choices.Add(label); - } - - gcode_picker = new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices); - const auto &enum_values = gcode_opt.enum_values; - auto needle = enum_values.cend(); - if (gcode_opt.default_value != nullptr) { - needle = std::find(enum_values.cbegin(), enum_values.cend(), gcode_opt.default_value->serialize()); - } - if (needle != enum_values.cend()) { - gcode_picker->SetSelection(needle - enum_values.cbegin()); - } else { - gcode_picker->SetSelection(0); - } - - append(gcode_picker); -} - -void PageFirmware::apply_custom_config(DynamicPrintConfig &config) -{ - auto sel = gcode_picker->GetSelection(); - if (sel >= 0 && sel < gcode_opt.enum_labels.size()) { - auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel)); - config.set_key_value("gcode_flavor", opt); - } -} - -PageBedShape::PageBedShape(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Bed Shape and Size")), _(L("Bed Shape"))), - shape_panel(new BedShapePanel(this)) -{ - append_text(_(L("Set the shape of your printer's bed."))); - - shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape")); - append(shape_panel); -} - -void PageBedShape::apply_custom_config(DynamicPrintConfig &config) -{ - const auto points(shape_panel->GetValue()); - auto *opt = new ConfigOptionPoints(points); - config.set_key_value("bed_shape", opt); -} - -PageDiameters::PageDiameters(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Filament and Nozzle Diameters")), _(L("Print Diameters"))), - spin_nozzle(new wxSpinCtrlDouble(this, wxID_ANY)), - spin_filam(new wxSpinCtrlDouble(this, wxID_ANY)) -{ - spin_nozzle->SetDigits(2); - spin_nozzle->SetIncrement(0.1); - const auto &def_nozzle = print_config_def.options["nozzle_diameter"]; - auto *default_nozzle = dynamic_cast<const ConfigOptionFloats*>(def_nozzle.default_value); - spin_nozzle->SetValue(default_nozzle != nullptr && default_nozzle->size() > 0 ? default_nozzle->get_at(0) : 0.5); - - spin_filam->SetDigits(2); - spin_filam->SetIncrement(0.25); - const auto &def_filam = print_config_def.options["filament_diameter"]; - auto *default_filam = dynamic_cast<const ConfigOptionFloats*>(def_filam.default_value); - spin_filam->SetValue(default_filam != nullptr && default_filam->size() > 0 ? default_filam->get_at(0) : 3.0); - - append_text(_(L("Enter the diameter of your printer's hot end nozzle."))); - - auto *sizer_nozzle = new wxFlexGridSizer(3, 5, 5); - auto *text_nozzle = new wxStaticText(this, wxID_ANY, _(L("Nozzle Diameter:"))); - auto *unit_nozzle = new wxStaticText(this, wxID_ANY, _(L("mm"))); - sizer_nozzle->AddGrowableCol(0, 1); - sizer_nozzle->Add(text_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - sizer_nozzle->Add(spin_nozzle); - sizer_nozzle->Add(unit_nozzle, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_nozzle); - - append_spacer(VERTICAL_SPACING); - - append_text(_(L("Enter the diameter of your filament."))); - append_text(_(L("Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."))); - - auto *sizer_filam = new wxFlexGridSizer(3, 5, 5); - auto *text_filam = new wxStaticText(this, wxID_ANY, _(L("Filament Diameter:"))); - auto *unit_filam = new wxStaticText(this, wxID_ANY, _(L("mm"))); - sizer_filam->AddGrowableCol(0, 1); - sizer_filam->Add(text_filam, 0, wxALIGN_CENTRE_VERTICAL); - sizer_filam->Add(spin_filam); - sizer_filam->Add(unit_filam, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_filam); -} - -void PageDiameters::apply_custom_config(DynamicPrintConfig &config) -{ - auto *opt_nozzle = new ConfigOptionFloats(1, spin_nozzle->GetValue()); - config.set_key_value("nozzle_diameter", opt_nozzle); - auto *opt_filam = new ConfigOptionFloats(1, spin_filam->GetValue()); - config.set_key_value("filament_diameter", opt_filam); -} - -PageTemperatures::PageTemperatures(ConfigWizard *parent) : - ConfigWizardPage(parent, _(L("Extruder and Bed Temperatures")), _(L("Temperatures"))), - spin_extr(new wxSpinCtrlDouble(this, wxID_ANY)), - spin_bed(new wxSpinCtrlDouble(this, wxID_ANY)) -{ - spin_extr->SetIncrement(5.0); - const auto &def_extr = print_config_def.options["temperature"]; - spin_extr->SetRange(def_extr.min, def_extr.max); - auto *default_extr = dynamic_cast<const ConfigOptionInts*>(def_extr.default_value); - spin_extr->SetValue(default_extr != nullptr && default_extr->size() > 0 ? default_extr->get_at(0) : 200); - - spin_bed->SetIncrement(5.0); - const auto &def_bed = print_config_def.options["bed_temperature"]; - spin_bed->SetRange(def_bed.min, def_bed.max); - auto *default_bed = dynamic_cast<const ConfigOptionInts*>(def_bed.default_value); - spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0); - - append_text(_(L("Enter the temperature needed for extruding your filament."))); - append_text(_(L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."))); - - auto *sizer_extr = new wxFlexGridSizer(3, 5, 5); - auto *text_extr = new wxStaticText(this, wxID_ANY, _(L("Extrusion Temperature:"))); - auto *unit_extr = new wxStaticText(this, wxID_ANY, _(L("°C"))); - sizer_extr->AddGrowableCol(0, 1); - sizer_extr->Add(text_extr, 0, wxALIGN_CENTRE_VERTICAL); - sizer_extr->Add(spin_extr); - sizer_extr->Add(unit_extr, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_extr); - - append_spacer(VERTICAL_SPACING); - - append_text(_(L("Enter the bed temperature needed for getting your filament to stick to your heated bed."))); - append_text(_(L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed."))); - - auto *sizer_bed = new wxFlexGridSizer(3, 5, 5); - auto *text_bed = new wxStaticText(this, wxID_ANY, _(L("Bed Temperature:"))); - auto *unit_bed = new wxStaticText(this, wxID_ANY, _(L("°C"))); - sizer_bed->AddGrowableCol(0, 1); - sizer_bed->Add(text_bed, 0, wxALIGN_CENTRE_VERTICAL); - sizer_bed->Add(spin_bed); - sizer_bed->Add(unit_bed, 0, wxALIGN_CENTRE_VERTICAL); - append(sizer_bed); -} - -void PageTemperatures::apply_custom_config(DynamicPrintConfig &config) -{ - auto *opt_extr = new ConfigOptionInts(1, spin_extr->GetValue()); - config.set_key_value("temperature", opt_extr); - auto *opt_extr1st = new ConfigOptionInts(1, spin_extr->GetValue()); - config.set_key_value("first_layer_temperature", opt_extr1st); - auto *opt_bed = new ConfigOptionInts(1, spin_bed->GetValue()); - config.set_key_value("bed_temperature", opt_bed); - auto *opt_bed1st = new ConfigOptionInts(1, spin_bed->GetValue()); - config.set_key_value("first_layer_bed_temperature", opt_bed1st); -} - - -// Index - -ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent) : - wxPanel(parent), - bg(GUI::from_u8(Slic3r::var("Slic3r_192px_transparent.png")), wxBITMAP_TYPE_PNG), - bullet_black(GUI::from_u8(Slic3r::var("bullet_black.png")), wxBITMAP_TYPE_PNG), - bullet_blue(GUI::from_u8(Slic3r::var("bullet_blue.png")), wxBITMAP_TYPE_PNG), - bullet_white(GUI::from_u8(Slic3r::var("bullet_white.png")), wxBITMAP_TYPE_PNG) -{ - SetMinSize(bg.GetSize()); - - wxClientDC dc(this); - text_height = dc.GetCharHeight(); - - // Add logo bitmap. - // This could be done in on_paint() along with the index labels, but I've found it tricky - // to get the bitmap rendered well on all platforms with transparent background. - // In some cases it didn't work at all. And so wxStaticBitmap is used here instead, - // because it has all the platform quirks figured out. - auto *sizer = new wxBoxSizer(wxVERTICAL); - auto *logo = new wxStaticBitmap(this, wxID_ANY, bg); - sizer->AddStretchSpacer(); - sizer->Add(logo); - SetSizer(sizer); - - Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this); -} - -void ConfigWizardIndex::load_items(ConfigWizardPage *firstpage) -{ - items.clear(); - item_active = items.cend(); - - for (auto *page = firstpage; page != nullptr; page = page->page_next()) { - items.emplace_back(page->shortname); - } - - Refresh(); -} - -void ConfigWizardIndex::set_active(ConfigWizardPage *page) -{ - item_active = std::find(items.cbegin(), items.cend(), page->shortname); - Refresh(); -} - -void ConfigWizardIndex::on_paint(wxPaintEvent & evt) -{ - enum { - MARGIN = 10, - SPACING = 5, - }; - - const auto size = GetClientSize(); - if (size.GetHeight() == 0 || size.GetWidth() == 0) { return; } - - wxPaintDC dc(this); - - const auto bullet_w = bullet_black.GetSize().GetWidth(); - const auto bullet_h = bullet_black.GetSize().GetHeight(); - const int yoff_icon = bullet_h < text_height ? (text_height - bullet_h) / 2 : 0; - const int yoff_text = bullet_h > text_height ? (bullet_h - text_height) / 2 : 0; - const int yinc = std::max(bullet_h, text_height) + SPACING; - - unsigned y = 0; - for (auto it = items.cbegin(); it != items.cend(); ++it) { - if (it < item_active) { dc.DrawBitmap(bullet_black, MARGIN, y + yoff_icon, false); } - if (it == item_active) { dc.DrawBitmap(bullet_blue, MARGIN, y + yoff_icon, false); } - if (it > item_active) { dc.DrawBitmap(bullet_white, MARGIN, y + yoff_icon, false); } - dc.DrawText(*it, MARGIN + bullet_w + SPACING, y + yoff_text); - y += yinc; - } -} - - - -// priv - -static const std::unordered_map<std::string, std::pair<std::string, std::string>> legacy_preset_map {{ - { "Original Prusa i3 MK2.ini", std::make_pair("MK2S", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode.ini", std::make_pair("MK2SMM", "0.4") }, - { "Original Prusa i3 MK2 MM Single Mode 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, - { "Original Prusa i3 MK2 MultiMaterial.ini", std::make_pair("MK2SMM", "0.4") }, - { "Original Prusa i3 MK2 MultiMaterial 0.6 nozzle.ini", std::make_pair("MK2SMM", "0.6") }, - { "Original Prusa i3 MK2 0.25 nozzle.ini", std::make_pair("MK2S", "0.25") }, - { "Original Prusa i3 MK2 0.6 nozzle.ini", std::make_pair("MK2S", "0.6") }, - { "Original Prusa i3 MK3.ini", std::make_pair("MK3", "0.4") }, -}}; - -void ConfigWizard::priv::load_vendors() -{ - const auto vendor_dir = fs::path(Slic3r::data_dir()) / "vendor"; - const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; - - // Load vendors from the "vendors" directory in datadir - for (fs::directory_iterator it(vendor_dir); it != fs::directory_iterator(); ++it) { - if (it->path().extension() == ".ini") { - try { - auto vp = VendorProfile::from_ini(it->path()); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % it->path() % e.what(); - } - - } - } - - // Additionally load up vendors from the application resources directory, but only those not seen in the datadir - for (fs::directory_iterator it(rsrc_vendor_dir); it != fs::directory_iterator(); ++it) { - if (it->path().extension() == ".ini") { - const auto id = it->path().stem().string(); - if (vendors.find(id) == vendors.end()) { - try { - auto vp = VendorProfile::from_ini(it->path()); - vendors_rsrc[vp.id] = it->path().filename().string(); - vendors[vp.id] = std::move(vp); - } - catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % it->path() % e.what(); - } - } - } - } - - // Load up the set of vendors / models / variants the user has had enabled up till now - const AppConfig *app_config = GUI::get_app_config(); - if (! app_config->legacy_datadir()) { - appconfig_vendors.set_vendors(*app_config); - } else { - // In case of legacy datadir, try to guess the preference based on the printer preset files that are present - const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; - for (fs::directory_iterator it(printer_dir); it != fs::directory_iterator(); ++it) { - auto needle = legacy_preset_map.find(it->path().filename().string()); - if (needle == legacy_preset_map.end()) { continue; } - - const auto &model = needle->second.first; - const auto &variant = needle->second.second; - appconfig_vendors.set_variant("PrusaResearch", model, variant, true); - } - } -} - -void ConfigWizard::priv::index_refresh() -{ - index->load_items(page_welcome); -} - -void ConfigWizard::priv::add_page(ConfigWizardPage *page) -{ - hscroll_sizer->Add(page, 0, wxEXPAND); - - auto *extra_buttons = page->extra_buttons(); - if (extra_buttons != nullptr) { - btnsizer->Prepend(extra_buttons, 0); - } -} - -void ConfigWizard::priv::set_page(ConfigWizardPage *page) -{ - if (page == nullptr) { return; } - if (page_current != nullptr) { page_current->Hide(); } - page_current = page; - enable_next(true); - - page->on_page_set(); - index->load_items(page_welcome); - index->set_active(page); - page->Show(); - - btn_prev->Enable(page->page_prev() != nullptr); - btn_next->Show(page->page_next() != nullptr); - btn_finish->Show(page->page_next() == nullptr); - - layout_fit(); -} - -void ConfigWizard::priv::layout_fit() -{ - q->Layout(); - q->Fit(); -} - -void ConfigWizard::priv::enable_next(bool enable) -{ - btn_next->Enable(enable); - btn_finish->Enable(enable); -} - -void ConfigWizard::priv::on_other_vendors() -{ - page_welcome - ->chain(page_vendors) - ->chain(page_update); - set_page(page_vendors); -} - -void ConfigWizard::priv::on_custom_setup() -{ - page_welcome->chain(page_firmware); - page_temps->chain(page_update); - set_page(page_firmware); -} - -void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater) -{ - const bool is_custom_setup = page_welcome->page_next() == page_firmware; - - if (! is_custom_setup) { - const auto enabled_vendors = appconfig_vendors.vendors(); - - // Install bundles from resources if needed: - std::vector<std::string> install_bundles; - for (const auto &vendor_rsrc : vendors_rsrc) { - const auto vendor = enabled_vendors.find(vendor_rsrc.first); - if (vendor == enabled_vendors.end()) { continue; } - - size_t size_sum = 0; - for (const auto &model : vendor->second) { size_sum += model.second.size(); } - if (size_sum == 0) { continue; } - - // This vendor needs to be installed - install_bundles.emplace_back(vendor_rsrc.second); - } - - // Decide whether to create snapshot based on run_reason and the reset profile checkbox - bool snapshot = true; - switch (run_reason) { - case ConfigWizard::RR_DATA_EMPTY: snapshot = false; break; - case ConfigWizard::RR_DATA_LEGACY: snapshot = true; break; - case ConfigWizard::RR_DATA_INCOMPAT: snapshot = false; break; // In this case snapshot is done by PresetUpdater with the appropriate reason - case ConfigWizard::RR_USER: snapshot = page_welcome->reset_user_profile(); break; - } - if (install_bundles.size() > 0) { - // Install bundles from resources. - updater->install_bundles_rsrc(std::move(install_bundles), snapshot); - } else { - BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources"; - } - - if (page_welcome->reset_user_profile()) { - BOOST_LOG_TRIVIAL(info) << "Resetting user profiles..."; - preset_bundle->reset(true); - } - - app_config->set_vendors(appconfig_vendors); - app_config->set("version_check", page_update->version_check ? "1" : "0"); - app_config->set("preset_update", page_update->preset_update ? "1" : "0"); - app_config->reset_selections(); - preset_bundle->load_presets(*app_config); - } else { - for (ConfigWizardPage *page = page_firmware; page != nullptr; page = page->page_next()) { - page->apply_custom_config(*custom_config); - } - preset_bundle->load_config("My Settings", *custom_config); - } - // Update the selections from the compatibilty. - preset_bundle->export_selections(*app_config); -} - -// Public - -ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) : - wxDialog(parent, wxID_ANY, name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - p(new priv(this)) -{ - p->run_reason = reason; - - p->load_vendors(); - p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({ - "gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature", - })); - - p->index = new ConfigWizardIndex(this); - - auto *vsizer = new wxBoxSizer(wxVERTICAL); - auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - auto *hline = new wxStaticLine(this); - p->btnsizer = new wxBoxSizer(wxHORIZONTAL); - - // Initially we _do not_ SetScrollRate in order to figure out the overall width of the Wizard without scrolling. - // Later, we compare that to the size of the current screen and set minimum width based on that (see below). - p->hscroll = new wxScrolledWindow(this); - p->hscroll_sizer = new wxBoxSizer(wxHORIZONTAL); - p->hscroll->SetSizer(p->hscroll_sizer); - - topsizer->Add(p->index, 0, wxEXPAND); - topsizer->AddSpacer(INDEX_MARGIN); - topsizer->Add(p->hscroll, 1, wxEXPAND); - - p->btn_prev = new wxButton(this, wxID_NONE, _(L("< &Back"))); - p->btn_next = new wxButton(this, wxID_NONE, _(L("&Next >"))); - p->btn_finish = new wxButton(this, wxID_APPLY, _(L("&Finish"))); - p->btn_cancel = new wxButton(this, wxID_CANCEL); - p->btnsizer->AddStretchSpacer(); - p->btnsizer->Add(p->btn_prev, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_next, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_finish, 0, wxLEFT, BTN_SPACING); - p->btnsizer->Add(p->btn_cancel, 0, wxLEFT, BTN_SPACING); - - p->add_page(p->page_welcome = new PageWelcome(this, reason == RR_DATA_EMPTY || reason == RR_DATA_LEGACY)); - p->add_page(p->page_update = new PageUpdate(this)); - p->add_page(p->page_vendors = new PageVendors(this)); - p->add_page(p->page_firmware = new PageFirmware(this)); - p->add_page(p->page_bed = new PageBedShape(this)); - p->add_page(p->page_diams = new PageDiameters(this)); - p->add_page(p->page_temps = new PageTemperatures(this)); - p->index_refresh(); - - p->page_welcome->chain(p->page_update); - p->page_firmware - ->chain(p->page_bed) - ->chain(p->page_diams) - ->chain(p->page_temps); - - vsizer->Add(topsizer, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - vsizer->Add(hline, 0, wxEXPAND); - vsizer->Add(p->btnsizer, 0, wxEXPAND | wxALL, DIALOG_MARGIN); - - p->set_page(p->page_welcome); - SetSizer(vsizer); - SetSizerAndFit(vsizer); - - // We can now enable scrolling on hscroll - p->hscroll->SetScrollRate(30, 30); - // Compare current ("ideal") wizard size with the size of the current screen. - // If the screen is smaller, resize wizrad to match, which will enable scrollbars. - auto wizard_size = GetSize(); - unsigned width, height; - if (GUI::get_current_screen_size(this, width, height)) { - wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN))); - wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN))); - SetMinSize(wizard_size); - } - Fit(); - - p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); }); - p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_next(); }); - p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->EndModal(wxID_OK); }); -} - -ConfigWizard::~ConfigWizard() {} - -bool ConfigWizard::run(PresetBundle *preset_bundle, const PresetUpdater *updater) -{ - BOOST_LOG_TRIVIAL(info) << "Running ConfigWizard, reason: " << p->run_reason; - if (ShowModal() == wxID_OK) { - auto *app_config = GUI::get_app_config(); - p->apply_config(app_config, preset_bundle, updater); - app_config->set_legacy_datadir(false); - BOOST_LOG_TRIVIAL(info) << "ConfigWizard applied"; - return true; - } else { - BOOST_LOG_TRIVIAL(info) << "ConfigWizard cancelled"; - return false; - } -} - - -const wxString& ConfigWizard::name() -{ - // A different naming convention is used for the Wizard on Windows vs. OSX & GTK. -#if WIN32 - static const wxString config_wizard_name = L("Configuration Wizard"); -#else - static const wxString config_wizard_name = L("Configuration Assistant"); -#endif - return config_wizard_name; -} - -} -} diff --git a/xs/src/slic3r/GUI/ConfigWizard.hpp b/xs/src/slic3r/GUI/ConfigWizard.hpp deleted file mode 100644 index 73fce7cd2..000000000 --- a/xs/src/slic3r/GUI/ConfigWizard.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef slic3r_ConfigWizard_hpp_ -#define slic3r_ConfigWizard_hpp_ - -#include <memory> - -#include <wx/dialog.h> - -namespace Slic3r { - -class PresetBundle; -class PresetUpdater; - -namespace GUI { - - -class ConfigWizard: public wxDialog -{ -public: - // Why is the Wizard run - enum RunReason { - RR_DATA_EMPTY, // No or empty datadir - RR_DATA_LEGACY, // Pre-updating datadir - RR_DATA_INCOMPAT, // Incompatible datadir - Slic3r downgrade situation - RR_USER, // User requested the Wizard from the menus - }; - - ConfigWizard(wxWindow *parent, RunReason run_reason); - ConfigWizard(ConfigWizard &&) = delete; - ConfigWizard(const ConfigWizard &) = delete; - ConfigWizard &operator=(ConfigWizard &&) = delete; - ConfigWizard &operator=(const ConfigWizard &) = delete; - ~ConfigWizard(); - - // Run the Wizard. Return whether it was completed. - bool run(PresetBundle *preset_bundle, const PresetUpdater *updater); - - static const wxString& name(); -private: - struct priv; - std::unique_ptr<priv> p; - - friend class ConfigWizardPage; -}; - - - -} -} - -#endif diff --git a/xs/src/slic3r/GUI/ConfigWizard_private.hpp b/xs/src/slic3r/GUI/ConfigWizard_private.hpp deleted file mode 100644 index 2c8f23cd3..000000000 --- a/xs/src/slic3r/GUI/ConfigWizard_private.hpp +++ /dev/null @@ -1,241 +0,0 @@ -#ifndef slic3r_ConfigWizard_private_hpp_ -#define slic3r_ConfigWizard_private_hpp_ - -#include "ConfigWizard.hpp" - -#include <vector> -#include <set> -#include <unordered_map> -#include <boost/filesystem.hpp> - -#include <wx/sizer.h> -#include <wx/panel.h> -#include <wx/button.h> -#include <wx/choice.h> -#include <wx/spinctrl.h> - -#include "libslic3r/PrintConfig.hpp" -#include "slic3r/Utils/PresetUpdater.hpp" -#include "AppConfig.hpp" -#include "Preset.hpp" -#include "BedShapeDialog.hpp" - -namespace fs = boost::filesystem; - -namespace Slic3r { -namespace GUI { - -enum { - WRAP_WIDTH = 500, - MODEL_MIN_WRAP = 150, - - DIALOG_MARGIN = 15, - INDEX_MARGIN = 40, - BTN_SPACING = 10, - INDENT_SPACING = 30, - VERTICAL_SPACING = 10, -}; - -struct PrinterPicker: wxPanel -{ - struct Checkbox : wxCheckBox - { - Checkbox(wxWindow *parent, const wxString &label, const std::string &model, const std::string &variant) : - wxCheckBox(parent, wxID_ANY, label), - model(model), - variant(variant) - {} - - std::string model; - std::string variant; - }; - - const std::string vendor_id; - std::vector<Checkbox*> cboxes; - unsigned variants_checked; - - PrinterPicker(wxWindow *parent, const VendorProfile &vendor, const AppConfig &appconfig_vendors); - - void select_all(bool select); - void select_one(size_t i, bool select); - void on_checkbox(const Checkbox *cbox, bool checked); -}; - -struct ConfigWizardPage: wxPanel -{ - ConfigWizard *parent; - const wxString shortname; - wxBoxSizer *content; - - ConfigWizardPage(ConfigWizard *parent, wxString title, wxString shortname); - - virtual ~ConfigWizardPage(); - - ConfigWizardPage* page_prev() const { return p_prev; } - ConfigWizardPage* page_next() const { return p_next; } - ConfigWizardPage* chain(ConfigWizardPage *page); - - template<class T> - void append(T *thing, int proportion = 0, int flag = wxEXPAND|wxTOP|wxBOTTOM, int border = 10) - { - content->Add(thing, proportion, flag, border); - } - - void append_text(wxString text); - void append_spacer(int space); - - ConfigWizard::priv *wizard_p() const { return parent->p.get(); } - - virtual bool Show(bool show = true); - virtual bool Hide() { return Show(false); } - virtual wxPanel* extra_buttons() { return nullptr; } - virtual void on_page_set() {} - virtual void apply_custom_config(DynamicPrintConfig &config) {} - - void enable_next(bool enable); -private: - ConfigWizardPage *p_prev; - ConfigWizardPage *p_next; -}; - -struct PageWelcome: ConfigWizardPage -{ - PrinterPicker *printer_picker; - wxPanel *others_buttons; - wxCheckBox *cbox_reset; - - PageWelcome(ConfigWizard *parent, bool check_first_variant); - - virtual wxPanel* extra_buttons() { return others_buttons; } - virtual void on_page_set(); - - bool reset_user_profile() const { return cbox_reset != nullptr ? cbox_reset->GetValue() : false; } - void on_variant_checked(); -}; - -struct PageUpdate: ConfigWizardPage -{ - bool version_check; - bool preset_update; - - PageUpdate(ConfigWizard *parent); -}; - -struct PageVendors: ConfigWizardPage -{ - std::vector<PrinterPicker*> pickers; - - PageVendors(ConfigWizard *parent); - - virtual void on_page_set(); - - void on_vendor_pick(size_t i); - void on_variant_checked(); -}; - -struct PageFirmware: ConfigWizardPage -{ - const ConfigOptionDef &gcode_opt; - wxChoice *gcode_picker; - - PageFirmware(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); -}; - -struct PageBedShape: ConfigWizardPage -{ - BedShapePanel *shape_panel; - - PageBedShape(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); -}; - -struct PageDiameters: ConfigWizardPage -{ - wxSpinCtrlDouble *spin_nozzle; - wxSpinCtrlDouble *spin_filam; - - PageDiameters(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); -}; - -struct PageTemperatures: ConfigWizardPage -{ - wxSpinCtrlDouble *spin_extr; - wxSpinCtrlDouble *spin_bed; - - PageTemperatures(ConfigWizard *parent); - virtual void apply_custom_config(DynamicPrintConfig &config); -}; - - -class ConfigWizardIndex: public wxPanel -{ -public: - ConfigWizardIndex(wxWindow *parent); - - void load_items(ConfigWizardPage *firstpage); - void set_active(ConfigWizardPage *page); -private: - const wxBitmap bg; - const wxBitmap bullet_black; - const wxBitmap bullet_blue; - const wxBitmap bullet_white; - int text_height; - - std::vector<wxString> items; - std::vector<wxString>::const_iterator item_active; - - void on_paint(wxPaintEvent &evt); -}; - -struct ConfigWizard::priv -{ - ConfigWizard *q; - ConfigWizard::RunReason run_reason; - AppConfig appconfig_vendors; - std::unordered_map<std::string, VendorProfile> vendors; - std::unordered_map<std::string, std::string> vendors_rsrc; - std::unique_ptr<DynamicPrintConfig> custom_config; - - wxScrolledWindow *hscroll = nullptr; - wxBoxSizer *hscroll_sizer = nullptr; - wxBoxSizer *btnsizer = nullptr; - ConfigWizardPage *page_current = nullptr; - ConfigWizardIndex *index = nullptr; - wxButton *btn_prev = nullptr; - wxButton *btn_next = nullptr; - wxButton *btn_finish = nullptr; - wxButton *btn_cancel = nullptr; - - PageWelcome *page_welcome = nullptr; - PageUpdate *page_update = nullptr; - PageVendors *page_vendors = nullptr; - PageFirmware *page_firmware = nullptr; - PageBedShape *page_bed = nullptr; - PageDiameters *page_diams = nullptr; - PageTemperatures *page_temps = nullptr; - - priv(ConfigWizard *q) : q(q) {} - - void load_vendors(); - void add_page(ConfigWizardPage *page); - void index_refresh(); - void set_page(ConfigWizardPage *page); - void layout_fit(); - void go_prev() { if (page_current != nullptr) { set_page(page_current->page_prev()); } } - void go_next() { if (page_current != nullptr) { set_page(page_current->page_next()); } } - void enable_next(bool enable); - - void on_other_vendors(); - void on_custom_setup(); - - void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); -}; - - - -} -} - -#endif diff --git a/xs/src/slic3r/GUI/Field.cpp b/xs/src/slic3r/GUI/Field.cpp deleted file mode 100644 index f143e8bc6..000000000 --- a/xs/src/slic3r/GUI/Field.cpp +++ /dev/null @@ -1,784 +0,0 @@ -#include "GUI.hpp"//"slic3r_gui.hpp" -#include "Field.hpp" - -//#include <wx/event.h> -#include <regex> -#include <wx/numformatter.h> -#include <wx/tooltip.h> -#include "PrintConfig.hpp" -#include <boost/algorithm/string/predicate.hpp> - -namespace Slic3r { namespace GUI { - - wxString double_to_string(double const value) - { - if (value - int(value) == 0) - return wxString::Format(_T("%i"), int(value)); - else { - int precision = 4; - for (size_t p = 1; p < 4; p++) - { - double cur_val = pow(10, p)*value; - if (cur_val - int(cur_val) == 0) { - precision = p; - break; - } - } - return wxNumberFormatter::ToString(value, precision, wxNumberFormatter::Style_None); - } - } - - void Field::PostInitialize(){ - auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - m_Undo_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - m_Undo_to_sys_btn = new MyButton(m_parent, wxID_ANY, "", wxDefaultPosition,wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - if (wxMSW) { - m_Undo_btn->SetBackgroundColour(color); - m_Undo_to_sys_btn->SetBackgroundColour(color); - } - m_Undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_initial_value(); })); - m_Undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_back_to_sys_value(); })); - - //set default bitmap - wxBitmap bmp; - bmp.LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG); - set_undo_bitmap(&bmp); - set_undo_to_sys_bitmap(&bmp); - - switch (m_opt.type) - { - case coPercents: - case coFloats: - case coStrings: - case coBools: - case coInts: { - auto tag_pos = m_opt_id.find("#"); - if (tag_pos != std::string::npos) - m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size())); - break; - } - default: - break; - } - - BUILD(); - } - - void Field::on_kill_focus(wxEvent& event) { - // Without this, there will be nasty focus bugs on Windows. - // Also, docs for wxEvent::Skip() say "In general, it is recommended to skip all - // non-command events to allow the default handling to take place." - event.Skip(); - // call the registered function if it is available - if (m_on_kill_focus!=nullptr) - m_on_kill_focus(); - } - void Field::on_change_field() - { -// std::cerr << "calling Field::_on_change \n"; - if (m_on_change != nullptr && !m_disable_change_event) - m_on_change(m_opt_id, get_value()); - } - - void Field::on_back_to_initial_value(){ - if (m_back_to_initial_value != nullptr && m_is_modified_value) - m_back_to_initial_value(m_opt_id); - } - - void Field::on_back_to_sys_value(){ - if (m_back_to_sys_value != nullptr && m_is_nonsys_value) - m_back_to_sys_value(m_opt_id); - } - - wxString Field::get_tooltip_text(const wxString& default_string) - { - wxString tooltip_text(""); - wxString tooltip = _(m_opt.tooltip); - if (tooltip.length() > 0) - tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " + - (boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string + - (boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + - _(L("parameter name")) + "\t: " + m_opt_id; - - return tooltip_text; - } - - bool Field::is_matched(const std::string& string, const std::string& pattern) - { - std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl - return std::regex_match(string, regex_pattern); - } - - void Field::get_value_by_opt_type(wxString& str) - { - switch (m_opt.type){ - case coInt: - m_value = wxAtoi(str); - break; - case coPercent: - case coPercents: - case coFloats: - case coFloat:{ - if (m_opt.type == coPercent && str.Last() == '%') - str.RemoveLast(); - else if (str.Last() == '%') { - wxString label = m_Label->GetLabel(); - if (label.Last() == '\n') label.RemoveLast(); - while (label.Last() == ' ') label.RemoveLast(); - if (label.Last() == ':') label.RemoveLast(); - show_error(m_parent, wxString::Format(_(L("%s doesn't support percentage")), label)); - set_value(double_to_string(m_opt.min), true); - m_value = double(m_opt.min); - break; - } - double val; - if(!str.ToCDouble(&val)) - { - show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits"))); - set_value(double_to_string(val), true); - } - if (m_opt.min > val || val > m_opt.max) - { - show_error(m_parent, _(L("Input value is out of range"))); - if (m_opt.min > val) val = m_opt.min; - if (val > m_opt.max) val = m_opt.max; - set_value(double_to_string(val), true); - } - m_value = val; - break; } - case coString: - case coStrings: - case coFloatOrPercent: - m_value = str.ToStdString(); - break; - default: - break; - } - } - - void TextCtrl::BUILD() { - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - wxString text_value = wxString(""); - - switch (m_opt.type) { - case coFloatOrPercent: - { - text_value = double_to_string(m_opt.default_value->getFloat()); - if (static_cast<const ConfigOptionFloatOrPercent*>(m_opt.default_value)->percent) - text_value += "%"; - break; - } - case coPercent: - { - text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getFloat())); - text_value += "%"; - break; - } - case coPercents: - case coFloats: - case coFloat: - { - double val = m_opt.type == coFloats ? - static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(m_opt_idx) : - m_opt.type == coFloat ? - m_opt.default_value->getFloat() : - static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(m_opt_idx); - text_value = double_to_string(val); - break; - } - case coString: - text_value = static_cast<const ConfigOptionString*>(m_opt.default_value)->value; - break; - case coStrings: - { - const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value); - if (vec == nullptr || vec->empty()) break; //for the case of empty default value - text_value = vec->get_at(m_opt_idx); - break; - } - default: - break; - } - - auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, (m_opt.multiline ? wxTE_MULTILINE : 0)); - - temp->SetToolTip(get_tooltip_text(text_value)); - - temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) - { - //! to allow the default handling - event.Skip(); - //! eliminating the g-code pop up text description - bool flag = false; -#ifdef __WXGTK__ - // I have no idea why, but on GTK flag works in other way - flag = true; -#endif // __WXGTK__ - temp->GetToolTip()->Enable(flag); - }), temp->GetId()); - -#if !defined(__WXGTK__) - temp->Bind(wxEVT_KILL_FOCUS, ([this, temp](wxEvent& e) - { - e.Skip();// on_kill_focus(e); - temp->GetToolTip()->Enable(true); - }), temp->GetId()); -#endif // __WXGTK__ - - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) - { -#ifdef __WXGTK__ - if (bChangedValueEvent) -#endif //__WXGTK__ - on_change_field(); - }), temp->GetId()); - -#ifdef __WXGTK__ - // to correct value updating on GTK we should: - // call on_change_field() on wxEVT_KEY_UP instead of wxEVT_TEXT - // and prevent value updating on wxEVT_KEY_DOWN - temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this); - temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this); -#endif //__WXGTK__ - - // select all text using Ctrl+A - temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) - { - if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL)) - temp->SetSelection(-1, -1); //select all - event.Skip(); - })); - - // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); - } - - boost::any& TextCtrl::get_value() - { - wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue(); - get_value_by_opt_type(ret_str); - - return m_value; - } - - void TextCtrl::enable() { dynamic_cast<wxTextCtrl*>(window)->Enable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(true); } - void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic_cast<wxTextCtrl*>(window)->SetEditable(false); } - -#ifdef __WXGTK__ - void TextCtrl::change_field_value(wxEvent& event) - { - if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP) - on_change_field(); - event.Skip(); - }; -#endif //__WXGTK__ - -void CheckBox::BUILD() { - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - bool check_value = m_opt.type == coBool ? - m_opt.default_value->getBool() : m_opt.type == coBools ? - static_cast<const ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) : - false; - - auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); - temp->SetValue(check_value); - if (m_opt.readonly) temp->Disable(); - - temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); - - temp->SetToolTip(get_tooltip_text(check_value ? "true" : "false")); - - // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); -} - -boost::any& CheckBox::get_value() -{ -// boost::any m_value; - bool value = dynamic_cast<wxCheckBox*>(window)->GetValue(); - if (m_opt.type == coBool) - m_value = static_cast<bool>(value); - else - m_value = static_cast<unsigned char>(value); - return m_value; -} - -int undef_spin_val = -9999; //! Probably, It's not necessary - -void SpinCtrl::BUILD() { - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - wxString text_value = wxString(""); - int default_value = 0; - - switch (m_opt.type) { - case coInt: - default_value = m_opt.default_value->getInt(); - text_value = wxString::Format(_T("%i"), default_value); - break; - case coInts: - { - const ConfigOptionInts *vec = static_cast<const ConfigOptionInts*>(m_opt.default_value); - if (vec == nullptr || vec->empty()) break; - for (size_t id = 0; id < vec->size(); ++id) - { - default_value = vec->get_at(id); - text_value += wxString::Format(_T("%i"), default_value); - } - break; - } - default: - break; - } - - const int min_val = m_opt.min == INT_MIN ? 0: m_opt.min; - const int max_val = m_opt.max < 2147483647 ? m_opt.max : 2147483647; - - auto temp = new wxSpinCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, - 0, min_val, max_val, default_value); - -// temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId()); -// temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { tmp_value = undef_spin_val; on_kill_focus(e); }), temp->GetId()); - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) - { -// # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value -// # when it was changed from the text control, so the on_change callback -// # gets the old one, and on_kill_focus resets the control to the old value. -// # As a workaround, we get the new value from $event->GetString and store -// # here temporarily so that we can return it from $self->get_value - std::string value = e.GetString().utf8_str().data(); - if (is_matched(value, "^\\d+$")) - tmp_value = std::stoi(value); - on_change_field(); -// # We don't reset tmp_value here because _on_change might put callbacks -// # in the CallAfter queue, and we want the tmp value to be available from -// # them as well. - }), temp->GetId()); - - temp->SetToolTip(get_tooltip_text(text_value)); - - // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); -} - -void Choice::BUILD() { - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - wxComboBox* temp; - if (!m_opt.gui_type.empty() && m_opt.gui_type.compare("select_open") != 0) - temp = new wxComboBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size); - else - temp = new wxComboBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size, 0, NULL, wxCB_READONLY); - - // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); - - if (m_opt.enum_labels.empty() && m_opt.enum_values.empty()){ - } - else{ - for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){ - const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el; - temp->Append(str); - } - set_selection(); - } - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); - temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); - - temp->SetToolTip(get_tooltip_text(temp->GetValue())); -} - -void Choice::set_selection() -{ - wxString text_value = wxString(""); - switch (m_opt.type){ - case coFloat: - case coPercent: { - double val = m_opt.default_value->getFloat(); - text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 1); - size_t idx = 0; - for (auto el : m_opt.enum_values) - { - if (el.compare(text_value) == 0) - break; - ++idx; - } -// if (m_opt.type == coPercent) text_value += "%"; - idx == m_opt.enum_values.size() ? - dynamic_cast<wxComboBox*>(window)->SetValue(text_value) : - dynamic_cast<wxComboBox*>(window)->SetSelection(idx); - break; - } - case coEnum:{ - int id_value = static_cast<const ConfigOptionEnum<SeamPosition>*>(m_opt.default_value)->value; //!! - dynamic_cast<wxComboBox*>(window)->SetSelection(id_value); - break; - } - case coInt:{ - int val = m_opt.default_value->getInt(); //!! - text_value = wxString::Format(_T("%i"), int(val)); - size_t idx = 0; - for (auto el : m_opt.enum_values) - { - if (el.compare(text_value) == 0) - break; - ++idx; - } - idx == m_opt.enum_values.size() ? - dynamic_cast<wxComboBox*>(window)->SetValue(text_value) : - dynamic_cast<wxComboBox*>(window)->SetSelection(idx); - break; - } - case coStrings:{ - text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx); - - size_t idx = 0; - for (auto el : m_opt.enum_values) - { - if (el.compare(text_value) == 0) - break; - ++idx; - } - idx == m_opt.enum_values.size() ? - dynamic_cast<wxComboBox*>(window)->SetValue(text_value) : - dynamic_cast<wxComboBox*>(window)->SetSelection(idx); - break; - } - } -} - -void Choice::set_value(const std::string& value, bool change_event) //! Redundant? -{ - m_disable_change_event = !change_event; - - size_t idx=0; - for (auto el : m_opt.enum_values) - { - if (el.compare(value) == 0) - break; - ++idx; - } - - idx == m_opt.enum_values.size() ? - dynamic_cast<wxComboBox*>(window)->SetValue(value) : - dynamic_cast<wxComboBox*>(window)->SetSelection(idx); - - m_disable_change_event = false; -} - -void Choice::set_value(const boost::any& value, bool change_event) -{ - m_disable_change_event = !change_event; - - switch (m_opt.type){ - case coInt: - case coFloat: - case coPercent: - case coString: - case coStrings:{ - wxString text_value; - if (m_opt.type == coInt) - text_value = wxString::Format(_T("%i"), int(boost::any_cast<int>(value))); - else - text_value = boost::any_cast<wxString>(value); - auto idx = 0; - for (auto el : m_opt.enum_values) - { - if (el.compare(text_value) == 0) - break; - ++idx; - } - idx == m_opt.enum_values.size() ? - dynamic_cast<wxComboBox*>(window)->SetValue(text_value) : - dynamic_cast<wxComboBox*>(window)->SetSelection(idx); - break; - } - case coEnum:{ - int val = boost::any_cast<int>(value); - if (m_opt_id.compare("external_fill_pattern") == 0) - { - if (!m_opt.enum_values.empty()){ - std::string key; - t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values(); - for (auto it : map_names) { - if (val == it.second) { - key = it.first; - break; - } - } - - size_t idx = 0; - for (auto el : m_opt.enum_values) - { - if (el.compare(key) == 0) - break; - ++idx; - } - - val = idx == m_opt.enum_values.size() ? 0 : idx; - } - else - val = 0; - } - dynamic_cast<wxComboBox*>(window)->SetSelection(val); - break; - } - default: - break; - } - - m_disable_change_event = false; -} - -//! it's needed for _update_serial_ports() -void Choice::set_values(const std::vector<std::string>& values) -{ - if (values.empty()) - return; - m_disable_change_event = true; - -// # it looks that Clear() also clears the text field in recent wxWidgets versions, -// # but we want to preserve it - auto ww = dynamic_cast<wxComboBox*>(window); - auto value = ww->GetValue(); - ww->Clear(); - ww->Append(""); - for (auto el : values) - ww->Append(wxString(el)); - ww->SetValue(value); - - m_disable_change_event = false; -} - -boost::any& Choice::get_value() -{ -// boost::any m_value; - wxString ret_str = static_cast<wxComboBox*>(window)->GetValue(); - - // options from right panel - std::vector <std::string> right_panel_options{ "support", "scale_unit" }; - for (auto rp_option: right_panel_options) - if (m_opt_id == rp_option) - return m_value = boost::any(ret_str); - - if (m_opt.type != coEnum) - /*m_value = */get_value_by_opt_type(ret_str); - else - { - int ret_enum = static_cast<wxComboBox*>(window)->GetSelection(); - if (m_opt_id.compare("external_fill_pattern") == 0) - { - if (!m_opt.enum_values.empty()){ - std::string key = m_opt.enum_values[ret_enum]; - t_config_enum_values map_names = ConfigOptionEnum<InfillPattern>::get_enum_values(); - int value = map_names.at(key); - - m_value = static_cast<InfillPattern>(value); - } - else - m_value = static_cast<InfillPattern>(0); - } - if (m_opt_id.compare("fill_pattern") == 0) - m_value = static_cast<InfillPattern>(ret_enum); - else if (m_opt_id.compare("gcode_flavor") == 0) - m_value = static_cast<GCodeFlavor>(ret_enum); - else if (m_opt_id.compare("support_material_pattern") == 0) - m_value = static_cast<SupportMaterialPattern>(ret_enum); - else if (m_opt_id.compare("seam_position") == 0) - m_value = static_cast<SeamPosition>(ret_enum); - else if (m_opt_id.compare("host_type") == 0) - m_value = static_cast<PrintHostType>(ret_enum); - } - - return m_value; -} - -void ColourPicker::BUILD() -{ - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - wxString clr(static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx)); - auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size); - - // // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); - - temp->Bind(wxEVT_COLOURPICKER_CHANGED, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); - - temp->SetToolTip(get_tooltip_text(clr)); -} - -boost::any& ColourPicker::get_value(){ -// boost::any m_value; - - auto colour = static_cast<wxColourPickerCtrl*>(window)->GetColour(); - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue()); - m_value = clr_str.ToStdString(); - - return m_value; -} - -void PointCtrl::BUILD() -{ - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - auto temp = new wxBoxSizer(wxHORIZONTAL); - // $self->wxSizer($sizer); - // - wxSize field_size(40, -1); - - auto default_pt = static_cast<const ConfigOptionPoints*>(m_opt.default_value)->values.at(0); - double val = default_pt(0); - wxString X = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); - val = default_pt(1); - wxString Y = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); - - x_textctrl = new wxTextCtrl(m_parent, wxID_ANY, X, wxDefaultPosition, field_size); - y_textctrl = new wxTextCtrl(m_parent, wxID_ANY, Y, wxDefaultPosition, field_size); - - temp->Add(new wxStaticText(m_parent, wxID_ANY, "x : "), 0, wxALIGN_CENTER_VERTICAL, 0); - temp->Add(x_textctrl); - temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0); - temp->Add(y_textctrl); - - x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); - y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); - - // // recast as a wxWindow to fit the calling convention - sizer = dynamic_cast<wxSizer*>(temp); - - x_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); - y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); -} - -void PointCtrl::set_value(const Vec2d& value, bool change_event) -{ - m_disable_change_event = !change_event; - - double val = value(0); - x_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None)); - val = value(1); - y_textctrl->SetValue(val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None)); - - m_disable_change_event = false; -} - -void PointCtrl::set_value(const boost::any& value, bool change_event) -{ - Vec2d pt(Vec2d::Zero()); - const Vec2d *ptf = boost::any_cast<Vec2d>(&value); - if (!ptf) - { - ConfigOptionPoints* pts = boost::any_cast<ConfigOptionPoints*>(value); - pt = pts->values.at(0); - } - else - pt = *ptf; - set_value(pt, change_event); -} - -boost::any& PointCtrl::get_value() -{ - double x, y; - x_textctrl->GetValue().ToDouble(&x); - y_textctrl->GetValue().ToDouble(&y); - return m_value = Vec2d(x, y); -} - -void StaticText::BUILD() -{ - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - wxString legend(static_cast<const ConfigOptionString*>(m_opt.default_value)->value); - auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size); - temp->SetFont(bold_font()); - - // // recast as a wxWindow to fit the calling convention - window = dynamic_cast<wxWindow*>(temp); - - temp->SetToolTip(get_tooltip_text(legend)); -} - -void SliderCtrl::BUILD() -{ - auto size = wxSize(wxDefaultSize); - if (m_opt.height >= 0) size.SetHeight(m_opt.height); - if (m_opt.width >= 0) size.SetWidth(m_opt.width); - - auto temp = new wxBoxSizer(wxHORIZONTAL); - - auto def_val = static_cast<const ConfigOptionInt*>(m_opt.default_value)->value; - auto min = m_opt.min == INT_MIN ? 0 : m_opt.min; - auto max = m_opt.max == INT_MAX ? 100 : m_opt.max; - - m_slider = new wxSlider(m_parent, wxID_ANY, def_val * m_scale, - min * m_scale, max * m_scale, - wxDefaultPosition, size); - wxSize field_size(40, -1); - - m_textctrl = new wxTextCtrl(m_parent, wxID_ANY, wxString::Format("%d", m_slider->GetValue()/m_scale), - wxDefaultPosition, field_size); - - temp->Add(m_slider, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, 0); - temp->Add(m_textctrl, 0, wxALIGN_CENTER_VERTICAL, 0); - - m_slider->Bind(wxEVT_SLIDER, ([this](wxCommandEvent e) { - if (!m_disable_change_event){ - int val = boost::any_cast<int>(get_value()); - m_textctrl->SetLabel(wxString::Format("%d", val)); - on_change_field(); - } - }), m_slider->GetId()); - - m_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { - std::string value = e.GetString().utf8_str().data(); - if (is_matched(value, "^-?\\d+(\\.\\d*)?$")){ - m_disable_change_event = true; - m_slider->SetValue(stoi(value)*m_scale); - m_disable_change_event = false; - on_change_field(); - } - }), m_textctrl->GetId()); - - m_sizer = dynamic_cast<wxSizer*>(temp); -} - -void SliderCtrl::set_value(const boost::any& value, bool change_event) -{ - m_disable_change_event = !change_event; - - m_slider->SetValue(boost::any_cast<int>(value)*m_scale); - int val = boost::any_cast<int>(get_value()); - m_textctrl->SetLabel(wxString::Format("%d", val)); - - m_disable_change_event = false; -} - -boost::any& SliderCtrl::get_value() -{ -// int ret_val; -// x_textctrl->GetValue().ToDouble(&val); - return m_value = int(m_slider->GetValue()/m_scale); -} - - -} // GUI -} // Slic3r - - diff --git a/xs/src/slic3r/GUI/Field.hpp b/xs/src/slic3r/GUI/Field.hpp deleted file mode 100644 index c38658e2b..000000000 --- a/xs/src/slic3r/GUI/Field.hpp +++ /dev/null @@ -1,466 +0,0 @@ -#ifndef SLIC3R_GUI_FIELD_HPP -#define SLIC3R_GUI_FIELD_HPP - -#include <wx/wxprec.h> -#ifndef WX_PRECOMP - #include <wx/wx.h> -#endif - -#include <memory> -#include <functional> -#include <boost/any.hpp> - -#include <wx/spinctrl.h> -#include <wx/clrpicker.h> - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Config.hpp" - -//#include "slic3r_gui.hpp" -#include "GUI.hpp" -#include "Utils.hpp" - -#ifdef __WXMSW__ -#define wxMSW true -#else -#define wxMSW false -#endif - -namespace Slic3r { namespace GUI { - -class Field; -using t_field = std::unique_ptr<Field>; -using t_kill_focus = std::function<void()>; -using t_change = std::function<void(t_config_option_key, const boost::any&)>; -using t_back_to_init = std::function<void(const std::string&)>; - -wxString double_to_string(double const value); - -class MyButton : public wxButton -{ - bool hidden = false; // never show button if it's hidden ones -public: - MyButton() {} - MyButton(wxWindow* parent, wxWindowID id, const wxString& label = wxEmptyString, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, long style = 0, - const wxValidator& validator = wxDefaultValidator, - const wxString& name = wxTextCtrlNameStr) - { - this->Create(parent, id, label, pos, size, style, validator, name); - } - - // overridden from wxWindow base class - virtual bool - AcceptsFocusFromKeyboard() const { return false; } - - virtual bool Show(bool show = true) override { - if (!show) - hidden = true; - return wxButton::Show(!hidden); - } -}; - -class Field { -protected: - // factory function to defer and enforce creation of derived type. - virtual void PostInitialize(); - - /// Finish constructing the Field's wxWidget-related properties, including setting its own sizer, etc. - virtual void BUILD() = 0; - - /// Call the attached on_kill_focus method. - //! It's important to use wxEvent instead of wxFocusEvent, - //! in another case we can't unfocused control at all - void on_kill_focus(wxEvent& event); - /// Call the attached on_change method. - void on_change_field(); - /// Call the attached m_back_to_initial_value method. - void on_back_to_initial_value(); - /// Call the attached m_back_to_sys_value method. - void on_back_to_sys_value(); - -public: - /// parent wx item, opportunity to refactor (probably not necessary - data duplication) - wxWindow* m_parent {nullptr}; - - /// Function object to store callback passed in from owning object. - t_kill_focus m_on_kill_focus {nullptr}; - - /// Function object to store callback passed in from owning object. - t_change m_on_change {nullptr}; - - /// Function object to store callback passed in from owning object. - t_back_to_init m_back_to_initial_value{ nullptr }; - t_back_to_init m_back_to_sys_value{ nullptr }; - - // This is used to avoid recursive invocation of the field change/update by wxWidgets. - bool m_disable_change_event {false}; - bool m_is_modified_value {false}; - bool m_is_nonsys_value {true}; - - /// Copy of ConfigOption for deduction purposes - const ConfigOptionDef m_opt {ConfigOptionDef()}; - const t_config_option_key m_opt_id;//! {""}; - int m_opt_idx = 0; - - /// Sets a value for this control. - /// subclasses should overload with a specific version - /// Postcondition: Method does not fire the on_change event. - virtual void set_value(const boost::any& value, bool change_event) = 0; - - /// Gets a boost::any representing this control. - /// subclasses should overload with a specific version - virtual boost::any& get_value() = 0; - - virtual void enable() = 0; - virtual void disable() = 0; - - /// Fires the enable or disable function, based on the input. - inline void toggle(bool en) { en ? enable() : disable(); } - - virtual wxString get_tooltip_text(const wxString& default_string); - - // set icon to "UndoToSystemValue" button according to an inheritance of preset -// void set_nonsys_btn_icon(const wxBitmap& icon); - - Field(const ConfigOptionDef& opt, const t_config_option_key& id) : m_opt(opt), m_opt_id(id) {}; - Field(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : m_parent(parent), m_opt(opt), m_opt_id(id) {}; - - /// If you don't know what you are getting back, check both methods for nullptr. - virtual wxSizer* getSizer() { return nullptr; } - virtual wxWindow* getWindow() { return nullptr; } - - bool is_matched(const std::string& string, const std::string& pattern); - void get_value_by_opt_type(wxString& str); - - /// Factory method for generating new derived classes. - template<class T> - static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) // interface for creating shared objects - { - auto p = Slic3r::make_unique<T>(parent, opt, id); - p->PostInitialize(); - return std::move(p); //!p; - } - - bool set_undo_bitmap(const wxBitmap *bmp) { - if (m_undo_bitmap != bmp) { - m_undo_bitmap = bmp; - m_Undo_btn->SetBitmap(*bmp); - return true; - } - return false; - } - - bool set_undo_to_sys_bitmap(const wxBitmap *bmp) { - if (m_undo_to_sys_bitmap != bmp) { - m_undo_to_sys_bitmap = bmp; - m_Undo_to_sys_btn->SetBitmap(*bmp); - return true; - } - return false; - } - - bool set_label_colour(const wxColour *clr) { - if (m_Label == nullptr) return false; - if (m_label_color != clr) { - m_label_color = clr; - m_Label->SetForegroundColour(*clr); - m_Label->Refresh(true); - } - return false; - } - - bool set_label_colour_force(const wxColour *clr) { - if (m_Label == nullptr) return false; - m_Label->SetForegroundColour(*clr); - m_Label->Refresh(true); - return false; - } - - bool set_undo_tooltip(const wxString *tip) { - if (m_undo_tooltip != tip) { - m_undo_tooltip = tip; - m_Undo_btn->SetToolTip(*tip); - return true; - } - return false; - } - - bool set_undo_to_sys_tooltip(const wxString *tip) { - if (m_undo_to_sys_tooltip != tip) { - m_undo_to_sys_tooltip = tip; - m_Undo_to_sys_btn->SetToolTip(*tip); - return true; - } - return false; - } - - void set_side_text_ptr(wxStaticText* side_text) { - m_side_text = side_text; - } - -protected: - MyButton* m_Undo_btn = nullptr; - // Bitmap and Tooltip text for m_Undo_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. - const wxBitmap* m_undo_bitmap = nullptr; - const wxString* m_undo_tooltip = nullptr; - MyButton* m_Undo_to_sys_btn = nullptr; - // Bitmap and Tooltip text for m_Undo_to_sys_btn. The wxButton will be updated only if the new wxBitmap pointer differs from the currently rendered one. - const wxBitmap* m_undo_to_sys_bitmap = nullptr; - const wxString* m_undo_to_sys_tooltip = nullptr; - - wxStaticText* m_Label = nullptr; - // Color for Label. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. - const wxColour* m_label_color = nullptr; - - wxStaticText* m_side_text = nullptr; - - // current value - boost::any m_value; - - friend class OptionsGroup; -}; - -/// Convenience function, accepts a const reference to t_field and checks to see whether -/// or not both wx pointers are null. -inline bool is_bad_field(const t_field& obj) { return obj->getSizer() == nullptr && obj->getWindow() == nullptr; } - -/// Covenience function to determine whether this field is a valid window field. -inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && obj->getWindow() != nullptr && obj->getSizer() == nullptr; } - -/// Covenience function to determine whether this field is a valid sizer field. -inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && obj->getSizer() != nullptr; } - -class TextCtrl : public Field { - using Field::Field; -#ifdef __WXGTK__ - bool bChangedValueEvent = true; - void change_field_value(wxEvent& event); -#endif //__WXGTK__ -public: - TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~TextCtrl() {} - - void BUILD(); - wxWindow* window {nullptr}; - - virtual void set_value(const std::string& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxTextCtrl*>(window)->SetValue(wxString(value)); - m_disable_change_event = false; - } - virtual void set_value(const boost::any& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value)); - m_disable_change_event = false; - } - - boost::any& get_value() override; - - virtual void enable(); - virtual void disable(); - virtual wxWindow* getWindow() { return window; } -}; - -class CheckBox : public Field { - using Field::Field; -public: - CheckBox(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - CheckBox(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~CheckBox() {} - - wxWindow* window{ nullptr }; - void BUILD() override; - - void set_value(const bool value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxCheckBox*>(window)->SetValue(value); - m_disable_change_event = false; - } - void set_value(const boost::any& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value)); - m_disable_change_event = false; - } - boost::any& get_value() override; - - void enable() override { dynamic_cast<wxCheckBox*>(window)->Enable(); } - void disable() override { dynamic_cast<wxCheckBox*>(window)->Disable(); } - wxWindow* getWindow() override { return window; } -}; - -class SpinCtrl : public Field { - using Field::Field; -public: - SpinCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id), tmp_value(-9999) {} - SpinCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id), tmp_value(-9999) {} - ~SpinCtrl() {} - - int tmp_value; - - wxWindow* window{ nullptr }; - void BUILD() override; - - void set_value(const std::string& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxSpinCtrl*>(window)->SetValue(value); - m_disable_change_event = false; - } - void set_value(const boost::any& value, bool change_event = false) { - m_disable_change_event = !change_event; - tmp_value = boost::any_cast<int>(value); - dynamic_cast<wxSpinCtrl*>(window)->SetValue(tmp_value); - m_disable_change_event = false; - } - boost::any& get_value() override { -// return boost::any(tmp_value); - return m_value = tmp_value; - } - - void enable() override { dynamic_cast<wxSpinCtrl*>(window)->Enable(); } - void disable() override { dynamic_cast<wxSpinCtrl*>(window)->Disable(); } - wxWindow* getWindow() override { return window; } -}; - -class Choice : public Field { - using Field::Field; -public: - Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~Choice() {} - - wxWindow* window{ nullptr }; - void BUILD() override; - - void set_selection(); - void set_value(const std::string& value, bool change_event = false); - void set_value(const boost::any& value, bool change_event = false); - void set_values(const std::vector<std::string> &values); - boost::any& get_value() override; - - void enable() override { dynamic_cast<wxComboBox*>(window)->Enable(); }; - void disable() override{ dynamic_cast<wxComboBox*>(window)->Disable(); }; - wxWindow* getWindow() override { return window; } -}; - -class ColourPicker : public Field { - using Field::Field; -public: - ColourPicker(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - ColourPicker(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~ColourPicker() {} - - wxWindow* window{ nullptr }; - void BUILD() override; - - void set_value(const std::string& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(value); - m_disable_change_event = false; - } - void set_value(const boost::any& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxColourPickerCtrl*>(window)->SetColour(boost::any_cast<wxString>(value)); - m_disable_change_event = false; - } - - boost::any& get_value() override; - - void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); }; - void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); }; - wxWindow* getWindow() override { return window; } -}; - -class PointCtrl : public Field { - using Field::Field; -public: - PointCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - PointCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~PointCtrl() {} - - wxSizer* sizer{ nullptr }; - wxTextCtrl* x_textctrl{ nullptr }; - wxTextCtrl* y_textctrl{ nullptr }; - - void BUILD() override; - - void set_value(const Vec2d& value, bool change_event = false); - void set_value(const boost::any& value, bool change_event = false); - boost::any& get_value() override; - - void enable() override { - x_textctrl->Enable(); - y_textctrl->Enable(); } - void disable() override{ - x_textctrl->Disable(); - y_textctrl->Disable(); } - wxSizer* getSizer() override { return sizer; } -}; - -class StaticText : public Field { - using Field::Field; -public: - StaticText(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - StaticText(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~StaticText() {} - - wxWindow* window{ nullptr }; - void BUILD() override; - - void set_value(const std::string& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxStaticText*>(window)->SetLabel(value); - m_disable_change_event = false; - } - void set_value(const boost::any& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value)); - m_disable_change_event = false; - } - - boost::any& get_value()override { return m_value; } - - void enable() override { dynamic_cast<wxStaticText*>(window)->Enable(); }; - void disable() override{ dynamic_cast<wxStaticText*>(window)->Disable(); }; - wxWindow* getWindow() override { return window; } -}; - -class SliderCtrl : public Field { - using Field::Field; -public: - SliderCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - SliderCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} - ~SliderCtrl() {} - - wxSizer* m_sizer{ nullptr }; - wxTextCtrl* m_textctrl{ nullptr }; - wxSlider* m_slider{ nullptr }; - - int m_scale = 10; - - void BUILD() override; - - void set_value(const int value, bool change_event = false); - void set_value(const boost::any& value, bool change_event = false); - boost::any& get_value() override; - - void enable() override { - m_slider->Enable(); - m_textctrl->Enable(); - m_textctrl->SetEditable(true); - } - void disable() override{ - m_slider->Disable(); - m_textctrl->Disable(); - m_textctrl->SetEditable(false); - } - wxSizer* getSizer() override { return m_sizer; } - wxWindow* getWindow() override { return dynamic_cast<wxWindow*>(m_slider); } -}; - -} // GUI -} // Slic3r - -#endif /* SLIC3R_GUI_FIELD_HPP */ diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp deleted file mode 100644 index d5ac64d90..000000000 --- a/xs/src/slic3r/GUI/FirmwareDialog.cpp +++ /dev/null @@ -1,846 +0,0 @@ -#include <numeric> -#include <algorithm> -#include <thread> -#include <condition_variable> -#include <stdexcept> -#include <boost/format.hpp> -#include <boost/asio.hpp> -#include <boost/filesystem/path.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/log/trivial.hpp> -#include <boost/optional.hpp> - -#include "libslic3r/Utils.hpp" -#include "avrdude/avrdude-slic3r.hpp" -#include "GUI.hpp" -#include "MsgDialog.hpp" -#include "../Utils/HexFile.hpp" -#include "../Utils/Serial.hpp" - -// wx includes need to come after asio because of the WinSock.h problem -#include "FirmwareDialog.hpp" - -#include <wx/app.h> -#include <wx/event.h> -#include <wx/sizer.h> -#include <wx/settings.h> -#include <wx/timer.h> -#include <wx/panel.h> -#include <wx/button.h> -#include <wx/filepicker.h> -#include <wx/textctrl.h> -#include <wx/stattext.h> -#include <wx/combobox.h> -#include <wx/gauge.h> -#include <wx/collpane.h> -#include <wx/msgdlg.h> -#include <wx/filefn.h> - - -namespace fs = boost::filesystem; -namespace asio = boost::asio; -using boost::system::error_code; -using boost::optional; - - -namespace Slic3r { - -using Utils::HexFile; -using Utils::SerialPortInfo; -using Utils::Serial; - - -// USB IDs used to perform device lookup -enum { - USB_VID_PRUSA = 0x2c99, - USB_PID_MK2 = 1, - USB_PID_MK3 = 2, - USB_PID_MMU_BOOT = 3, - USB_PID_MMU_APP = 4, -}; - -// This enum discriminates the kind of information in EVT_AVRDUDE, -// it's stored in the ExtraLong field of wxCommandEvent. -enum AvrdudeEvent -{ - AE_MESSAGE, - AE_PROGRESS, - AE_STATUS, - AE_EXIT, -}; - -wxDECLARE_EVENT(EVT_AVRDUDE, wxCommandEvent); -wxDEFINE_EVENT(EVT_AVRDUDE, wxCommandEvent); - -wxDECLARE_EVENT(EVT_ASYNC_DIALOG, wxCommandEvent); -wxDEFINE_EVENT(EVT_ASYNC_DIALOG, wxCommandEvent); - - -// Private - -struct FirmwareDialog::priv -{ - enum AvrDudeComplete - { - AC_NONE, - AC_SUCCESS, - AC_FAILURE, - AC_USER_CANCELLED, - }; - - FirmwareDialog *q; // PIMPL back pointer ("Q-Pointer") - - // GUI elements - wxComboBox *port_picker; - wxStaticText *port_autodetect; - wxFilePickerCtrl *hex_picker; - wxStaticText *txt_status; - wxGauge *progressbar; - wxCollapsiblePane *spoiler; - wxTextCtrl *txt_stdout; - wxButton *btn_rescan; - wxButton *btn_close; - wxButton *btn_flash; - wxString btn_flash_label_ready; - wxString btn_flash_label_flashing; - wxString label_status_flashing; - - wxTimer timer_pulse; - - // Async modal dialog during flashing - std::mutex mutex; - int modal_response; - std::condition_variable response_cv; - - // Data - std::vector<SerialPortInfo> ports; - optional<SerialPortInfo> port; - HexFile hex_file; - - // This is a shared pointer holding the background AvrDude task - // also serves as a status indication (it is set _iff_ the background task is running, otherwise it is reset). - AvrDude::Ptr avrdude; - std::string avrdude_config; - unsigned progress_tasks_done; - unsigned progress_tasks_bar; - bool user_cancelled; - const bool extra_verbose; // For debugging - - priv(FirmwareDialog *q) : - q(q), - btn_flash_label_ready(_(L("Flash!"))), - btn_flash_label_flashing(_(L("Cancel"))), - label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))), - timer_pulse(q), - avrdude_config((fs::path(::Slic3r::resources_dir()) / "avrdude" / "avrdude.conf").string()), - progress_tasks_done(0), - progress_tasks_bar(0), - user_cancelled(false), - extra_verbose(false) - {} - - void find_serial_ports(); - void fit_no_shrink(); - void set_txt_status(const wxString &label); - void flashing_start(unsigned tasks); - void flashing_done(AvrDudeComplete complete); - void enable_port_picker(bool enable); - void load_hex_file(const wxString &path); - void queue_status(wxString message); - void queue_error(const wxString &message); - - bool ask_model_id_mismatch(const std::string &printer_model); - bool check_model_id(); - void wait_for_mmu_bootloader(unsigned retries); - void mmu_reboot(const SerialPortInfo &port); - void lookup_port_mmu(); - void prepare_common(); - void prepare_mk2(); - void prepare_mk3(); - void prepare_mm_control(); - void perform_upload(); - - void user_cancel(); - void on_avrdude(const wxCommandEvent &evt); - void on_async_dialog(const wxCommandEvent &evt); - void ensure_joined(); -}; - -void FirmwareDialog::priv::find_serial_ports() -{ - auto new_ports = Utils::scan_serial_ports_extended(); - if (new_ports != this->ports) { - this->ports = new_ports; - port_picker->Clear(); - for (const auto &port : this->ports) - port_picker->Append(wxString::FromUTF8(port.friendly_name.data())); - if (ports.size() > 0) { - int idx = port_picker->GetValue().IsEmpty() ? 0 : -1; - for (int i = 0; i < (int)this->ports.size(); ++ i) - if (this->ports[i].is_printer) { - idx = i; - break; - } - if (idx != -1) - port_picker->SetSelection(idx); - } - } -} - -void FirmwareDialog::priv::fit_no_shrink() -{ - // Ensure content fits into window and window is not shrinked - const auto old_size = q->GetSize(); - q->Layout(); - q->Fit(); - const auto new_size = q->GetSize(); - const auto new_width = std::max(old_size.GetWidth(), new_size.GetWidth()); - const auto new_height = std::max(old_size.GetHeight(), new_size.GetHeight()); - q->SetSize(new_width, new_height); -} - -void FirmwareDialog::priv::set_txt_status(const wxString &label) -{ - const auto width = txt_status->GetSize().GetWidth(); - txt_status->SetLabel(label); - txt_status->Wrap(width); - - fit_no_shrink(); -} - -void FirmwareDialog::priv::flashing_start(unsigned tasks) -{ - modal_response = wxID_NONE; - txt_stdout->Clear(); - set_txt_status(label_status_flashing); - txt_status->SetForegroundColour(GUI::get_label_clr_modified()); - port_picker->Disable(); - btn_rescan->Disable(); - hex_picker->Disable(); - btn_close->Disable(); - btn_flash->SetLabel(btn_flash_label_flashing); - progressbar->SetRange(200 * tasks); // See progress callback below - progressbar->SetValue(0); - progress_tasks_done = 0; - progress_tasks_bar = 0; - user_cancelled = false; - timer_pulse.Start(50); -} - -void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete) -{ - auto text_color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); - port_picker->Enable(); - btn_rescan->Enable(); - hex_picker->Enable(); - btn_close->Enable(); - btn_flash->SetLabel(btn_flash_label_ready); - txt_status->SetForegroundColour(text_color); - timer_pulse.Stop(); - progressbar->SetValue(progressbar->GetRange()); - - switch (complete) { - case AC_SUCCESS: set_txt_status(_(L("Flashing succeeded!"))); break; - case AC_FAILURE: set_txt_status(_(L("Flashing failed. Please see the avrdude log below."))); break; - case AC_USER_CANCELLED: set_txt_status(_(L("Flashing cancelled."))); break; - default: break; - } -} - -void FirmwareDialog::priv::enable_port_picker(bool enable) -{ - port_picker->Show(enable); - btn_rescan->Show(enable); - port_autodetect->Show(! enable); - q->Layout(); - fit_no_shrink(); -} - -void FirmwareDialog::priv::load_hex_file(const wxString &path) -{ - hex_file = HexFile(path.wx_str()); - enable_port_picker(hex_file.device != HexFile::DEV_MM_CONTROL); -} - -void FirmwareDialog::priv::queue_status(wxString message) -{ - auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId()); - evt->SetExtraLong(AE_STATUS); - evt->SetString(std::move(message)); - wxQueueEvent(this->q, evt); -} - -void FirmwareDialog::priv::queue_error(const wxString &message) -{ - auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId()); - evt->SetExtraLong(AE_STATUS); - evt->SetString(wxString::Format(_(L("Flashing failed: %s")), message)); - - wxQueueEvent(this->q, evt); avrdude->cancel(); -} - -bool FirmwareDialog::priv::ask_model_id_mismatch(const std::string &printer_model) -{ - // model_id in the hex file doesn't match what the printer repoted. - // Ask the user if it should be flashed anyway. - - std::unique_lock<std::mutex> lock(mutex); - - auto evt = new wxCommandEvent(EVT_ASYNC_DIALOG, this->q->GetId()); - evt->SetString(wxString::Format(_(L( - "This firmware hex file does not match the printer model.\n" - "The hex file is intended for: %s\n" - "Printer reported: %s\n\n" - "Do you want to continue and flash this hex file anyway?\n" - "Please only continue if you are sure this is the right thing to do.")), - hex_file.model_id, printer_model - )); - wxQueueEvent(this->q, evt); - - response_cv.wait(lock, [this]() { return this->modal_response != wxID_NONE; }); - - if (modal_response == wxID_YES) { - return true; - } else { - user_cancel(); - return false; - } -} - -bool FirmwareDialog::priv::check_model_id() -{ - // XXX: The implementation in Serial doesn't currently work reliably enough to be used. - // Therefore, regretably, so far the check cannot be used and we just return true here. - // TODO: Rewrite Serial using more platform-native code. - return true; - - // if (hex_file.model_id.empty()) { - // // No data to check against, assume it's ok - // return true; - // } - - // asio::io_service io; - // Serial serial(io, port->port, 115200); - // serial.printer_setup(); - - // enum { - // TIMEOUT = 2000, - // RETREIES = 5, - // }; - - // if (! serial.printer_ready_wait(RETREIES, TIMEOUT)) { - // queue_error(wxString::Format(_(L("Could not connect to the printer at %s")), port->port)); - // return false; - // } - - // std::string line; - // error_code ec; - // serial.printer_write_line("PRUSA Rev"); - // while (serial.read_line(TIMEOUT, line, ec)) { - // if (ec) { - // queue_error(wxString::Format(_(L("Could not connect to the printer at %s")), port->port)); - // return false; - // } - - // if (line == "ok") { continue; } - - // if (line == hex_file.model_id) { - // return true; - // } else { - // return ask_model_id_mismatch(line); - // } - - // line.clear(); - // } - - // return false; -} - -void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries) -{ - enum { - SLEEP_MS = 500, - }; - - for (unsigned i = 0; i < retries && !user_cancelled; i++) { - std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_MS)); - - auto ports = Utils::scan_serial_ports_extended(); - ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) { - return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT; - }), ports.end()); - - if (ports.size() == 1) { - port = ports[0]; - return; - } else if (ports.size() > 1) { - BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found"; - queue_error(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing."))); - return; - } - } -} - -void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port) -{ - asio::io_service io; - Serial serial(io, port.port, 1200); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); -} - -void FirmwareDialog::priv::lookup_port_mmu() -{ - static const auto msg_not_found = - "The Multi Material Control device was not found.\n" - "If the device is connected, please press the Reset button next to the USB connector ..."; - - BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ..."; - - auto ports = Utils::scan_serial_ports_extended(); - ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) { - return port.id_vendor != USB_VID_PRUSA || - port.id_product != USB_PID_MMU_BOOT && - port.id_product != USB_PID_MMU_APP; - }), ports.end()); - - if (ports.size() == 0) { - BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ..."; - queue_status(_(L(msg_not_found))); - wait_for_mmu_bootloader(30); - } else if (ports.size() > 1) { - BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found"; - queue_error(_(L("Multiple Original Prusa i3 MMU 2.0 devices found. Please only connect one at a time for flashing."))); - } else { - if (ports[0].id_product == USB_PID_MMU_APP) { - // The device needs to be rebooted into the bootloader mode - BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port; - mmu_reboot(ports[0]); - wait_for_mmu_bootloader(10); - - if (! port) { - // The device in bootloader mode was not found, inform the user and wait some more... - BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ..."; - queue_status(_(L(msg_not_found))); - wait_for_mmu_bootloader(30); - } - } else { - port = ports[0]; - } - } -} - -void FirmwareDialog::priv::prepare_common() -{ - std::vector<std::string> args {{ - extra_verbose ? "-vvvvv" : "-v", - "-p", "atmega2560", - // Using the "Wiring" mode to program Rambo or Einsy, using the STK500v2 protocol (not the STK500). - // The Prusa's avrdude is patched to never send semicolons inside the data packets, as the USB to serial chip - // is flashed with a buggy firmware. - "-c", "wiring", - "-P", port->port, - "-b", "115200", // TODO: Allow other rates? Ditto elsewhere. - "-D", - "-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(), - }}; - - BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " - << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) { - return a + ' ' + b; - }); - - avrdude->push_args(std::move(args)); -} - -void FirmwareDialog::priv::prepare_mk2() -{ - if (! port) { return; } - - if (! check_model_id()) { - avrdude->cancel(); - return; - } - - prepare_common(); -} - -void FirmwareDialog::priv::prepare_mk3() -{ - if (! port) { return; } - - if (! check_model_id()) { - avrdude->cancel(); - return; - } - - prepare_common(); - - // The hex file also contains another section with l10n data to be flashed into the external flash on MK3 (Einsy) - // This is done via another avrdude invocation, here we build arg list for that: - std::vector<std::string> args {{ - extra_verbose ? "-vvvvv" : "-v", - "-p", "atmega2560", - // Using the "Arduino" mode to program Einsy's external flash with languages, using the STK500 protocol (not the STK500v2). - // The Prusa's avrdude is patched again to never send semicolons inside the data packets. - "-c", "arduino", - "-P", port->port, - "-b", "115200", - "-D", - "-u", // disable safe mode - "-U", (boost::format("flash:w:1:%1%:i") % hex_file.path.string()).str(), - }}; - - BOOST_LOG_TRIVIAL(info) << "Invoking avrdude for external flash flashing, arguments: " - << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) { - return a + ' ' + b; - }); - - avrdude->push_args(std::move(args)); -} - -void FirmwareDialog::priv::prepare_mm_control() -{ - port = boost::none; - lookup_port_mmu(); - if (! port) { - queue_error(_(L("The device could not have been found"))); - return; - } - - BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/3 at `%1%`, flashing ...") % port->port; - queue_status(label_status_flashing); - - std::vector<std::string> args {{ - extra_verbose ? "-vvvvv" : "-v", - "-p", "atmega32u4", - "-c", "avr109", - "-P", port->port, - "-b", "57600", - "-D", - "-U", (boost::format("flash:w:0:%1%:i") % hex_file.path.string()).str(), - }}; - - BOOST_LOG_TRIVIAL(info) << "Invoking avrdude, arguments: " - << std::accumulate(std::next(args.begin()), args.end(), args[0], [](std::string a, const std::string &b) { - return a + ' ' + b; - }); - - avrdude->push_args(std::move(args)); -} - - -void FirmwareDialog::priv::perform_upload() -{ - auto filename = hex_picker->GetPath(); - if (filename.IsEmpty()) { return; } - - load_hex_file(filename); // Might already be loaded, but we want to make sure it's fresh - - int selection = port_picker->GetSelection(); - if (selection != wxNOT_FOUND) { - port = this->ports[selection]; - - // Verify whether the combo box list selection equals to the combo box edit value. - if (wxString::FromUTF8(port->friendly_name.data()) != port_picker->GetValue()) { - return; - } - } - - const bool extra_verbose = false; // For debugging - - flashing_start(hex_file.device == HexFile::DEV_MK3 ? 2 : 1); - - // Init the avrdude object - AvrDude avrdude(avrdude_config); - - // It is ok here to use the q-pointer to the FirmwareDialog - // because the dialog ensures it doesn't exit before the background thread is done. - auto q = this->q; - - avrdude - .on_run([this](AvrDude::Ptr avrdude) { - this->avrdude = std::move(avrdude); - - try { - switch (this->hex_file.device) { - case HexFile::DEV_MK3: - this->prepare_mk3(); - break; - - case HexFile::DEV_MM_CONTROL: - this->prepare_mm_control(); - break; - - default: - this->prepare_mk2(); - break; - } - } catch (const std::exception &ex) { - queue_error(wxString::Format(_(L("Error accessing port at %s: %s")), port->port, ex.what())); - } - }) - .on_message(std::move([q, extra_verbose](const char *msg, unsigned /* size */) { - if (extra_verbose) { - BOOST_LOG_TRIVIAL(debug) << "avrdude: " << msg; - } - - auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); - auto wxmsg = wxString::FromUTF8(msg); - evt->SetExtraLong(AE_MESSAGE); - evt->SetString(std::move(wxmsg)); - wxQueueEvent(q, evt); - })) - .on_progress(std::move([q](const char * /* task */, unsigned progress) { - auto evt = new wxCommandEvent(EVT_AVRDUDE, q->GetId()); - evt->SetExtraLong(AE_PROGRESS); - evt->SetInt(progress); - wxQueueEvent(q, evt); - })) - .on_complete(std::move([this]() { - auto evt = new wxCommandEvent(EVT_AVRDUDE, this->q->GetId()); - evt->SetExtraLong(AE_EXIT); - evt->SetInt(this->avrdude->exit_code()); - wxQueueEvent(this->q, evt); - })) - .run(); -} - -void FirmwareDialog::priv::user_cancel() -{ - if (avrdude) { - user_cancelled = true; - avrdude->cancel(); - } -} - -void FirmwareDialog::priv::on_avrdude(const wxCommandEvent &evt) -{ - AvrDudeComplete complete_kind; - - switch (evt.GetExtraLong()) { - case AE_MESSAGE: - txt_stdout->AppendText(evt.GetString()); - break; - - case AE_PROGRESS: - // We try to track overall progress here. - // Avrdude performs 3 tasks per one memory operation ("-U" arg), - // first of which is reading of status data (very short). - // We use the timer_pulse during the very first task to indicate intialization - // and then display overall progress during the latter tasks. - - if (progress_tasks_done > 0) { - progressbar->SetValue(progress_tasks_bar + evt.GetInt()); - } - - if (evt.GetInt() == 100) { - timer_pulse.Stop(); - if (progress_tasks_done % 3 != 0) { - progress_tasks_bar += 100; - } - progress_tasks_done++; - } - - break; - - case AE_EXIT: - BOOST_LOG_TRIVIAL(info) << "avrdude exit code: " << evt.GetInt(); - - // Figure out the exit state - if (user_cancelled) { complete_kind = AC_USER_CANCELLED; } - else if (avrdude->cancelled()) { complete_kind = AC_NONE; } // Ie. cancelled programatically - else { complete_kind = evt.GetInt() == 0 ? AC_SUCCESS : AC_FAILURE; } - - flashing_done(complete_kind); - ensure_joined(); - break; - - case AE_STATUS: - set_txt_status(evt.GetString()); - break; - - default: - break; - } -} - -void FirmwareDialog::priv::on_async_dialog(const wxCommandEvent &evt) -{ - wxMessageDialog dlg(this->q, evt.GetString(), wxMessageBoxCaptionStr, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - { - std::lock_guard<std::mutex> lock(mutex); - modal_response = dlg.ShowModal(); - } - response_cv.notify_all(); -} - -void FirmwareDialog::priv::ensure_joined() -{ - // Make sure the background thread is collected and the AvrDude object reset - if (avrdude) { avrdude->join(); } - avrdude.reset(); -} - - -// Public - -FirmwareDialog::FirmwareDialog(wxWindow *parent) : - wxDialog(parent, wxID_ANY, _(L("Firmware flasher")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - p(new priv(this)) -{ - enum { - DIALOG_MARGIN = 15, - SPACING = 10, - MIN_WIDTH = 600, - MIN_HEIGHT = 200, - MIN_HEIGHT_EXPANDED = 500, - }; - - wxFont status_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - status_font.MakeBold(); - wxFont mono_font(wxFontInfo().Family(wxFONTFAMILY_TELETYPE)); - mono_font.MakeSmaller(); - - // Create GUI components and layout - - auto *panel = new wxPanel(this); - wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL); - panel->SetSizer(vsizer); - - auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:"))); - p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr, - "Hex files (*.hex)|*.hex|All files|*.*"); - - auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:"))); - p->port_picker = new wxComboBox(panel, wxID_ANY); - p->port_autodetect = new wxStaticText(panel, wxID_ANY, _(L("Autodetected"))); - p->btn_rescan = new wxButton(panel, wxID_ANY, _(L("Rescan"))); - auto *port_sizer = new wxBoxSizer(wxHORIZONTAL); - port_sizer->Add(p->port_picker, 1, wxEXPAND | wxRIGHT, SPACING); - port_sizer->Add(p->btn_rescan, 0); - port_sizer->Add(p->port_autodetect, 1, wxEXPAND); - p->enable_port_picker(true); - - auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:"))); - p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH); - - auto *label_status = new wxStaticText(panel, wxID_ANY, _(L("Status:"))); - p->txt_status = new wxStaticText(panel, wxID_ANY, _(L("Ready"))); - p->txt_status->SetFont(status_font); - - auto *grid = new wxFlexGridSizer(2, SPACING, SPACING); - grid->AddGrowableCol(1); - - grid->Add(label_hex_picker, 0, wxALIGN_CENTER_VERTICAL); - grid->Add(p->hex_picker, 0, wxEXPAND); - - grid->Add(label_port_picker, 0, wxALIGN_CENTER_VERTICAL); - grid->Add(port_sizer, 0, wxEXPAND); - - grid->Add(label_progress, 0, wxALIGN_CENTER_VERTICAL); - grid->Add(p->progressbar, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); - - grid->Add(label_status, 0, wxALIGN_CENTER_VERTICAL); - grid->Add(p->txt_status, 0, wxEXPAND); - - vsizer->Add(grid, 0, wxEXPAND | wxTOP | wxBOTTOM, SPACING); - - p->spoiler = new wxCollapsiblePane(panel, wxID_ANY, _(L("Advanced: avrdude output log")), wxDefaultPosition, wxDefaultSize, wxCP_DEFAULT_STYLE | wxCP_NO_TLW_RESIZE); - auto *spoiler_pane = p->spoiler->GetPane(); - auto *spoiler_sizer = new wxBoxSizer(wxVERTICAL); - p->txt_stdout = new wxTextCtrl(spoiler_pane, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE | wxTE_READONLY); - p->txt_stdout->SetFont(mono_font); - spoiler_sizer->Add(p->txt_stdout, 1, wxEXPAND); - spoiler_pane->SetSizer(spoiler_sizer); - // The doc says proportion need to be 0 for wxCollapsiblePane. - // Experience says it needs to be 1, otherwise things won't get sized properly. - vsizer->Add(p->spoiler, 1, wxEXPAND | wxBOTTOM, SPACING); - - p->btn_close = new wxButton(panel, wxID_CLOSE); - p->btn_flash = new wxButton(panel, wxID_ANY, p->btn_flash_label_ready); - p->btn_flash->Disable(); - auto *bsizer = new wxBoxSizer(wxHORIZONTAL); - bsizer->Add(p->btn_close); - bsizer->AddStretchSpacer(); - bsizer->Add(p->btn_flash); - vsizer->Add(bsizer, 0, wxEXPAND); - - auto *topsizer = new wxBoxSizer(wxVERTICAL); - topsizer->Add(panel, 1, wxEXPAND | wxALL, DIALOG_MARGIN); - SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT)); - SetSizerAndFit(topsizer); - const auto size = GetSize(); - SetSize(std::max(size.GetWidth(), static_cast<int>(MIN_WIDTH)), std::max(size.GetHeight(), static_cast<int>(MIN_HEIGHT))); - Layout(); - - // Bind events - - p->hex_picker->Bind(wxEVT_FILEPICKER_CHANGED, [this](wxFileDirPickerEvent& evt) { - if (wxFileExists(evt.GetPath())) { - this->p->load_hex_file(evt.GetPath()); - this->p->btn_flash->Enable(); - } - }); - - p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [this](wxCollapsiblePaneEvent &evt) { - if (evt.GetCollapsed()) { - this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT)); - const auto new_height = this->GetSize().GetHeight() - this->p->txt_stdout->GetSize().GetHeight(); - this->SetSize(this->GetSize().GetWidth(), new_height); - } else { - this->SetMinSize(wxSize(MIN_WIDTH, MIN_HEIGHT_EXPANDED)); - } - - this->Layout(); - this->p->fit_no_shrink(); - }); - - p->btn_close->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->Close(); }); - p->btn_rescan->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { this->p->find_serial_ports(); }); - - p->btn_flash->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { - if (this->p->avrdude) { - // Flashing is in progress, ask the user if they're really sure about canceling it - wxMessageDialog dlg(this, - _(L("Are you sure you want to cancel firmware flashing?\nThis could leave your printer in an unusable state!")), - _(L("Confirmation")), - wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - if (dlg.ShowModal() == wxID_YES) { - this->p->set_txt_status(_(L("Cancelling..."))); - this->p->user_cancel(); - } - } else { - // Start a flashing task - this->p->perform_upload(); - } - }); - - Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->p->progressbar->Pulse(); }); - - Bind(EVT_AVRDUDE, [this](wxCommandEvent &evt) { this->p->on_avrdude(evt); }); - Bind(EVT_ASYNC_DIALOG, [this](wxCommandEvent &evt) { this->p->on_async_dialog(evt); }); - - Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent &evt) { - if (this->p->avrdude) { - evt.Veto(); - } else { - evt.Skip(); - } - }); - - p->find_serial_ports(); -} - -FirmwareDialog::~FirmwareDialog() -{ - // Needed bacuse of forward defs -} - -void FirmwareDialog::run(wxWindow *parent) -{ - FirmwareDialog dialog(parent); - dialog.ShowModal(); -} - - -} diff --git a/xs/src/slic3r/GUI/FirmwareDialog.hpp b/xs/src/slic3r/GUI/FirmwareDialog.hpp deleted file mode 100644 index ad048bf5d..000000000 --- a/xs/src/slic3r/GUI/FirmwareDialog.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef slic3r_FirmwareDialog_hpp_ -#define slic3r_FirmwareDialog_hpp_ - -#include <memory> - -#include <wx/dialog.h> - - -namespace Slic3r { - - -class FirmwareDialog: public wxDialog -{ -public: - FirmwareDialog(wxWindow *parent); - FirmwareDialog(FirmwareDialog &&) = delete; - FirmwareDialog(const FirmwareDialog &) = delete; - FirmwareDialog &operator=(FirmwareDialog &&) = delete; - FirmwareDialog &operator=(const FirmwareDialog &) = delete; - ~FirmwareDialog(); - - static void run(wxWindow *parent); -private: - struct priv; - std::unique_ptr<priv> p; -}; - - -} - -#endif diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp deleted file mode 100644 index cb3250916..000000000 --- a/xs/src/slic3r/GUI/GLCanvas3D.cpp +++ /dev/null @@ -1,5522 +0,0 @@ -#include "GLCanvas3D.hpp" - -#include "../../admesh/stl.h" -#include "../../libslic3r/libslic3r.h" -#include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLShader.hpp" -#include "../../slic3r/GUI/GUI.hpp" -#include "../../slic3r/GUI/PresetBundle.hpp" -#include "../../slic3r/GUI/GLGizmo.hpp" -#include "../../libslic3r/ClipperUtils.hpp" -#include "../../libslic3r/PrintConfig.hpp" -#include "../../libslic3r/GCode/PreviewData.hpp" - -#include <GL/glew.h> - -#include <wx/glcanvas.h> -#include <wx/timer.h> -#include <wx/bitmap.h> -#include <wx/dcmemory.h> -#include <wx/image.h> -#include <wx/settings.h> - -// Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. -#include "../../libslic3r/Print.hpp" - -#include <tbb/parallel_for.h> -#include <tbb/spin_mutex.h> - -#include <boost/log/trivial.hpp> -#include <boost/algorithm/string/predicate.hpp> - -#include <iostream> -#include <float.h> -#include <algorithm> - -static const float TRACKBALLSIZE = 0.8f; -static const float GIMBALL_LOCK_THETA_MAX = 180.0f; -static const float GROUND_Z = -0.02f; - -// phi / theta angles to orient the camera. -static const float VIEW_DEFAULT[2] = { 45.0f, 45.0f }; -static const float VIEW_LEFT[2] = { 90.0f, 90.0f }; -static const float VIEW_RIGHT[2] = { -90.0f, 90.0f }; -static const float VIEW_TOP[2] = { 0.0f, 0.0f }; -static const float VIEW_BOTTOM[2] = { 0.0f, 180.0f }; -static const float VIEW_FRONT[2] = { 0.0f, 90.0f }; -static const float VIEW_REAR[2] = { 180.0f, 90.0f }; - -static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f; -static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f; - -static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f }; - -static const float DEFAULT_BG_COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f }; -static const float ERROR_BG_COLOR[3] = { 144.0f / 255.0f, 49.0f / 255.0f, 10.0f / 255.0f }; - -namespace Slic3r { -namespace GUI { - -bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords) -{ - m_vertices.clear(); - m_tex_coords.clear(); - - unsigned int v_size = 9 * (unsigned int)triangles.size(); - unsigned int t_size = 6 * (unsigned int)triangles.size(); - if (v_size == 0) - return false; - - m_vertices = std::vector<float>(v_size, 0.0f); - if (generate_tex_coords) - m_tex_coords = std::vector<float>(t_size, 0.0f); - - float min_x = unscale<float>(triangles[0].points[0](0)); - float min_y = unscale<float>(triangles[0].points[0](1)); - float max_x = min_x; - float max_y = min_y; - - unsigned int v_coord = 0; - unsigned int t_coord = 0; - for (const Polygon& t : triangles) - { - for (unsigned int v = 0; v < 3; ++v) - { - const Point& p = t.points[v]; - float x = unscale<float>(p(0)); - float y = unscale<float>(p(1)); - - m_vertices[v_coord++] = x; - m_vertices[v_coord++] = y; - m_vertices[v_coord++] = z; - - if (generate_tex_coords) - { - m_tex_coords[t_coord++] = x; - m_tex_coords[t_coord++] = y; - - min_x = std::min(min_x, x); - max_x = std::max(max_x, x); - min_y = std::min(min_y, y); - max_y = std::max(max_y, y); - } - } - } - - if (generate_tex_coords) - { - float size_x = max_x - min_x; - float size_y = max_y - min_y; - - if ((size_x != 0.0f) && (size_y != 0.0f)) - { - float inv_size_x = 1.0f / size_x; - float inv_size_y = -1.0f / size_y; - for (unsigned int i = 0; i < m_tex_coords.size(); i += 2) - { - m_tex_coords[i] *= inv_size_x; - m_tex_coords[i + 1] *= inv_size_y; - } - } - } - - return true; -} - -bool GeometryBuffer::set_from_lines(const Lines& lines, float z) -{ - m_vertices.clear(); - m_tex_coords.clear(); - - unsigned int size = 6 * (unsigned int)lines.size(); - if (size == 0) - return false; - - m_vertices = std::vector<float>(size, 0.0f); - - unsigned int coord = 0; - for (const Line& l : lines) - { - m_vertices[coord++] = unscale<float>(l.a(0)); - m_vertices[coord++] = unscale<float>(l.a(1)); - m_vertices[coord++] = z; - m_vertices[coord++] = unscale<float>(l.b(0)); - m_vertices[coord++] = unscale<float>(l.b(1)); - m_vertices[coord++] = z; - } - - return true; -} - -const float* GeometryBuffer::get_vertices() const -{ - return m_vertices.data(); -} - -const float* GeometryBuffer::get_tex_coords() const -{ - return m_tex_coords.data(); -} - -unsigned int GeometryBuffer::get_vertices_count() const -{ - return (unsigned int)m_vertices.size() / 3; -} - -Size::Size() - : m_width(0) - , m_height(0) -{ -} - -Size::Size(int width, int height) - : m_width(width) - , m_height(height) -{ -} - -int Size::get_width() const -{ - return m_width; -} - -void Size::set_width(int width) -{ - m_width = width; -} - -int Size::get_height() const -{ - return m_height; -} - -void Size::set_height(int height) -{ - m_height = height; -} - -Rect::Rect() - : m_left(0.0f) - , m_top(0.0f) - , m_right(0.0f) - , m_bottom(0.0f) -{ -} - -Rect::Rect(float left, float top, float right, float bottom) - : m_left(left) - , m_top(top) - , m_right(right) - , m_bottom(bottom) -{ -} - -float Rect::get_left() const -{ - return m_left; -} - -void Rect::set_left(float left) -{ - m_left = left; -} - -float Rect::get_top() const -{ - return m_top; -} - -void Rect::set_top(float top) -{ - m_top = top; -} - -float Rect::get_right() const -{ - return m_right; -} - -void Rect::set_right(float right) -{ - m_right = right; -} - -float Rect::get_bottom() const -{ - return m_bottom; -} - -void Rect::set_bottom(float bottom) -{ - m_bottom = bottom; -} - -GLCanvas3D::Camera::Camera() - : type(Ortho) - , zoom(1.0f) - , phi(45.0f) -// , distance(0.0f) - , target(0.0, 0.0, 0.0) - , m_theta(45.0f) -{ -} - -std::string GLCanvas3D::Camera::get_type_as_string() const -{ - switch (type) - { - default: - case Unknown: - return "unknown"; -// case Perspective: -// return "perspective"; - case Ortho: - return "ortho"; - }; -} - -float GLCanvas3D::Camera::get_theta() const -{ - return m_theta; -} - -void GLCanvas3D::Camera::set_theta(float theta) -{ - m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); -} - -GLCanvas3D::Bed::Bed() - : m_type(Custom) -{ -} - -bool GLCanvas3D::Bed::is_prusa() const -{ - return (m_type == MK2) || (m_type == MK3); -} - -bool GLCanvas3D::Bed::is_custom() const -{ - return m_type == Custom; -} - -const Pointfs& GLCanvas3D::Bed::get_shape() const -{ - return m_shape; -} - -bool GLCanvas3D::Bed::set_shape(const Pointfs& shape) -{ - EType new_type = _detect_type(); - if (m_shape == shape && m_type == new_type) - // No change, no need to update the UI. - return false; - m_shape = shape; - m_type = new_type; - - _calc_bounding_box(); - - ExPolygon poly; - for (const Vec2d& p : m_shape) - { - poly.contour.append(Point(scale_(p(0)), scale_(p(1)))); - } - - _calc_triangles(poly); - - const BoundingBox& bed_bbox = poly.contour.bounding_box(); - _calc_gridlines(poly, bed_bbox); - - m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour; - // Let the calee to update the UI. - return true; -} - -const BoundingBoxf3& GLCanvas3D::Bed::get_bounding_box() const -{ - return m_bounding_box; -} - -bool GLCanvas3D::Bed::contains(const Point& point) const -{ - return m_polygon.contains(point); -} - -Point GLCanvas3D::Bed::point_projection(const Point& point) const -{ - return m_polygon.point_projection(point); -} - -void GLCanvas3D::Bed::render(float theta) const -{ - switch (m_type) - { - case MK2: - { - _render_mk2(theta); - break; - } - case MK3: - { - _render_mk3(theta); - break; - } - default: - case Custom: - { - _render_custom(); - break; - } - } -} - -void GLCanvas3D::Bed::_calc_bounding_box() -{ - m_bounding_box = BoundingBoxf3(); - for (const Vec2d& p : m_shape) - { - m_bounding_box.merge(Vec3d(p(0), p(1), 0.0)); - } -} - -void GLCanvas3D::Bed::_calc_triangles(const ExPolygon& poly) -{ - Polygons triangles; - poly.triangulate(&triangles); - - if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom)) - printf("Unable to create bed triangles\n"); -} - -void GLCanvas3D::Bed::_calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox) -{ - Polylines axes_lines; - for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) - { - Polyline line; - line.append(Point(x, bed_bbox.min(1))); - line.append(Point(x, bed_bbox.max(1))); - axes_lines.push_back(line); - } - for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) - { - Polyline line; - line.append(Point(bed_bbox.min(0), y)); - line.append(Point(bed_bbox.max(0), y)); - axes_lines.push_back(line); - } - - // clip with a slightly grown expolygon because our lines lay on the contours and may get erroneously clipped - Lines gridlines = to_lines(intersection_pl(axes_lines, offset(poly, SCALED_EPSILON))); - - // append bed contours - Lines contour_lines = to_lines(poly); - std::copy(contour_lines.begin(), contour_lines.end(), std::back_inserter(gridlines)); - - if (!m_gridlines.set_from_lines(gridlines, GROUND_Z)) - printf("Unable to create bed grid lines\n"); -} - -GLCanvas3D::Bed::EType GLCanvas3D::Bed::_detect_type() const -{ - EType type = Custom; - - const PresetBundle* bundle = get_preset_bundle(); - if (bundle != nullptr) - { - const Preset* curr = &bundle->printers.get_selected_preset(); - while (curr != nullptr) - { - if (curr->config.has("bed_shape") && _are_equal(m_shape, dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values)) - { - if ((curr->vendor != nullptr) && (curr->vendor->name == "Prusa Research")) - { - if (boost::contains(curr->name, "MK2")) - { - type = MK2; - break; - } - else if (boost::contains(curr->name, "MK3")) - { - type = MK3; - break; - } - } - } - - curr = bundle->printers.get_preset_parent(*curr); - } - } - - return type; -} - -void GLCanvas3D::Bed::_render_mk2(float theta) const -{ - std::string filename = resources_dir() + "/icons/bed/mk2_top.png"; - if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) - { - if (!m_top_texture.load_from_file(filename, true)) - { - _render_custom(); - return; - } - } - - filename = resources_dir() + "/icons/bed/mk2_bottom.png"; - if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) - { - if (!m_bottom_texture.load_from_file(filename, true)) - { - _render_custom(); - return; - } - } - - _render_prusa(theta); -} - -void GLCanvas3D::Bed::_render_mk3(float theta) const -{ - std::string filename = resources_dir() + "/icons/bed/mk3_top.png"; - if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) - { - if (!m_top_texture.load_from_file(filename, true)) - { - _render_custom(); - return; - } - } - - filename = resources_dir() + "/icons/bed/mk3_bottom.png"; - if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) - { - if (!m_bottom_texture.load_from_file(filename, true)) - { - _render_custom(); - return; - } - } - - _render_prusa(theta); -} - -void GLCanvas3D::Bed::_render_prusa(float theta) const -{ - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) - { - ::glEnable(GL_DEPTH_TEST); - ::glDepthMask(GL_FALSE); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnable(GL_TEXTURE_2D); - ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_TEXTURE_COORD_ARRAY); - - if (theta > 90.0f) - ::glFrontFace(GL_CW); - - ::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); - ::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()); - ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); - - if (theta > 90.0f) - ::glFrontFace(GL_CCW); - - ::glBindTexture(GL_TEXTURE_2D, 0); - ::glDisableClientState(GL_TEXTURE_COORD_ARRAY); - ::glDisableClientState(GL_VERTEX_ARRAY); - - ::glDisable(GL_TEXTURE_2D); - - ::glDisable(GL_BLEND); - ::glDepthMask(GL_TRUE); - } -} - -void GLCanvas3D::Bed::_render_custom() const -{ - m_top_texture.reset(); - m_bottom_texture.reset(); - - unsigned int triangles_vcount = m_triangles.get_vertices_count(); - if (triangles_vcount > 0) - { - ::glEnable(GL_LIGHTING); - ::glDisable(GL_DEPTH_TEST); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnableClientState(GL_VERTEX_ARRAY); - - ::glColor4f(0.8f, 0.6f, 0.5f, 0.4f); - ::glNormal3d(0.0f, 0.0f, 1.0f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()); - ::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount); - - // draw grid - unsigned int gridlines_vcount = m_gridlines.get_vertices_count(); - - // we need depth test for grid, otherwise it would disappear when looking the object from below - ::glEnable(GL_DEPTH_TEST); - ::glLineWidth(3.0f); - ::glColor4f(0.2f, 0.2f, 0.2f, 0.4f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()); - ::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount); - - ::glDisableClientState(GL_VERTEX_ARRAY); - - ::glDisable(GL_BLEND); - ::glDisable(GL_LIGHTING); - } -} - -bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) -{ - if (bed_1.size() != bed_2.size()) - return false; - - for (unsigned int i = 0; i < (unsigned int)bed_1.size(); ++i) - { - if (bed_1[i] != bed_2[i]) - return false; - } - - return true; -} - -GLCanvas3D::Axes::Axes() - : origin(0, 0, 0), length(0.0f) -{ -} - -void GLCanvas3D::Axes::render(bool depth_test) const -{ - if (depth_test) - ::glEnable(GL_DEPTH_TEST); - else - ::glDisable(GL_DEPTH_TEST); - - ::glLineWidth(2.0f); - ::glBegin(GL_LINES); - // draw line for x axis - ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2)); - ::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2)); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2)); - ::glEnd(); - // draw line for Z axis - // (re-enable depth test so that axis is correctly shown when objects are behind it) - if (!depth_test) - ::glEnable(GL_DEPTH_TEST); - - ::glBegin(GL_LINES); - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2)); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length); - ::glEnd(); -} - -GLCanvas3D::CuttingPlane::CuttingPlane() - : m_z(-1.0f) -{ -} - -bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons) -{ - m_z = z; - - // grow slices in order to display them better - ExPolygons expolygons = offset_ex(polygons, scale_(0.1)); - Lines lines = to_lines(expolygons); - return m_lines.set_from_lines(lines, m_z); -} - -void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const -{ - _render_plane(bb); - _render_contour(); -} - -void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) const -{ - if (m_z >= 0.0f) - { - ::glDisable(GL_CULL_FACE); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - float margin = 20.0f; - float min_x = bb.min(0) - margin; - float max_x = bb.max(0) + margin; - float min_y = bb.min(1) - margin; - float max_y = bb.max(1) + margin; - - ::glBegin(GL_QUADS); - ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); - ::glVertex3f(min_x, min_y, m_z); - ::glVertex3f(max_x, min_y, m_z); - ::glVertex3f(max_x, max_y, m_z); - ::glVertex3f(min_x, max_y, m_z); - ::glEnd(); - - ::glEnable(GL_CULL_FACE); - ::glDisable(GL_BLEND); - } -} - -void GLCanvas3D::CuttingPlane::_render_contour() const -{ - ::glEnableClientState(GL_VERTEX_ARRAY); - - if (m_z >= 0.0f) - { - unsigned int lines_vcount = m_lines.get_vertices_count(); - - ::glLineWidth(2.0f); - ::glColor3f(0.0f, 0.0f, 0.0f); - ::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_vertices()); - ::glDrawArrays(GL_LINES, 0, (GLsizei)lines_vcount); - } - - ::glDisableClientState(GL_VERTEX_ARRAY); -} - -GLCanvas3D::Shader::Shader() - : m_shader(nullptr) -{ -} - -GLCanvas3D::Shader::~Shader() -{ - _reset(); -} - -bool GLCanvas3D::Shader::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) -{ - if (is_initialized()) - return true; - - m_shader = new GLShader(); - if (m_shader != nullptr) - { - if (!m_shader->load_from_file(fragment_shader_filename.c_str(), vertex_shader_filename.c_str())) - { - std::cout << "Compilaton of shader failed:" << std::endl; - std::cout << m_shader->last_error << std::endl; - _reset(); - return false; - } - } - - return true; -} - -bool GLCanvas3D::Shader::is_initialized() const -{ - return (m_shader != nullptr); -} - -bool GLCanvas3D::Shader::start_using() const -{ - if (is_initialized()) - { - m_shader->enable(); - return true; - } - else - return false; -} - -void GLCanvas3D::Shader::stop_using() const -{ - if (m_shader != nullptr) - m_shader->disable(); -} - -void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), value); -} - -void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const -{ - if (m_shader != nullptr) - m_shader->set_uniform(name.c_str(), matrix); -} - -const GLShader* GLCanvas3D::Shader::get_shader() const -{ - return m_shader; -} - -void GLCanvas3D::Shader::_reset() -{ - if (m_shader != nullptr) - { - m_shader->release(); - delete m_shader; - m_shader = nullptr; - } -} - -GLCanvas3D::LayersEditing::LayersEditing() - : m_use_legacy_opengl(false) - , m_enabled(false) - , m_z_texture_id(0) - , state(Unknown) - , band_width(2.0f) - , strength(0.005f) - , last_object_id(-1) - , last_z(0.0f) - , last_action(0) -{ -} - -GLCanvas3D::LayersEditing::~LayersEditing() -{ - if (m_z_texture_id != 0) - { - ::glDeleteTextures(1, &m_z_texture_id); - m_z_texture_id = 0; - } -} - -bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename) -{ - if (!m_shader.init(vertex_shader_filename, fragment_shader_filename)) - return false; - - ::glGenTextures(1, (GLuint*)&m_z_texture_id); - ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - ::glBindTexture(GL_TEXTURE_2D, 0); - - return true; -} - -bool GLCanvas3D::LayersEditing::is_allowed() const -{ - return !m_use_legacy_opengl && m_shader.is_initialized(); -} - -void GLCanvas3D::LayersEditing::set_use_legacy_opengl(bool use_legacy_opengl) -{ - m_use_legacy_opengl = use_legacy_opengl; -} - -bool GLCanvas3D::LayersEditing::is_enabled() const -{ - return m_enabled; -} - -void GLCanvas3D::LayersEditing::set_enabled(bool enabled) -{ - m_enabled = is_allowed() && enabled; -} - -unsigned int GLCanvas3D::LayersEditing::get_z_texture_id() const -{ - return m_z_texture_id; -} - -void GLCanvas3D::LayersEditing::render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const -{ - if (!m_enabled) - return; - - const Rect& bar_rect = get_bar_rect_viewport(canvas); - const Rect& reset_rect = get_reset_rect_viewport(canvas); - - ::glDisable(GL_DEPTH_TEST); - - // The viewport and camera are set to complete view and glOrtho(-$x / 2, $x / 2, -$y / 2, $y / 2, -$depth, $depth), - // where x, y is the window size divided by $self->_zoom. - ::glPushMatrix(); - ::glLoadIdentity(); - - _render_tooltip_texture(canvas, bar_rect, reset_rect); - _render_reset_texture(reset_rect); - _render_active_object_annotations(canvas, volume, print_object, bar_rect); - _render_profile(print_object, bar_rect); - - // Revert the matrices. - ::glPopMatrix(); - - ::glEnable(GL_DEPTH_TEST); -} - -int GLCanvas3D::LayersEditing::get_shader_program_id() const -{ - const GLShader* shader = m_shader.get_shader(); - return (shader != nullptr) ? shader->shader_program_id : -1; -} - -float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas) -{ - const Point& mouse_pos = canvas.get_local_mouse_position(); - const Rect& rect = get_bar_rect_screen(canvas); - float x = (float)mouse_pos(0); - float y = (float)mouse_pos(1); - float t = rect.get_top(); - float b = rect.get_bottom(); - - return ((rect.get_left() <= x) && (x <= rect.get_right()) && (t <= y) && (y <= b)) ? - // Inside the bar. - (b - y - 1.0f) / (b - t - 1.0f) : - // Outside the bar. - -1000.0f; -} - -bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, float x, float y) -{ - const Rect& rect = get_bar_rect_screen(canvas); - return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom()); -} - -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()); -} - -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(); - - return Rect(w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0.0f, w, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); -} - -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 - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, h - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, w, h); -} - -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 = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom); -} - -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 = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - return Rect((half_w - VARIABLE_LAYER_THICKNESS_BAR_WIDTH) * inv_zoom, (-half_h + VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom); -} - - -bool GLCanvas3D::LayersEditing::_is_initialized() const -{ - return m_shader.is_initialized(); -} - -void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const -{ - 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)) - return; - } - - float zoom = canvas.get_camera_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 - (float)m_tooltip_texture.get_width() * inv_zoom - gap; - float r = bar_left - gap; - float t = reset_bottom + (float)m_tooltip_texture.get_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)) - 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()); -} - -void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const -{ - float max_z = print_object.model_object()->bounding_box().max(2); - - m_shader.start_using(); - - m_shader.set_uniform("z_to_texture_row", (float)volume.layer_height_texture_z_to_row_id()); - m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height()); - m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas)); - m_shader.set_uniform("z_cursor_band_width", band_width); - // The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix - m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX); - - GLsizei w = (GLsizei)volume.layer_height_texture_width(); - GLsizei h = (GLsizei)volume.layer_height_texture_height(); - GLsizei half_w = w / 2; - GLsizei half_h = h / 2; - - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - ::glBindTexture(GL_TEXTURE_2D, m_z_texture_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - ::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - ::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0()); - ::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1()); - - // Render the color bar - float l = bar_rect.get_left(); - float r = bar_rect.get_right(); - float t = bar_rect.get_top(); - float b = bar_rect.get_bottom(); - - ::glBegin(GL_QUADS); - ::glVertex3f(l, b, 0.0f); - ::glVertex3f(r, b, 0.0f); - ::glVertex3f(r, t, max_z); - ::glVertex3f(l, t, max_z); - ::glEnd(); - ::glBindTexture(GL_TEXTURE_2D, 0); - - m_shader.stop_using(); -} - -void GLCanvas3D::LayersEditing::_render_profile(const PrintObject& print_object, const Rect& bar_rect) const -{ - // FIXME show some kind of legend. - - // Get a maximum layer height value. - // FIXME This is a duplicate code of Slicing.cpp. - double layer_height_max = DBL_MAX; - const PrintConfig& print_config = print_object.print()->config(); - const std::vector<double>& nozzle_diameters = dynamic_cast<const ConfigOptionFloats*>(print_config.option("nozzle_diameter"))->values; - const std::vector<double>& layer_heights_min = dynamic_cast<const ConfigOptionFloats*>(print_config.option("min_layer_height"))->values; - const std::vector<double>& layer_heights_max = dynamic_cast<const ConfigOptionFloats*>(print_config.option("max_layer_height"))->values; - for (unsigned int i = 0; i < (unsigned int)nozzle_diameters.size(); ++i) - { - double lh_min = (layer_heights_min[i] == 0.0) ? 0.07 : std::max(0.01, layer_heights_min[i]); - double lh_max = (layer_heights_max[i] == 0.0) ? (0.75 * nozzle_diameters[i]) : layer_heights_max[i]; - layer_height_max = std::min(layer_height_max, std::max(lh_min, lh_max)); - } - - // Make the vertical bar a bit wider so the layer height curve does not touch the edge of the bar region. - layer_height_max *= 1.12; - - double max_z = unscale<double>(print_object.size(2)); - double layer_height = dynamic_cast<const ConfigOptionFloat*>(print_object.config().option("layer_height"))->value; - float l = bar_rect.get_left(); - float w = bar_rect.get_right() - l; - float b = bar_rect.get_bottom(); - float t = bar_rect.get_top(); - float h = t - b; - float scale_x = w / (float)layer_height_max; - float scale_y = h / (float)max_z; - float x = l + (float)layer_height * scale_x; - - // Baseline - ::glColor3f(0.0f, 0.0f, 0.0f); - ::glBegin(GL_LINE_STRIP); - ::glVertex2f(x, b); - ::glVertex2f(x, t); - ::glEnd(); - - // Curve - const ModelObject* model_object = print_object.model_object(); - if (model_object->layer_height_profile_valid) - { - const std::vector<double>& profile = model_object->layer_height_profile; - - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i < profile.size(); i += 2) - { - ::glVertex2f(l + (float)profile[i + 1] * scale_x, b + (float)profile[i] * scale_y); - } - ::glEnd(); - } -} - -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); - -GLCanvas3D::Mouse::Drag::Drag() - : start_position_2D(Invalid_2D_Point) - , start_position_3D(Invalid_3D_Point) - , volume_center_offset(0, 0, 0) - , move_with_shift(false) - , move_volume_idx(-1) - , gizmo_volume_idx(-1) -{ -} - -GLCanvas3D::Mouse::Mouse() - : dragging(false) - , position(DBL_MAX, DBL_MAX) -{ -} - -void GLCanvas3D::Mouse::set_start_position_2D_as_invalid() -{ - drag.start_position_2D = Drag::Invalid_2D_Point; -} - -void GLCanvas3D::Mouse::set_start_position_3D_as_invalid() -{ - drag.start_position_3D = Drag::Invalid_3D_Point; -} - -bool GLCanvas3D::Mouse::is_start_position_2D_defined() const -{ - return (drag.start_position_2D != Drag::Invalid_2D_Point); -} - -bool GLCanvas3D::Mouse::is_start_position_3D_defined() const -{ - return (drag.start_position_3D != Drag::Invalid_3D_Point); -} - -const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f; -const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale; -const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale; - -GLCanvas3D::Gizmos::Gizmos() - : m_enabled(false) - , m_current(Undefined) -{ -} - -GLCanvas3D::Gizmos::~Gizmos() -{ - _reset(); -} - -bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) -{ - GLGizmoBase* gizmo = new GLGizmoMove3D(parent); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) - return false; - -#if !ENABLE_MODELINSTANCE_3D_OFFSET - // temporary disable z grabber - gizmo->disable_grabber(2); -#endif // !ENABLE_MODELINSTANCE_3D_OFFSET - - m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); - - gizmo = new GLGizmoScale3D(parent); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) - return false; - - // temporary disable x grabbers - gizmo->disable_grabber(0); - gizmo->disable_grabber(1); - // temporary disable y grabbers - gizmo->disable_grabber(2); - gizmo->disable_grabber(3); - // temporary disable z grabbers - gizmo->disable_grabber(4); - gizmo->disable_grabber(5); - - m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); - - gizmo = new GLGizmoRotate3D(parent); - if (gizmo == nullptr) - { - _reset(); - return false; - } - - if (!gizmo->init()) - { - _reset(); - return false; - } - - // temporary disable x and y grabbers - gizmo->disable_grabber(0); - gizmo->disable_grabber(1); - - m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); - - gizmo = new GLGizmoFlatten(parent); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) { - _reset(); - return false; - } - - m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); - - - return true; -} - -bool GLCanvas3D::Gizmos::is_enabled() const -{ - return m_enabled; -} - -void GLCanvas3D::Gizmos::set_enabled(bool enable) -{ - m_enabled = enable; -} - -void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) -{ - if (!m_enabled) - return; - - float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height); - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if (it->second == nullptr) - continue; - - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; - float half_tex_size = 0.5f * tex_size; - - // we currently use circular icons for gizmo, so we check the radius - if (it->second->get_state() != GLGizmoBase::On) - { - bool inside = (mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size; - it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); - } - top_y += (tex_size + OverlayGapY); - } -} - -void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) -{ - if (!m_enabled) - return; - - float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height); - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if (it->second == nullptr) - continue; - - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; - float half_tex_size = 0.5f * tex_size; - - // we currently use circular icons for gizmo, so we check the radius - if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size) - { - if ((it->second->get_state() == GLGizmoBase::On)) - { - it->second->set_state(GLGizmoBase::Off); - m_current = Undefined; - } - else - { - it->second->set_state(GLGizmoBase::On); - m_current = it->first; - } - } - else - it->second->set_state(GLGizmoBase::Off); - - top_y += (tex_size + OverlayGapY); - } -} - -void GLCanvas3D::Gizmos::reset_all_states() -{ - if (!m_enabled) - return; - - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if (it->second != nullptr) - { - it->second->set_state(GLGizmoBase::Off); - it->second->set_hover_id(-1); - } - } - - m_current = Undefined; -} - -void GLCanvas3D::Gizmos::set_hover_id(int id) -{ - if (!m_enabled) - return; - - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On)) - it->second->set_hover_id(id); - } -} - -bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const -{ - if (!m_enabled) - return false; - - float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height); - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if (it->second == nullptr) - continue; - - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale; - float half_tex_size = 0.5f * tex_size; - - // we currently use circular icons for gizmo, so we check the radius - if ((mouse_pos - Vec2d(OverlayOffsetX + half_tex_size, top_y + half_tex_size)).norm() < half_tex_size) - return true; - - top_y += (tex_size + OverlayGapY); - } - - return false; -} - -bool GLCanvas3D::Gizmos::grabber_contains_mouse() const -{ - if (!m_enabled) - return false; - - GLGizmoBase* curr = _get_current(); - return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; -} - -void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray) -{ - if (!m_enabled) - return; - - GLGizmoBase* curr = _get_current(); - if (curr != nullptr) - curr->update(mouse_ray); -} - -GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const -{ - return m_current; -} - -bool GLCanvas3D::Gizmos::is_running() const -{ - if (!m_enabled) - return false; - - GLGizmoBase* curr = _get_current(); - return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; -} - -bool GLCanvas3D::Gizmos::is_dragging() const -{ - GLGizmoBase* curr = _get_current(); - return (curr != nullptr) ? curr->is_dragging() : false; -} - -void GLCanvas3D::Gizmos::start_dragging(const BoundingBoxf3& box) -{ - GLGizmoBase* curr = _get_current(); - if (curr != nullptr) - curr->start_dragging(box); -} - -void GLCanvas3D::Gizmos::stop_dragging() -{ - GLGizmoBase* curr = _get_current(); - if (curr != nullptr) - curr->stop_dragging(); -} - -Vec3d GLCanvas3D::Gizmos::get_position() const -{ - if (!m_enabled) - return Vec3d::Zero(); - - GizmosMap::const_iterator it = m_gizmos.find(Move); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoMove3D*>(it->second)->get_position() : Vec3d::Zero(); -} - -void GLCanvas3D::Gizmos::set_position(const Vec3d& position) -{ - if (!m_enabled) - return; - - GizmosMap::const_iterator it = m_gizmos.find(Move); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoMove3D*>(it->second)->set_position(position); -} - -float GLCanvas3D::Gizmos::get_scale() const -{ - if (!m_enabled) - return 1.0f; - - GizmosMap::const_iterator it = m_gizmos.find(Scale); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale3D*>(it->second)->get_scale_x() : 1.0f; -} - -void GLCanvas3D::Gizmos::set_scale(float scale) -{ - if (!m_enabled) - return; - - GizmosMap::const_iterator it = m_gizmos.find(Scale); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoScale3D*>(it->second)->set_scale(scale); -} - -float GLCanvas3D::Gizmos::get_angle_z() const -{ - if (!m_enabled) - return 0.0f; - - GizmosMap::const_iterator it = m_gizmos.find(Rotate); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate3D*>(it->second)->get_angle_z() : 0.0f; -} - -void GLCanvas3D::Gizmos::set_angle_z(float angle_z) -{ - if (!m_enabled) - return; - - GizmosMap::const_iterator it = m_gizmos.find(Rotate); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoRotate3D*>(it->second)->set_angle_z(angle_z); -} - -Vec3d GLCanvas3D::Gizmos::get_flattening_normal() const -{ - if (!m_enabled) - return Vec3d::Zero(); - - GizmosMap::const_iterator it = m_gizmos.find(Flatten); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoFlatten*>(it->second)->get_flattening_normal() : Vec3d::Zero(); -} - -void GLCanvas3D::Gizmos::set_flattening_data(const ModelObject* model_object) -{ - if (!m_enabled) - return; - - GizmosMap::const_iterator it = m_gizmos.find(Flatten); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object); -} - -void GLCanvas3D::Gizmos::render_current_gizmo(const BoundingBoxf3& box) const -{ - if (!m_enabled) - return; - - ::glDisable(GL_DEPTH_TEST); - - if (box.radius() > 0.0) - _render_current_gizmo(box); -} - -void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const -{ - if (!m_enabled) - return; - - GLGizmoBase* curr = _get_current(); - if (curr != nullptr) - curr->render_for_picking(box); -} - -void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas) const -{ - if (!m_enabled) - return; - - ::glDisable(GL_DEPTH_TEST); - - ::glPushMatrix(); - ::glLoadIdentity(); - - _render_overlay(canvas); - - ::glPopMatrix(); -} - -void GLCanvas3D::Gizmos::_reset() -{ - for (GizmosMap::value_type& gizmo : m_gizmos) - { - delete gizmo.second; - gizmo.second = nullptr; - } - - m_gizmos.clear(); -} - -void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const -{ - if (m_gizmos.empty()) - return; - - float cnv_w = (float)canvas.get_canvas_size().get_width(); - float zoom = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - float height = _get_total_overlay_height(); - float top_x = (OverlayOffsetX - 0.5f * cnv_w) * inv_zoom; - float top_y = 0.5f * height * inv_zoom; - float scaled_gap_y = OverlayGapY * inv_zoom; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom; - GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y); - top_y -= (tex_size + scaled_gap_y); - } -} - -void GLCanvas3D::Gizmos::_render_current_gizmo(const BoundingBoxf3& box) const -{ - GLGizmoBase* curr = _get_current(); - if (curr != nullptr) - curr->render(box); -} - -float GLCanvas3D::Gizmos::_get_total_overlay_height() const -{ - float height = 0.0f; - - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - height += (float)it->second->get_textures_size(); - if (std::distance(it, m_gizmos.end()) > 1) - height += OverlayGapY; - } - - return height; -} - -const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 }; -const unsigned char GLCanvas3D::WarningTexture::Opacity = 255; - -GLCanvas3D::WarningTexture::WarningTexture() - : GUI::GLTexture() - , m_original_width(0) - , m_original_height(0) -{ -} - -bool GLCanvas3D::WarningTexture::generate(const std::string& msg) -{ - reset(); - - if (msg.empty()) - return false; - - wxMemoryDC memDC; - // select default font - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - font.MakeLarger(); - font.MakeBold(); - memDC.SetFont(font); - - // calculates texture size - wxCoord w, h; - memDC.GetTextExtent(msg, &w, &h); - - int pow_of_two_size = next_highest_power_of_2(std::max<unsigned int>(w, h)); - - m_original_width = (int)w; - m_original_height = (int)h; - m_width = pow_of_two_size; - m_height = pow_of_two_size; - - // generates bitmap - wxBitmap bitmap(m_width, m_height); - -#if defined(__APPLE__) || defined(_MSC_VER) - bitmap.UseAlpha(); -#endif - - memDC.SelectObject(bitmap); - memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); - memDC.Clear(); - - memDC.SetTextForeground(*wxWHITE); - - // draw message - memDC.DrawText(msg, 0, 0); - - memDC.SelectObject(wxNullBitmap); - - // Convert the bitmap into a linear data ready to be loaded into the GPU. - wxImage image = bitmap.ConvertToImage(); - image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]); - - // prepare buffer - std::vector<unsigned char> data(4 * m_width * m_height, 0); - for (int h = 0; h < m_height; ++h) - { - int hh = h * m_width; - unsigned char* px_ptr = data.data() + 4 * hh; - for (int w = 0; w < m_width; ++w) - { - *px_ptr++ = image.GetRed(w, h); - *px_ptr++ = image.GetGreen(w, h); - *px_ptr++ = image.GetBlue(w, h); - *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity; - } - } - - // sends buffer to gpu - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - ::glGenTextures(1, &m_id); - ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - ::glBindTexture(GL_TEXTURE_2D, 0); - - return true; -} - -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)) - { - ::glDisable(GL_DEPTH_TEST); - ::glPushMatrix(); - ::glLoadIdentity(); - - const Size& cnv_size = canvas.get_canvas_size(); - float zoom = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - 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; - float bottom = top - (float)m_original_height * inv_zoom; - - float uv_left = 0.0f; - float uv_top = 0.0f; - float uv_right = (float)m_original_width / (float)m_width; - float uv_bottom = (float)m_original_height / (float)m_height; - - GLTexture::Quad_UVs uvs; - uvs.left_top = { uv_left, uv_top }; - uvs.left_bottom = { uv_left, uv_bottom }; - uvs.right_bottom = { uv_right, uv_bottom }; - uvs.right_top = { uv_right, uv_top }; - - GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); - - ::glPopMatrix(); - ::glEnable(GL_DEPTH_TEST); - } -} - -const unsigned char GLCanvas3D::LegendTexture::Squares_Border_Color[3] = { 64, 64, 64 }; -const unsigned char GLCanvas3D::LegendTexture::Background_Color[3] = { 9, 91, 134 }; -const unsigned char GLCanvas3D::LegendTexture::Opacity = 255; - -GLCanvas3D::LegendTexture::LegendTexture() - : GUI::GLTexture() - , m_original_width(0) - , m_original_height(0) -{ -} - -bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) -{ - reset(); - - // collects items to render - auto title = _(preview_data.get_legend_title()); - const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors); - - unsigned int items_count = (unsigned int)items.size(); - if (items_count == 0) - // nothing to render, return - return false; - - wxMemoryDC memDC; - // select default font - memDC.SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - - // calculates texture size - wxCoord w, h; - memDC.GetTextExtent(title, &w, &h); - int title_width = (int)w; - int title_height = (int)h; - - int max_text_width = 0; - int max_text_height = 0; - for (const GCodePreviewData::LegendItem& item : items) - { - memDC.GetTextExtent(GUI::from_u8(item.text), &w, &h); - max_text_width = std::max(max_text_width, (int)w); - max_text_height = std::max(max_text_height, (int)h); - } - - m_original_width = std::max(2 * Px_Border + title_width, 2 * (Px_Border + Px_Square_Contour) + Px_Square + Px_Text_Offset + max_text_width); - m_original_height = 2 * (Px_Border + Px_Square_Contour) + title_height + Px_Title_Offset + items_count * Px_Square; - if (items_count > 1) - m_original_height += (items_count - 1) * Px_Square_Contour; - - int pow_of_two_size = next_highest_power_of_2(std::max(m_original_width, m_original_height)); - - m_width = pow_of_two_size; - m_height = pow_of_two_size; - - // generates bitmap - wxBitmap bitmap(m_width, m_height); - -#if defined(__APPLE__) || defined(_MSC_VER) - bitmap.UseAlpha(); -#endif - - memDC.SelectObject(bitmap); - memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); - memDC.Clear(); - - memDC.SetTextForeground(*wxWHITE); - - // draw title - int title_x = Px_Border; - int title_y = Px_Border; - memDC.DrawText(title, title_x, title_y); - - // draw icons contours as background - int squares_contour_x = Px_Border; - int squares_contour_y = Px_Border + title_height + Px_Title_Offset; - int squares_contour_width = Px_Square + 2 * Px_Square_Contour; - int squares_contour_height = items_count * Px_Square + 2 * Px_Square_Contour; - if (items_count > 1) - squares_contour_height += (items_count - 1) * Px_Square_Contour; - - wxColour color(Squares_Border_Color[0], Squares_Border_Color[1], Squares_Border_Color[2]); - wxPen pen(color); - wxBrush brush(color); - memDC.SetPen(pen); - memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height)); - - // draw items (colored icon + text) - int icon_x = squares_contour_x + Px_Square_Contour; - int icon_x_inner = icon_x + 1; - int icon_y = squares_contour_y + Px_Square_Contour; - int icon_y_step = Px_Square + Px_Square_Contour; - - int text_x = icon_x + Px_Square + Px_Text_Offset; - int text_y_offset = (Px_Square - max_text_height) / 2; - - int px_inner_square = Px_Square - 2; - - for (const GCodePreviewData::LegendItem& item : items) - { - // draw darker icon perimeter - const std::vector<unsigned char>& item_color_bytes = item.color.as_bytes(); - wxImage::HSVValue dark_hsv = wxImage::RGBtoHSV(wxImage::RGBValue(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2])); - dark_hsv.value *= 0.75; - wxImage::RGBValue dark_rgb = wxImage::HSVtoRGB(dark_hsv); - color.Set(dark_rgb.red, dark_rgb.green, dark_rgb.blue, item_color_bytes[3]); - pen.SetColour(color); - brush.SetColour(color); - memDC.SetPen(pen); - memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(icon_x, icon_y, Px_Square, Px_Square)); - - // draw icon interior - color.Set(item_color_bytes[0], item_color_bytes[1], item_color_bytes[2], item_color_bytes[3]); - pen.SetColour(color); - brush.SetColour(color); - memDC.SetPen(pen); - memDC.SetBrush(brush); - memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square)); - - // draw text - memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset); - - // update y - icon_y += icon_y_step; - } - - memDC.SelectObject(wxNullBitmap); - - // Convert the bitmap into a linear data ready to be loaded into the GPU. - wxImage image = bitmap.ConvertToImage(); - image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]); - - // prepare buffer - std::vector<unsigned char> data(4 * m_width * m_height, 0); - for (int h = 0; h < m_height; ++h) - { - int hh = h * m_width; - unsigned char* px_ptr = data.data() + 4 * hh; - for (int w = 0; w < m_width; ++w) - { - *px_ptr++ = image.GetRed(w, h); - *px_ptr++ = image.GetGreen(w, h); - *px_ptr++ = image.GetBlue(w, h); - *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity; - } - } - - // sends buffer to gpu - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - ::glGenTextures(1, &m_id); - ::glBindTexture(GL_TEXTURE_2D, (GLuint)m_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - ::glBindTexture(GL_TEXTURE_2D, 0); - - return true; -} - -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)) - { - ::glDisable(GL_DEPTH_TEST); - ::glPushMatrix(); - ::glLoadIdentity(); - - const Size& cnv_size = canvas.get_canvas_size(); - float zoom = canvas.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - 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; - float bottom = top - (float)m_original_height * inv_zoom; - - float uv_left = 0.0f; - float uv_top = 0.0f; - float uv_right = (float)m_original_width / (float)m_width; - float uv_bottom = (float)m_original_height / (float)m_height; - - GLTexture::Quad_UVs uvs; - uvs.left_top = { uv_left, uv_top }; - uvs.left_bottom = { uv_left, uv_bottom }; - uvs.right_bottom = { uv_right, uv_bottom }; - uvs.right_top = { uv_right, uv_top }; - - GLTexture::render_sub_texture(m_id, left, right, bottom, top, uvs); - - ::glPopMatrix(); - ::glEnable(GL_DEPTH_TEST); - } -} - -GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const -{ - GizmosMap::const_iterator it = m_gizmos.find(m_current); - return (it != m_gizmos.end()) ? it->second : nullptr; -} - -GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) - : m_canvas(canvas) - , m_context(nullptr) - , m_timer(nullptr) - , m_toolbar(*this) - , m_config(nullptr) - , m_print(nullptr) - , m_model(nullptr) - , m_dirty(true) - , m_initialized(false) - , m_use_VBOs(false) - , m_force_zoom_to_bed_enabled(false) - , m_apply_zoom_to_volumes_filter(false) - , m_hover_volume_id(-1) - , m_toolbar_action_running(false) - , m_warning_texture_enabled(false) - , m_legend_texture_enabled(false) - , m_picking_enabled(false) - , m_moving_enabled(false) - , m_shader_enabled(false) - , m_dynamic_background_enabled(false) - , m_multisample_allowed(false) - , m_color_by("volume") - , m_select_by("object") - , m_drag_by("instance") - , m_reload_delayed(false) -{ - if (m_canvas != nullptr) - { - m_context = new wxGLContext(m_canvas); - m_timer = new wxTimer(m_canvas); - } -} - -GLCanvas3D::~GLCanvas3D() -{ - reset_volumes(); - - if (m_timer != nullptr) - { - delete m_timer; - m_timer = nullptr; - } - - if (m_context != nullptr) - { - delete m_context; - m_context = nullptr; - } - - _deregister_callbacks(); -} - -bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) -{ - if (m_initialized) - return true; - - if ((m_canvas == nullptr) || (m_context == nullptr)) - return false; - - ::glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - ::glClearDepth(1.0f); - - ::glDepthFunc(GL_LESS); - - ::glEnable(GL_DEPTH_TEST); - ::glEnable(GL_CULL_FACE); - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // Set antialiasing / multisampling - ::glDisable(GL_LINE_SMOOTH); - ::glDisable(GL_POLYGON_SMOOTH); - - // ambient lighting - GLfloat ambient[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; - ::glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); - - ::glEnable(GL_LIGHT0); - ::glEnable(GL_LIGHT1); - - // light from camera - GLfloat specular_cam[4] = { 0.3f, 0.3f, 0.3f, 1.0f }; - ::glLightfv(GL_LIGHT1, GL_SPECULAR, specular_cam); - GLfloat diffuse_cam[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - ::glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse_cam); - - // light from above - GLfloat specular_top[4] = { 0.2f, 0.2f, 0.2f, 1.0f }; - ::glLightfv(GL_LIGHT0, GL_SPECULAR, specular_top); - GLfloat diffuse_top[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; - ::glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse_top); - - // Enables Smooth Color Shading; try GL_FLAT for (lack of) fun. - ::glShadeModel(GL_SMOOTH); - - // A handy trick -- have surface material mirror the color. - ::glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - ::glEnable(GL_COLOR_MATERIAL); - - if (m_multisample_allowed) - ::glEnable(GL_MULTISAMPLE); - - if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs")) - return false; - - if (useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) - return false; - - m_use_VBOs = useVBOs; - m_layers_editing.set_use_legacy_opengl(use_legacy_opengl); - - // on linux the gl context is not valid until the canvas is not shown on screen - // we defer the geometry finalization of volumes until the first call to render() - if (!m_volumes.empty()) - m_volumes.finalize_geometry(m_use_VBOs); - - if (m_gizmos.is_enabled() && !m_gizmos.init(*this)) - return false; - - if (!_init_toolbar()) - return false; - - m_initialized = true; - - return true; -} - -bool GLCanvas3D::set_current() -{ - if ((m_canvas != nullptr) && (m_context != nullptr)) - return m_canvas->SetCurrent(*m_context); - - return false; -} - -void GLCanvas3D::set_as_dirty() -{ - m_dirty = true; -} - -unsigned int GLCanvas3D::get_volumes_count() const -{ - return (unsigned int)m_volumes.volumes.size(); -} - -void GLCanvas3D::reset_volumes() -{ - if (!m_volumes.empty()) - { - // ensures this canvas is current - if (!set_current()) - return; - - m_volumes.release_geometry(); - m_volumes.clear(); - m_dirty = true; - } - - enable_warning_texture(false); - _reset_warning_texture(); -} - -void GLCanvas3D::deselect_volumes() -{ - for (GLVolume* vol : m_volumes.volumes) - { - if (vol != nullptr) - vol->selected = false; - } -} - -void GLCanvas3D::select_volume(unsigned int id) -{ - if (id < (unsigned int)m_volumes.volumes.size()) - { - GLVolume* vol = m_volumes.volumes[id]; - if (vol != nullptr) - vol->selected = true; - } -} - -void GLCanvas3D::update_volumes_selection(const std::vector<int>& selections) -{ - if (m_model == nullptr) - return; - - if (selections.empty()) - return; - - for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) - { - if ((selections[obj_idx] == 1) && (obj_idx < (unsigned int)m_objects_volumes_idxs.size())) - { - const std::vector<int>& volume_idxs = m_objects_volumes_idxs[obj_idx]; - for (int v : volume_idxs) - { - select_volume(v); - } - } - } -} - -int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const -{ - ModelInstance::EPrintVolumeState state; - m_volumes.check_outside_state(config, &state); - return (int)state; -} - -bool GLCanvas3D::move_volume_up(unsigned int id) -{ - if ((id > 0) && (id < (unsigned int)m_volumes.volumes.size())) - { - std::swap(m_volumes.volumes[id - 1], m_volumes.volumes[id]); - std::swap(m_volumes.volumes[id - 1]->composite_id, m_volumes.volumes[id]->composite_id); - std::swap(m_volumes.volumes[id - 1]->select_group_id, m_volumes.volumes[id]->select_group_id); - std::swap(m_volumes.volumes[id - 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); - return true; - } - - return false; -} - -bool GLCanvas3D::move_volume_down(unsigned int id) -{ - if ((id >= 0) && (id + 1 < (unsigned int)m_volumes.volumes.size())) - { - std::swap(m_volumes.volumes[id + 1], m_volumes.volumes[id]); - std::swap(m_volumes.volumes[id + 1]->composite_id, m_volumes.volumes[id]->composite_id); - std::swap(m_volumes.volumes[id + 1]->select_group_id, m_volumes.volumes[id]->select_group_id); - std::swap(m_volumes.volumes[id + 1]->drag_group_id, m_volumes.volumes[id]->drag_group_id); - return true; - } - - return false; -} - -void GLCanvas3D::set_objects_selections(const std::vector<int>& selections) -{ - m_objects_selections = selections; -} - -void GLCanvas3D::set_config(DynamicPrintConfig* config) -{ - m_config = config; -} - -void GLCanvas3D::set_print(Print* print) -{ - m_print = print; -} - -void GLCanvas3D::set_model(Model* model) -{ - m_model = model; -} - -void GLCanvas3D::set_bed_shape(const Pointfs& shape) -{ - bool new_shape = m_bed.set_shape(shape); - - // Set the origin and size for painting of the coordinate system axes. - m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size()); - - if (new_shape) - { - // forces the selection of the proper camera target - if (m_volumes.volumes.empty()) - zoom_to_bed(); - else - zoom_to_volumes(); - } - - m_dirty = true; -} - -void GLCanvas3D::set_auto_bed_shape() -{ - // draw a default square bed around object center - const BoundingBoxf3& bbox = volumes_bounding_box(); - double max_size = bbox.max_size(); - const Vec3d center = bbox.center(); - - Pointfs bed_shape; - bed_shape.reserve(4); - bed_shape.emplace_back(center(0) - max_size, center(1) - max_size); - bed_shape.emplace_back(center(0) + max_size, center(1) - max_size); - bed_shape.emplace_back(center(0) + max_size, center(1) + max_size); - bed_shape.emplace_back(center(0) - max_size, center(1) + max_size); - - set_bed_shape(bed_shape); - - // Set the origin for painting of the coordinate system axes. - m_axes.origin = Vec3d(center(0), center(1), (double)GROUND_Z); -} - -void GLCanvas3D::set_axes_length(float length) -{ - m_axes.length = length; -} - -void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons) -{ - m_cutting_plane.set(z, polygons); -} - -void GLCanvas3D::set_color_by(const std::string& value) -{ - m_color_by = value; -} - -void GLCanvas3D::set_select_by(const std::string& value) -{ - m_select_by = value; - m_volumes.set_select_by(value); -} - -void GLCanvas3D::set_drag_by(const std::string& value) -{ - m_drag_by = value; - m_volumes.set_drag_by(value); -} - -const std::string& GLCanvas3D::get_select_by() const -{ - return m_select_by; -} - -const std::string& GLCanvas3D::get_drag_by() const -{ - return m_drag_by; -} - -float GLCanvas3D::get_camera_zoom() const -{ - return m_camera.zoom; -} - -BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const -{ - BoundingBoxf3 bb; - for (const GLVolume* volume : m_volumes.volumes) - { - if (!m_apply_zoom_to_volumes_filter || ((volume != nullptr) && volume->zoom_to_volumes)) - bb.merge(volume->transformed_bounding_box()); - } - return bb; -} - -bool GLCanvas3D::is_layers_editing_enabled() const -{ - return m_layers_editing.is_enabled(); -} - -bool GLCanvas3D::is_layers_editing_allowed() const -{ - return m_layers_editing.is_allowed(); -} - -bool GLCanvas3D::is_shader_enabled() const -{ - return m_shader_enabled; -} - -bool GLCanvas3D::is_reload_delayed() const -{ - return m_reload_delayed; -} - -void GLCanvas3D::enable_layers_editing(bool enable) -{ - m_layers_editing.set_enabled(enable); -} - -void GLCanvas3D::enable_warning_texture(bool enable) -{ - m_warning_texture_enabled = enable; -} - -void GLCanvas3D::enable_legend_texture(bool enable) -{ - m_legend_texture_enabled = enable; -} - -void GLCanvas3D::enable_picking(bool enable) -{ - m_picking_enabled = enable; -} - -void GLCanvas3D::enable_moving(bool enable) -{ - m_moving_enabled = enable; -} - -void GLCanvas3D::enable_gizmos(bool enable) -{ - m_gizmos.set_enabled(enable); -} - -void GLCanvas3D::enable_toolbar(bool enable) -{ - m_toolbar.set_enabled(enable); -} - -void GLCanvas3D::enable_shader(bool enable) -{ - m_shader_enabled = enable; -} - -void GLCanvas3D::enable_force_zoom_to_bed(bool enable) -{ - m_force_zoom_to_bed_enabled = enable; -} - -void GLCanvas3D::enable_dynamic_background(bool enable) -{ - m_dynamic_background_enabled = enable; -} - -void GLCanvas3D::allow_multisample(bool allow) -{ - m_multisample_allowed = allow; -} - -void GLCanvas3D::enable_toolbar_item(const std::string& name, bool enable) -{ - if (enable) - m_toolbar.enable_item(name); - else - m_toolbar.disable_item(name); -} - -bool GLCanvas3D::is_toolbar_item_pressed(const std::string& name) const -{ - return m_toolbar.is_item_pressed(name); -} - -void GLCanvas3D::zoom_to_bed() -{ - _zoom_to_bounding_box(m_bed.get_bounding_box()); -} - -void GLCanvas3D::zoom_to_volumes() -{ - m_apply_zoom_to_volumes_filter = true; - _zoom_to_bounding_box(volumes_bounding_box()); - m_apply_zoom_to_volumes_filter = false; -} - -void GLCanvas3D::select_view(const std::string& direction) -{ - const float* dir_vec = nullptr; - - if (direction == "iso") - dir_vec = VIEW_DEFAULT; - else if (direction == "left") - dir_vec = VIEW_LEFT; - else if (direction == "right") - dir_vec = VIEW_RIGHT; - else if (direction == "top") - dir_vec = VIEW_TOP; - else if (direction == "bottom") - dir_vec = VIEW_BOTTOM; - else if (direction == "front") - dir_vec = VIEW_FRONT; - else if (direction == "rear") - dir_vec = VIEW_REAR; - - if ((dir_vec != nullptr) && !empty(volumes_bounding_box())) - { - m_camera.phi = dir_vec[0]; - m_camera.set_theta(dir_vec[1]); - - m_on_viewport_changed_callback.call(); - - if (m_canvas != nullptr) - m_canvas->Refresh(); - } -} - -void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) -{ - m_camera.phi = other.m_camera.phi; - m_camera.set_theta(other.m_camera.get_theta()); - m_camera.target = other.m_camera.target; - m_camera.zoom = other.m_camera.zoom; - m_dirty = true; -} - -void GLCanvas3D::update_volumes_colors_by_extruder() -{ - if (m_config != nullptr) - m_volumes.update_colors_by_extruder(m_config); -} - -void GLCanvas3D::update_gizmos_data() -{ - if (!m_gizmos.is_enabled()) - return; - - int id = _get_first_selected_object_id(); - if ((id != -1) && (m_model != nullptr)) - { - ModelObject* model_object = m_model->objects[id]; - if (model_object != nullptr) - { - ModelInstance* model_instance = model_object->instances[0]; - if (model_instance != nullptr) - { -#if ENABLE_MODELINSTANCE_3D_OFFSET - m_gizmos.set_position(model_instance->get_offset()); -#else - m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0)); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - m_gizmos.set_scale(model_instance->scaling_factor); - m_gizmos.set_angle_z(model_instance->rotation); - m_gizmos.set_flattening_data(model_object); - } - } - } - else - { - m_gizmos.set_position(Vec3d::Zero()); - m_gizmos.set_scale(1.0f); - m_gizmos.set_angle_z(0.0f); - m_gizmos.set_flattening_data(nullptr); - } -} - -void GLCanvas3D::render() -{ - if (m_canvas == nullptr) - return; - - if (!_is_shown_on_screen()) - return; - - // ensures this canvas is current and initialized - if (!set_current() || !_3DScene::init(m_canvas)) - return; - - if (m_force_zoom_to_bed_enabled) - _force_zoom_to_bed(); - - _camera_tranform(); - - GLfloat position_cam[4] = { 1.0f, 0.0f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT1, GL_POSITION, position_cam); - GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f }; - ::glLightfv(GL_LIGHT0, GL_POSITION, position_top); - - float theta = m_camera.get_theta(); - bool is_custom_bed = m_bed.is_custom(); - - // picking pass - _picking_pass(); - - // draw scene - _render_background(); - - if (is_custom_bed) // untextured bed needs to be rendered before objects - { - _render_bed(theta); - // disable depth testing so that axes are not covered by ground - _render_axes(false); - } - _render_objects(); - if (!is_custom_bed) // textured bed needs to be rendered after objects - { - _render_axes(true); - _render_bed(theta); - } - - _render_current_gizmo(); - _render_cutting_plane(); - - // draw overlays - _render_gizmos_overlay(); - _render_warning_texture(); - _render_legend_texture(); - _render_toolbar(); - _render_layer_editing_overlay(); - - m_canvas->SwapBuffers(); -} - -std::vector<double> GLCanvas3D::get_current_print_zs(bool active_only) const -{ - return m_volumes.get_current_print_zs(active_only); -} - -void GLCanvas3D::set_toolpaths_range(double low, double high) -{ - m_volumes.set_range(low, high); -} - -std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs) -{ - if (instance_idxs.empty()) - { - for (unsigned int i = 0; i < model_object.instances.size(); ++i) - { - instance_idxs.push_back(i); - } - } - return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_select_by, m_drag_by, m_use_VBOs && m_initialized); -} - -std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx) -{ - if ((0 <= obj_idx) && (obj_idx < (int)model.objects.size())) - { - const ModelObject* model_object = model.objects[obj_idx]; - if (model_object != nullptr) - return load_object(*model_object, obj_idx, std::vector<int>()); - } - - return std::vector<int>(); -} - -int GLCanvas3D::get_first_volume_id(int obj_idx) const -{ - for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) - { - if ((m_volumes.volumes[i] != nullptr) && (m_volumes.volumes[i]->object_idx() == obj_idx)) - return i; - } - - return -1; -} - -int GLCanvas3D::get_in_object_volume_id(int scene_vol_idx) const -{ - return ((0 <= scene_vol_idx) && (scene_vol_idx < (int)m_volumes.volumes.size())) ? m_volumes.volumes[scene_vol_idx]->volume_idx() : -1; -} - -void GLCanvas3D::reload_scene(bool force) -{ - if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr)) - return; - - reset_volumes(); - - // ensures this canvas is current - if (!set_current()) - return; - - set_bed_shape(dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"))->values); - - if (!m_canvas->IsShown() && !force) - { - m_reload_delayed = true; - return; - } - - m_reload_delayed = false; - - m_objects_volumes_idxs.clear(); - - for (unsigned int obj_idx = 0; obj_idx < (unsigned int)m_model->objects.size(); ++obj_idx) - { - m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx)); - } - - // 1st call to reset if no objects left - update_gizmos_data(); - update_volumes_selection(m_objects_selections); - // 2nd call to restore selection, if any - if (!m_objects_selections.empty()) - update_gizmos_data(); - - if (m_config->has("nozzle_diameter")) - { - // Should the wipe tower be visualized ? - unsigned int extruders_count = (unsigned int)dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter"))->values.size(); - - bool semm = dynamic_cast<const ConfigOptionBool*>(m_config->option("single_extruder_multi_material"))->value; - bool wt = dynamic_cast<const ConfigOptionBool*>(m_config->option("wipe_tower"))->value; - bool co = dynamic_cast<const ConfigOptionBool*>(m_config->option("complete_objects"))->value; - - if ((extruders_count > 1) && semm && wt && !co) - { - // Height of a print (Show at least a slab) - double height = std::max(m_model->bounding_box().max(2), 10.0); - - float x = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_x"))->value; - float y = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_y"))->value; - float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value; - float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value; - - float depth = m_print->get_wipe_tower_depth(); - if (!m_print->is_step_done(psWipeTower)) - depth = (900.f/w) * (float)(extruders_count - 1) ; - - m_volumes.load_wipe_tower_preview(1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), - m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); - } - } - - update_volumes_colors_by_extruder(); - - // checks for geometry outside the print volume to render it accordingly - if (!m_volumes.empty()) - { - ModelInstance::EPrintVolumeState state; - bool contained = m_volumes.check_outside_state(m_config, &state); - - if (!contained) - { - enable_warning_texture(true); - _generate_warning_texture(L("Detected object outside print volume")); - m_on_enable_action_buttons_callback.call(state == ModelInstance::PVS_Fully_Outside); - } - else - { - enable_warning_texture(false); - m_volumes.reset_outside_state(); - _reset_warning_texture(); - m_on_enable_action_buttons_callback.call(!m_model->objects.empty()); - } - } - else - { - enable_warning_texture(false); - _reset_warning_texture(); - m_on_enable_action_buttons_callback.call(false); - } -} - -void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors) -{ - if ((m_canvas != nullptr) && (m_print != nullptr)) - { - // ensures that this canvas is current - if (!set_current()) - return; - - if (m_volumes.empty()) - { - std::vector<float> tool_colors = _parse_colors(str_tool_colors); - - m_gcode_preview_volume_index.reset(); - - _load_gcode_extrusion_paths(preview_data, tool_colors); - _load_gcode_travel_paths(preview_data, tool_colors); - _load_gcode_retractions(preview_data); - _load_gcode_unretractions(preview_data); - - if (m_volumes.empty()) - reset_legend_texture(); - else - { - _generate_legend_texture(preview_data, tool_colors); - - // removes empty volumes - m_volumes.volumes.erase(std::remove_if(m_volumes.volumes.begin(), m_volumes.volumes.end(), - [](const GLVolume* volume) { return volume->print_zs.empty(); }), m_volumes.volumes.end()); - - _load_shells(); - } - _update_toolpath_volumes_outside_state(); - } - - _update_gcode_volumes_visibility(preview_data); - _show_warning_texture_if_needed(); - } -} - -void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors) -{ - if (m_print == nullptr) - return; - - _load_print_toolpaths(); - _load_wipe_tower_toolpaths(str_tool_colors); - for (const PrintObject* object : m_print->objects()) - { - if (object != nullptr) - _load_print_object_toolpaths(*object, str_tool_colors); - } - - for (GLVolume* volume : m_volumes.volumes) - { - volume->is_extrusion_path = true; - } - - _update_toolpath_volumes_outside_state(); - _show_warning_texture_if_needed(); - reset_legend_texture(); -} - -void GLCanvas3D::register_on_viewport_changed_callback(void* callback) -{ - if (callback != nullptr) - m_on_viewport_changed_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_double_click_callback(void* callback) -{ - if (callback != nullptr) - m_on_double_click_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_right_click_callback(void* callback) -{ - if (callback != nullptr) - m_on_right_click_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_select_object_callback(void* callback) -{ - if (callback != nullptr) - m_on_select_object_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_model_update_callback(void* callback) -{ - if (callback != nullptr) - m_on_model_update_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_remove_object_callback(void* callback) -{ - if (callback != nullptr) - m_on_remove_object_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_arrange_callback(void* callback) -{ - if (callback != nullptr) - m_on_arrange_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_rotate_object_left_callback(void* callback) -{ - if (callback != nullptr) - m_on_rotate_object_left_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_rotate_object_right_callback(void* callback) -{ - if (callback != nullptr) - m_on_rotate_object_right_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_scale_object_uniformly_callback(void* callback) -{ - if (callback != nullptr) - m_on_scale_object_uniformly_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_increase_objects_callback(void* callback) -{ - if (callback != nullptr) - m_on_increase_objects_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_decrease_objects_callback(void* callback) -{ - if (callback != nullptr) - m_on_decrease_objects_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_instance_moved_callback(void* callback) -{ - if (callback != nullptr) - m_on_instance_moved_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_wipe_tower_moved_callback(void* callback) -{ - if (callback != nullptr) - m_on_wipe_tower_moved_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_enable_action_buttons_callback(void* callback) -{ - if (callback != nullptr) - m_on_enable_action_buttons_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_scale_uniformly_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_rotate_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_gizmo_flatten_callback(void* callback) -{ - if (callback != nullptr) - m_on_gizmo_flatten_callback.register_callback(callback); -} - -void GLCanvas3D::register_on_update_geometry_info_callback(void* callback) -{ - if (callback != nullptr) - m_on_update_geometry_info_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_add_callback(void* callback) -{ - if (callback != nullptr) - m_action_add_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_delete_callback(void* callback) -{ - if (callback != nullptr) - m_action_delete_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_deleteall_callback(void* callback) -{ - if (callback != nullptr) - m_action_deleteall_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_arrange_callback(void* callback) -{ - if (callback != nullptr) - m_action_arrange_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_more_callback(void* callback) -{ - if (callback != nullptr) - m_action_more_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_fewer_callback(void* callback) -{ - if (callback != nullptr) - m_action_fewer_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_split_callback(void* callback) -{ - if (callback != nullptr) - m_action_split_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_cut_callback(void* callback) -{ - if (callback != nullptr) - m_action_cut_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_settings_callback(void* callback) -{ - if (callback != nullptr) - m_action_settings_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_layersediting_callback(void* callback) -{ - if (callback != nullptr) - m_action_layersediting_callback.register_callback(callback); -} - -void GLCanvas3D::register_action_selectbyparts_callback(void* callback) -{ - if (callback != nullptr) - m_action_selectbyparts_callback.register_callback(callback); -} - -void GLCanvas3D::bind_event_handlers() -{ - if (m_canvas != nullptr) - { - m_canvas->Bind(wxEVT_SIZE, &GLCanvas3D::on_size, this); - m_canvas->Bind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); - m_canvas->Bind(wxEVT_CHAR, &GLCanvas3D::on_char, this); - m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); - m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); - m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_RIGHT_UP, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_MOTION, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_ENTER_WINDOW, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_LEAVE_WINDOW, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_LEFT_DCLICK, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); - m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); - m_canvas->Bind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this); - } -} - -void GLCanvas3D::unbind_event_handlers() -{ - if (m_canvas != nullptr) - { - m_canvas->Unbind(wxEVT_SIZE, &GLCanvas3D::on_size, this); - m_canvas->Unbind(wxEVT_IDLE, &GLCanvas3D::on_idle, this); - m_canvas->Unbind(wxEVT_CHAR, &GLCanvas3D::on_char, this); - m_canvas->Unbind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); - m_canvas->Unbind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); - m_canvas->Unbind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_RIGHT_UP, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_MOTION, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_ENTER_WINDOW, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_LEAVE_WINDOW, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_LEFT_DCLICK, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); - m_canvas->Unbind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this); - } -} - -void GLCanvas3D::on_size(wxSizeEvent& evt) -{ - m_dirty = true; -} - -void GLCanvas3D::on_idle(wxIdleEvent& evt) -{ - if (!m_dirty) - return; - - _refresh_if_shown_on_screen(); -} - -void GLCanvas3D::on_char(wxKeyEvent& evt) -{ - if (evt.HasModifiers()) - evt.Skip(); - else - { - int keyCode = evt.GetKeyCode(); - switch (keyCode - 48) - { - // numerical input - case 0: { select_view("iso"); break; } - case 1: { select_view("top"); break; } - case 2: { select_view("bottom"); break; } - case 3: { select_view("front"); break; } - case 4: { select_view("rear"); break; } - case 5: { select_view("left"); break; } - case 6: { select_view("right"); break; } - default: - { - // text input - switch (keyCode) - { - // key + - case 43: { m_on_increase_objects_callback.call(); break; } - // key - - case 45: { m_on_decrease_objects_callback.call(); break; } - // key A/a - case 65: - case 97: { m_on_arrange_callback.call(); break; } - // key B/b - case 66: - case 98: { zoom_to_bed(); break; } - // key L/l - case 76: - case 108: { m_on_rotate_object_left_callback.call(); break; } - // key R/r - case 82: - case 114: { m_on_rotate_object_right_callback.call(); break; } - // key S/s - case 83: - case 115: { m_on_scale_object_uniformly_callback.call(); break; } - // key Z/z - case 90: - case 122: { zoom_to_volumes(); break; } - default: - { - evt.Skip(); - break; - } - } - } - } - } -} - -void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) -{ - // Ignore the wheel events if the middle button is pressed. - if (evt.MiddleIsDown()) - return; - - // Performs layers editing updates, if enabled - if (is_layers_editing_enabled()) - { - int object_idx_selected = _get_first_selected_object_id(); - if (object_idx_selected != -1) - { - // A volume is selected. Test, whether hovering over a layer thickness bar. - if (m_layers_editing.bar_rect_contains(*this, (float)evt.GetX(), (float)evt.GetY())) - { - // Adjust the width of the selection. - m_layers_editing.band_width = std::max(std::min(m_layers_editing.band_width * (1.0f + 0.1f * (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta()), 10.0f), 1.5f); - if (m_canvas != nullptr) - m_canvas->Refresh(); - - return; - } - } - } - - // Calculate the zoom delta and apply it to the current zoom factor - float zoom = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); - zoom = std::max(std::min(zoom, 4.0f), -4.0f) / 10.0f; - zoom = get_camera_zoom() / (1.0f - zoom); - - // Don't allow to zoom too far outside the scene. - float zoom_min = _get_zoom_to_bounding_box_factor(_max_bounding_box()); - if (zoom_min > 0.0f) - zoom = std::max(zoom, zoom_min * 0.8f); - - m_camera.zoom = zoom; - m_on_viewport_changed_callback.call(); - - _refresh_if_shown_on_screen(); -} - -void GLCanvas3D::on_timer(wxTimerEvent& evt) -{ - if (m_layers_editing.state != LayersEditing::Editing) - return; - - _perform_layer_editing_action(); -} - -void GLCanvas3D::on_mouse(wxMouseEvent& evt) -{ - Point pos(evt.GetX(), evt.GetY()); - - int selected_object_idx = _get_first_selected_object_id(); - int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; - m_layers_editing.last_object_id = layer_editing_object_idx; - bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); - int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position); - - if (evt.Entering()) - { -#if defined(__WXMSW__) || defined(__linux__) - // On Windows and Linux needs focus in order to catch key events - if (m_canvas != nullptr) - m_canvas->SetFocus(); - - m_mouse.set_start_position_2D_as_invalid(); -#endif - } - else if (evt.Leaving()) - { - // to remove hover on objects when the mouse goes out of this canvas - m_mouse.position = Vec2d(-1.0, -1.0); - m_dirty = true; - } - else if (evt.LeftDClick() && (m_hover_volume_id != -1) && !gizmos_overlay_contains_mouse && (toolbar_contains_mouse == -1)) - m_on_double_click_callback.call(); - else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) - { - m_toolbar_action_running = true; - m_toolbar.do_action((unsigned int)toolbar_contains_mouse); - } - else if (evt.LeftDown() || evt.RightDown()) - { - // If user pressed left or right button we first check whether this happened - // on a volume or not. - int volume_idx = m_hover_volume_id; - m_layers_editing.state = LayersEditing::Unknown; - if ((layer_editing_object_idx != -1) && m_layers_editing.bar_rect_contains(*this, pos(0), pos(1))) - { - // A volume is selected and the mouse is inside the layer thickness bar. - // Start editing the layer height. - m_layers_editing.state = LayersEditing::Editing; - _perform_layer_editing_action(&evt); - } - 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. - // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself, - // therefore it is safe to call it while the background processing is running. - const_cast<PrintObject*>(m_print->get_object(layer_editing_object_idx))->reset_layer_height_profile(); - // Index 2 means no editing, just wait for mouse up event. - m_layers_editing.state = LayersEditing::Completed; - - m_dirty = true; - } - } - else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse) - { - update_gizmos_data(); - m_gizmos.update_on_off_state(*this, m_mouse.position); - m_dirty = true; - } - else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse()) - { - update_gizmos_data(); - m_gizmos.start_dragging(_selected_volumes_bounding_box()); - m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id(selected_object_idx); - - if (m_gizmos.get_current_type() == Gizmos::Flatten) { - // Rotate the object so the normal points downward: - Vec3d normal = m_gizmos.get_flattening_normal(); - if (normal(0) != 0.0 || normal(1) != 0.0 || normal(2) != 0.0) { - Vec3d axis = normal(2) > 0.999 ? Vec3d::UnitX() : normal.cross(-Vec3d::UnitZ()).normalized(); - float angle = acos(clamp(-1.0, 1.0, -normal(2))); - m_on_gizmo_flatten_callback.call(angle, (float)axis(0), (float)axis(1), (float)axis(2)); - } - } - - m_dirty = true; - } - else if (toolbar_contains_mouse != -1) - { - m_toolbar_action_running = true; - m_toolbar.do_action((unsigned int)toolbar_contains_mouse); - } - else - { - // Select volume in this 3D canvas. - // Don't deselect a volume if layer editing is enabled. We want the object to stay selected - // during the scene manipulation. - - if (m_picking_enabled && ((volume_idx != -1) || !is_layers_editing_enabled())) - { - if (volume_idx != -1) - { - deselect_volumes(); - select_volume(volume_idx); - int group_id = m_volumes.volumes[volume_idx]->select_group_id; - if (group_id != -1) - { - for (GLVolume* vol : m_volumes.volumes) - { - if ((vol != nullptr) && (vol->select_group_id == group_id)) - vol->selected = true; - } - } - - update_gizmos_data(); - m_dirty = true; - } - } - - // propagate event through callback - if (m_picking_enabled && (volume_idx != -1)) - _on_select(volume_idx, selected_object_idx); - - if (volume_idx != -1) - { - if (evt.LeftDown() && m_moving_enabled) - { - // The mouse_to_3d gets the Z coordinate from the Z buffer at the screen coordinate pos x, y, - // an converts the screen space coordinate to unscaled object space. - Vec3d pos3d = (volume_idx == -1) ? Vec3d(DBL_MAX, DBL_MAX, DBL_MAX) : _mouse_to_3d(pos); - - // Only accept the initial position, if it is inside the volume bounding box. - BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); - volume_bbox.offset(1.0); - if (volume_bbox.contains(pos3d)) - { - // The dragging operation is initiated. - m_mouse.drag.move_with_shift = evt.ShiftDown(); - m_mouse.drag.move_volume_idx = volume_idx; - m_mouse.drag.start_position_3D = pos3d; - // Remember the shift to to the object center.The object center will later be used - // to limit the object placement close to the bed. - m_mouse.drag.volume_center_offset = volume_bbox.center() - pos3d; - } - } - else if (evt.RightDown()) - { - // forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while - // the context menu is already shown, ensuring it to disappear if the mouse is outside any volume - m_mouse.position = Vec2d((double)pos(0), (double)pos(1)); - render(); - if (m_hover_volume_id != -1) - { - // if right clicking on volume, propagate event through callback (shows context menu) - if (m_volumes.volumes[volume_idx]->hover) - m_on_right_click_callback.call(pos(0), pos(1)); - } - } - } - } - } - else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1)) - { - m_mouse.dragging = true; - - // Get new position at the same Z of the initial click point. - float z0 = 0.0f; - float z1 = 1.0f; - Vec3d cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)); - - // Clip the new position, so the object center remains close to the bed. - cur_pos += m_mouse.drag.volume_center_offset; - Point cur_pos2(scale_(cur_pos(0)), scale_(cur_pos(1))); - if (!m_bed.contains(cur_pos2)) - { - Point ip = m_bed.point_projection(cur_pos2); - cur_pos(0) = unscale<double>(ip(0)); - cur_pos(1) = unscale<double>(ip(1)); - } - cur_pos -= m_mouse.drag.volume_center_offset; - - // Calculate the translation vector. - Vec3d vector = cur_pos - m_mouse.drag.start_position_3D; - // Get the volume being dragged. - GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx]; - // Get all volumes belonging to the same group, if any. - std::vector<GLVolume*> volumes; - int group_id = m_mouse.drag.move_with_shift ? volume->select_group_id : volume->drag_group_id; - if (group_id == -1) - volumes.push_back(volume); - else - { - for (GLVolume* v : m_volumes.volumes) - { - if (v != nullptr) - { - if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (!m_mouse.drag.move_with_shift && (v->drag_group_id == group_id))) - volumes.push_back(v); - } - } - } - - // Apply new temporary volume origin and ignore Z. - for (GLVolume* v : volumes) - { - v->set_offset(v->get_offset() + Vec3d(vector(0), vector(1), 0.0)); - } - - update_position_values(volume->get_offset()); - m_mouse.drag.start_position_3D = cur_pos; - - m_dirty = true; - } - else if (evt.Dragging() && m_gizmos.is_dragging()) - { - if (!m_canvas->HasCapture()) - m_canvas->CaptureMouse(); - - m_mouse.dragging = true; - m_gizmos.update(mouse_ray(pos)); - - std::vector<GLVolume*> volumes; - if (m_mouse.drag.gizmo_volume_idx != -1) - { - GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; - // Get all volumes belonging to the same group, if any. - if (volume->select_group_id == -1) - volumes.push_back(volume); - else - { - for (GLVolume* v : m_volumes.volumes) - { - if ((v != nullptr) && (v->select_group_id == volume->select_group_id)) - volumes.push_back(v); - } - } - } - - switch (m_gizmos.get_current_type()) - { - case Gizmos::Move: - { - // Apply new temporary offset - GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx]; - Vec3d offset = m_gizmos.get_position() - volume->get_offset(); - for (GLVolume* v : volumes) - { - v->set_offset(v->get_offset() + offset); - } - update_position_values(volume->get_offset()); - break; - } - case Gizmos::Scale: - { - // Apply new temporary scale factor - float scale_factor = m_gizmos.get_scale(); - for (GLVolume* v : volumes) - { - v->set_scaling_factor((double)scale_factor); - } - update_scale_values((double)scale_factor); - break; - } - case Gizmos::Rotate: - { - // Apply new temporary angle_z - float angle_z = m_gizmos.get_angle_z(); - for (GLVolume* v : volumes) - { - v->set_rotation((double)angle_z); - } - update_rotation_value((double)angle_z, Z); - break; - } - default: - break; - } - - if (!volumes.empty()) - { - BoundingBoxf3 bb; - for (const GLVolume* volume : volumes) - { - bb.merge(volume->transformed_bounding_box()); - } - const Vec3d& size = bb.size(); - m_on_update_geometry_info_callback.call(size(0), size(1), size(2), m_gizmos.get_scale()); - } - - m_dirty = true; - } - else if (evt.Dragging() && !gizmos_overlay_contains_mouse) - { - m_mouse.dragging = true; - - if ((m_layers_editing.state != LayersEditing::Unknown) && (layer_editing_object_idx != -1)) - { - if (m_layers_editing.state == LayersEditing::Editing) - _perform_layer_editing_action(&evt); - } - else if (evt.LeftIsDown()) - { - // if dragging over blank area with left button, rotate - if (m_mouse.is_start_position_3D_defined()) - { - const Vec3d& orig = m_mouse.drag.start_position_3D; - m_camera.phi += (((float)pos(0) - (float)orig(0)) * TRACKBALLSIZE); - m_camera.set_theta(m_camera.get_theta() - ((float)pos(1) - (float)orig(1)) * TRACKBALLSIZE); - - m_on_viewport_changed_callback.call(); - - m_dirty = true; - } - m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); - } - else if (evt.MiddleIsDown() || evt.RightIsDown()) - { - // If dragging over blank area with right button, pan. - if (m_mouse.is_start_position_2D_defined()) - { - // get point in model space at Z = 0 - float z = 0.0f; - const Vec3d& cur_pos = _mouse_to_3d(pos, &z); - Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); - m_camera.target += orig - cur_pos; - - m_on_viewport_changed_callback.call(); - - m_dirty = true; - } - - m_mouse.drag.start_position_2D = pos; - } - } - else if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) - { - if (m_layers_editing.state != LayersEditing::Unknown) - { - m_layers_editing.state = LayersEditing::Unknown; - _stop_timer(); - - if (layer_editing_object_idx != -1) - m_on_model_update_callback.call(); - } - else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) - { - // get all volumes belonging to the same group, if any - std::vector<int> volume_idxs; - int vol_id = m_mouse.drag.move_volume_idx; - int group_id = m_mouse.drag.move_with_shift ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id; - if (group_id == -1) - volume_idxs.push_back(vol_id); - else - { - for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) - { - if ((m_mouse.drag.move_with_shift && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id)) - volume_idxs.push_back(i); - } - } - - _on_move(volume_idxs); - - // force re-selection of the wipe tower, if needed - if ((volume_idxs.size() == 1) && m_volumes.volumes[volume_idxs[0]]->is_wipe_tower) - select_volume(volume_idxs[0]); - } - else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) - { - // deselect and propagate event through callback - if (m_picking_enabled && !m_toolbar_action_running) - { - deselect_volumes(); - _on_select(-1, -1); - update_gizmos_data(); - } - } - else if (evt.LeftUp() && m_gizmos.is_dragging()) - { - switch (m_gizmos.get_current_type()) - { - case Gizmos::Move: - { - // get all volumes belonging to the same group, if any - std::vector<int> volume_idxs; - int vol_id = m_mouse.drag.gizmo_volume_idx; - int group_id = m_volumes.volumes[vol_id]->select_group_id; - if (group_id == -1) - volume_idxs.push_back(vol_id); - else - { - for (int i = 0; i < (int)m_volumes.volumes.size(); ++i) - { - if (m_volumes.volumes[i]->select_group_id == group_id) - volume_idxs.push_back(i); - } - } - - _on_move(volume_idxs); - - break; - } - case Gizmos::Scale: - { - m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale()); - break; - } - case Gizmos::Rotate: - { - m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z()); - break; - } - default: - break; - } - m_gizmos.stop_dragging(); - Slic3r::GUI::update_settings_value(); - } - - m_mouse.drag.move_volume_idx = -1; - m_mouse.drag.gizmo_volume_idx = -1; - m_mouse.set_start_position_3D_as_invalid(); - m_mouse.set_start_position_2D_as_invalid(); - m_mouse.dragging = false; - m_toolbar_action_running = false; - m_dirty = true; - - if (m_canvas->HasCapture()) - m_canvas->ReleaseMouse(); - } - else if (evt.Moving()) - { - m_mouse.position = Vec2d((double)pos(0), (double)pos(1)); - // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. - if (m_picking_enabled) - m_dirty = true; - } - else - evt.Skip(); -} - -void GLCanvas3D::on_paint(wxPaintEvent& evt) -{ - render(); -} - -void GLCanvas3D::on_key_down(wxKeyEvent& evt) -{ - if (evt.HasModifiers()) - evt.Skip(); - else - { - int key = evt.GetKeyCode(); - if (key == WXK_DELETE) - m_on_remove_object_callback.call(); - else - { -#ifdef __WXOSX__ - if (key == WXK_BACK) - m_on_remove_object_callback.call(); -#endif - evt.Skip(); - } - } -} - -Size GLCanvas3D::get_canvas_size() const -{ - int w = 0; - int h = 0; - - if (m_canvas != nullptr) - m_canvas->GetSize(&w, &h); - - return Size(w, h); -} - -Point GLCanvas3D::get_local_mouse_position() const -{ - if (m_canvas == nullptr) - return Point(); - - wxPoint mouse_pos = m_canvas->ScreenToClient(wxGetMousePosition()); - return Point(mouse_pos.x, mouse_pos.y); -} - -void GLCanvas3D::reset_legend_texture() -{ - if (!set_current()) - return; - - m_legend_texture.reset(); -} - -void GLCanvas3D::set_tooltip(const std::string& tooltip) -{ - if (m_canvas != nullptr) - m_canvas->SetToolTip(tooltip); -} - -bool GLCanvas3D::_is_shown_on_screen() const -{ - return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; -} - -void GLCanvas3D::_force_zoom_to_bed() -{ - zoom_to_bed(); - m_force_zoom_to_bed_enabled = false; -} - -bool GLCanvas3D::_init_toolbar() -{ - if (!m_toolbar.is_enabled()) - return true; - - if (!m_toolbar.init("toolbar.png", 36, 1, 1)) - { - // unable to init the toolbar texture, disable it - m_toolbar.set_enabled(false); - return true; - } - -// m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); - m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); - m_toolbar.set_separator_size(5); - m_toolbar.set_gap_size(2); - - GLToolbarItem::Data item; - - item.name = "add"; - item.tooltip = GUI::L_str("Add..."); - item.sprite_id = 0; - item.is_toggable = false; - item.action_callback = &m_action_add_callback; - if (!m_toolbar.add_item(item)) - return false; - - item.name = "delete"; - item.tooltip = GUI::L_str("Delete"); - item.sprite_id = 1; - item.is_toggable = false; - item.action_callback = &m_action_delete_callback; - if (!m_toolbar.add_item(item)) - return false; - - item.name = "deleteall"; - item.tooltip = GUI::L_str("Delete all"); - item.sprite_id = 2; - item.is_toggable = false; - item.action_callback = &m_action_deleteall_callback; - if (!m_toolbar.add_item(item)) - return false; - - item.name = "arrange"; - item.tooltip = GUI::L_str("Arrange"); - item.sprite_id = 3; - item.is_toggable = false; - item.action_callback = &m_action_arrange_callback; - if (!m_toolbar.add_item(item)) - return false; - - if (!m_toolbar.add_separator()) - return false; - - item.name = "more"; - item.tooltip = GUI::L_str("Add instance"); - item.sprite_id = 4; - item.is_toggable = false; - item.action_callback = &m_action_more_callback; - if (!m_toolbar.add_item(item)) - return false; - - item.name = "fewer"; - item.tooltip = GUI::L_str("Remove instance"); - item.sprite_id = 5; - item.is_toggable = false; - item.action_callback = &m_action_fewer_callback; - if (!m_toolbar.add_item(item)) - return false; - - if (!m_toolbar.add_separator()) - return false; - - item.name = "split"; - item.tooltip = GUI::L_str("Split"); - item.sprite_id = 6; - item.is_toggable = false; - item.action_callback = &m_action_split_callback; - if (!m_toolbar.add_item(item)) - return false; - - item.name = "cut"; - item.tooltip = GUI::L_str("Cut..."); - item.sprite_id = 7; - item.is_toggable = false; - item.action_callback = &m_action_cut_callback; - if (!m_toolbar.add_item(item)) - return false; - - if (!m_toolbar.add_separator()) - return false; - - item.name = "settings"; - item.tooltip = GUI::L_str("Settings..."); - item.sprite_id = 8; - item.is_toggable = false; - item.action_callback = &m_action_settings_callback; - if (!m_toolbar.add_item(item)) - return false; - - item.name = "layersediting"; - item.tooltip = GUI::L_str("Layers editing"); - item.sprite_id = 9; - item.is_toggable = true; - item.action_callback = &m_action_layersediting_callback; - if (!m_toolbar.add_item(item)) - return false; - - if (!m_toolbar.add_separator()) - return false; - - item.name = "selectbyparts"; - item.tooltip = GUI::L_str("Select by parts"); - item.sprite_id = 10; - item.is_toggable = true; - item.action_callback = &m_action_selectbyparts_callback; - if (!m_toolbar.add_item(item)) - return false; - - enable_toolbar_item("add", true); - - return true; -} - -void GLCanvas3D::_resize(unsigned int w, unsigned int h) -{ - if ((m_canvas == nullptr) && (m_context == nullptr)) - return; - - // ensures that this canvas is current - set_current(); - ::glViewport(0, 0, w, h); - - ::glMatrixMode(GL_PROJECTION); - ::glLoadIdentity(); - - const BoundingBoxf3& bbox = _max_bounding_box(); - - switch (m_camera.type) - { - case Camera::Ortho: - { - float w2 = w; - float h2 = h; - float two_zoom = 2.0f * get_camera_zoom(); - if (two_zoom != 0.0f) - { - float inv_two_zoom = 1.0f / two_zoom; - w2 *= inv_two_zoom; - h2 *= inv_two_zoom; - } - - // FIXME: calculate a tighter value for depth will improve z-fighting - float depth = 5.0f * (float)bbox.max_size(); - ::glOrtho(-w2, w2, -h2, h2, -depth, depth); - - break; - } -// case Camera::Perspective: -// { -// float bbox_r = (float)bbox.radius(); -// float fov = PI * 45.0f / 180.0f; -// float fov_tan = tan(0.5f * fov); -// float cam_distance = 0.5f * bbox_r / fov_tan; -// m_camera.distance = cam_distance; -// -// float nr = cam_distance - bbox_r * 1.1f; -// float fr = cam_distance + bbox_r * 1.1f; -// if (nr < 1.0f) -// nr = 1.0f; -// -// if (fr < nr + 1.0f) -// fr = nr + 1.0f; -// -// float h2 = fov_tan * nr; -// float w2 = h2 * w / h; -// ::glFrustum(-w2, w2, -h2, h2, nr, fr); -// -// break; -// } - default: - { - throw std::runtime_error("Invalid camera type."); - break; - } - } - - ::glMatrixMode(GL_MODELVIEW); - - m_dirty = false; -} - -BoundingBoxf3 GLCanvas3D::_max_bounding_box() const -{ - BoundingBoxf3 bb = m_bed.get_bounding_box(); - bb.merge(volumes_bounding_box()); - return bb; -} - -BoundingBoxf3 GLCanvas3D::_selected_volumes_bounding_box() const -{ - BoundingBoxf3 bb; - - std::vector<const GLVolume*> selected_volumes; - for (const GLVolume* volume : m_volumes.volumes) - { - if ((volume != nullptr) && !volume->is_wipe_tower && volume->selected) - selected_volumes.push_back(volume); - } - - bool use_drag_group_id = selected_volumes.size() > 1; - if (use_drag_group_id) - { - int drag_group_id = selected_volumes[0]->drag_group_id; - for (const GLVolume* volume : selected_volumes) - { - if (drag_group_id != volume->drag_group_id) - { - use_drag_group_id = false; - break; - } - } - } - - if (use_drag_group_id) - { - for (const GLVolume* volume : selected_volumes) - { - bb.merge(volume->bounding_box); - } - - bb = bb.transformed(selected_volumes[0]->world_matrix().cast<double>()); - } - else - { - for (const GLVolume* volume : selected_volumes) - { - bb.merge(volume->transformed_bounding_box()); - } - } - - return bb; -} - -void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) -{ - // Calculate the zoom factor needed to adjust viewport to bounding box. - float zoom = _get_zoom_to_bounding_box_factor(bbox); - if (zoom > 0.0f) - { - m_camera.zoom = zoom; - // center view around bounding box center - m_camera.target = bbox.center(); - - m_on_viewport_changed_callback.call(); - - _refresh_if_shown_on_screen(); - } -} - -float GLCanvas3D::_get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const -{ - float max_bb_size = bbox.max_size(); - if (max_bb_size == 0.0f) - return -1.0f; - - // project the bbox vertices on a plane perpendicular to the camera forward axis - // then calculates the vertices coordinate on this plane along the camera xy axes - - // we need the view matrix, we let opengl calculate it (same as done in render()) - _camera_tranform(); - - // get the view matrix back from opengl - GLfloat matrix[16]; - ::glGetFloatv(GL_MODELVIEW_MATRIX, matrix); - - // camera axes - Vec3d right((double)matrix[0], (double)matrix[4], (double)matrix[8]); - Vec3d up((double)matrix[1], (double)matrix[5], (double)matrix[9]); - Vec3d forward((double)matrix[2], (double)matrix[6], (double)matrix[10]); - - Vec3d bb_min = bbox.min; - Vec3d bb_max = bbox.max; - Vec3d bb_center = bbox.center(); - - // bbox vertices in world space - std::vector<Vec3d> vertices; - vertices.reserve(8); - vertices.push_back(bb_min); - vertices.emplace_back(bb_max(0), bb_min(1), bb_min(2)); - vertices.emplace_back(bb_max(0), bb_max(1), bb_min(2)); - vertices.emplace_back(bb_min(0), bb_max(1), bb_min(2)); - vertices.emplace_back(bb_min(0), bb_min(1), bb_max(2)); - vertices.emplace_back(bb_max(0), bb_min(1), bb_max(2)); - vertices.push_back(bb_max); - vertices.emplace_back(bb_min(0), bb_max(1), bb_max(2)); - - double max_x = 0.0; - double max_y = 0.0; - - // margin factor to give some empty space around the bbox - double margin_factor = 1.25; - - for (const Vec3d v : vertices) - { - // project vertex on the plane perpendicular to camera forward axis - Vec3d pos(v(0) - bb_center(0), v(1) - bb_center(1), v(2) - bb_center(2)); - Vec3d proj_on_plane = pos - pos.dot(forward) * forward; - - // calculates vertex coordinate along camera xy axes - double x_on_plane = proj_on_plane.dot(right); - double y_on_plane = proj_on_plane.dot(up); - - max_x = std::max(max_x, margin_factor * std::abs(x_on_plane)); - max_y = std::max(max_y, margin_factor * std::abs(y_on_plane)); - } - - if ((max_x == 0.0) || (max_y == 0.0)) - return -1.0f; - - max_x *= 2.0; - max_y *= 2.0; - - const Size& cnv_size = get_canvas_size(); - return (float)std::min((double)cnv_size.get_width() / max_x, (double)cnv_size.get_height() / max_y); -} - -void GLCanvas3D::_deregister_callbacks() -{ - m_on_viewport_changed_callback.deregister_callback(); - m_on_double_click_callback.deregister_callback(); - m_on_right_click_callback.deregister_callback(); - m_on_select_object_callback.deregister_callback(); - m_on_model_update_callback.deregister_callback(); - m_on_remove_object_callback.deregister_callback(); - m_on_arrange_callback.deregister_callback(); - m_on_rotate_object_left_callback.deregister_callback(); - m_on_rotate_object_right_callback.deregister_callback(); - m_on_scale_object_uniformly_callback.deregister_callback(); - m_on_increase_objects_callback.deregister_callback(); - m_on_decrease_objects_callback.deregister_callback(); - m_on_instance_moved_callback.deregister_callback(); - m_on_wipe_tower_moved_callback.deregister_callback(); - m_on_enable_action_buttons_callback.deregister_callback(); - m_on_gizmo_scale_uniformly_callback.deregister_callback(); - m_on_gizmo_rotate_callback.deregister_callback(); - m_on_gizmo_flatten_callback.deregister_callback(); - m_on_update_geometry_info_callback.deregister_callback(); - - m_action_add_callback.deregister_callback(); - m_action_delete_callback.deregister_callback(); - m_action_deleteall_callback.deregister_callback(); - m_action_arrange_callback.deregister_callback(); - m_action_more_callback.deregister_callback(); - m_action_fewer_callback.deregister_callback(); - m_action_split_callback.deregister_callback(); - m_action_cut_callback.deregister_callback(); - m_action_settings_callback.deregister_callback(); - m_action_layersediting_callback.deregister_callback(); - m_action_selectbyparts_callback.deregister_callback(); -} - -void GLCanvas3D::_mark_volumes_for_layer_height() const -{ - if (m_print == nullptr) - return; - - for (GLVolume* vol : m_volumes.volumes) - { - int object_id = int(vol->select_group_id / 1000000); - int shader_id = m_layers_editing.get_shader_program_id(); - - if (is_layers_editing_enabled() && (shader_id != -1) && vol->selected && - vol->has_layer_height_texture() && (object_id < (int)m_print->objects().size())) - { - vol->set_layer_height_texture_data(m_layers_editing.get_z_texture_id(), shader_id, - m_print->get_object(object_id), _get_layers_editing_cursor_z_relative(), m_layers_editing.band_width); - } - else - vol->reset_layer_height_texture_data(); - } -} - -void GLCanvas3D::_refresh_if_shown_on_screen() -{ - if (_is_shown_on_screen()) - { - const Size& cnv_size = get_canvas_size(); - _resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height()); - if (m_canvas != nullptr) - m_canvas->Refresh(); - } -} - -void GLCanvas3D::_camera_tranform() const -{ - ::glMatrixMode(GL_MODELVIEW); - ::glLoadIdentity(); - - ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch - ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw - - Vec3d neg_target = - m_camera.target; - ::glTranslatef((GLfloat)neg_target(0), (GLfloat)neg_target(1), (GLfloat)neg_target(2)); -} - -void GLCanvas3D::_picking_pass() const -{ - const Vec2d& pos = m_mouse.position; - - if (m_picking_enabled && !m_mouse.dragging && (pos != Vec2d(DBL_MAX, DBL_MAX))) - { - // Render the object for picking. - // FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing. - // Better to use software ray - casting on a bounding - box hierarchy. - - if (m_multisample_allowed) - ::glDisable(GL_MULTISAMPLE); - - ::glDisable(GL_BLEND); - ::glEnable(GL_DEPTH_TEST); - - ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - _render_volumes(true); - m_gizmos.render_current_gizmo_for_picking_pass(_selected_volumes_bounding_box()); - - if (m_multisample_allowed) - ::glEnable(GL_MULTISAMPLE); - - int volume_id = -1; - for (GLVolume* vol : m_volumes.volumes) - { - vol->hover = false; - } - - GLubyte color[4] = { 0, 0, 0, 0 }; - const Size& cnv_size = get_canvas_size(); - bool inside = (0 <= pos(0)) && (pos(0) < cnv_size.get_width()) && (0 <= pos(1)) && (pos(1) < cnv_size.get_height()); - if (inside) - { - ::glReadPixels(pos(0), cnv_size.get_height() - pos(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color); - volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; - } - - if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) - { - m_hover_volume_id = volume_id; - m_volumes.volumes[volume_id]->hover = true; - int group_id = m_volumes.volumes[volume_id]->select_group_id; - if (group_id != -1) - { - for (GLVolume* vol : m_volumes.volumes) - { - if (vol->select_group_id == group_id) - vol->hover = true; - } - } - m_gizmos.set_hover_id(-1); - } - else - { - m_hover_volume_id = -1; - m_gizmos.set_hover_id(inside ? (254 - (int)color[2]) : -1); - } - - // updates gizmos overlay - if (_get_first_selected_object_id() != -1) - m_gizmos.update_hover_state(*this, pos); - else - m_gizmos.reset_all_states(); - - m_toolbar.update_hover_state(pos); - } -} - -void GLCanvas3D::_render_background() const -{ - ::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - ::glPushMatrix(); - ::glLoadIdentity(); - ::glMatrixMode(GL_PROJECTION); - ::glPushMatrix(); - ::glLoadIdentity(); - - // Draws a bluish bottom to top gradient over the complete screen. - ::glDisable(GL_DEPTH_TEST); - - ::glBegin(GL_QUADS); - ::glColor3f(0.0f, 0.0f, 0.0f); - ::glVertex2f(-1.0f, -1.0f); - ::glVertex2f(1.0f, -1.0f); - - if (m_dynamic_background_enabled && _is_any_volume_outside()) - ::glColor3f(ERROR_BG_COLOR[0], ERROR_BG_COLOR[1], ERROR_BG_COLOR[2]); - else - ::glColor3f(DEFAULT_BG_COLOR[0], DEFAULT_BG_COLOR[1], DEFAULT_BG_COLOR[2]); - - ::glVertex2f(1.0f, 1.0f); - ::glVertex2f(-1.0f, 1.0f); - ::glEnd(); - - ::glEnable(GL_DEPTH_TEST); - - ::glPopMatrix(); - ::glMatrixMode(GL_MODELVIEW); - ::glPopMatrix(); -} - -void GLCanvas3D::_render_bed(float theta) const -{ - m_bed.render(theta); -} - -void GLCanvas3D::_render_axes(bool depth_test) const -{ - m_axes.render(depth_test); -} - -void GLCanvas3D::_render_objects() const -{ - if (m_volumes.empty()) - return; - - ::glEnable(GL_LIGHTING); - ::glEnable(GL_DEPTH_TEST); - - if (!m_shader_enabled) - _render_volumes(false); - else if (m_use_VBOs) - { - if (m_picking_enabled) - { - _mark_volumes_for_layer_height(); - - if (m_config != nullptr) - { - const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(); - m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height")); - m_volumes.check_outside_state(m_config, nullptr); - } - // do not cull backfaces to show broken geometry, if any - ::glDisable(GL_CULL_FACE); - } - - m_shader.start_using(); - m_volumes.render_VBOs(); - m_shader.stop_using(); - - if (m_picking_enabled) - ::glEnable(GL_CULL_FACE); - } - else - { - // do not cull backfaces to show broken geometry, if any - if (m_picking_enabled) - ::glDisable(GL_CULL_FACE); - - m_volumes.render_legacy(); - - if (m_picking_enabled) - ::glEnable(GL_CULL_FACE); - } - - ::glDisable(GL_LIGHTING); -} - -void GLCanvas3D::_render_cutting_plane() const -{ - m_cutting_plane.render(volumes_bounding_box()); -} - -void GLCanvas3D::_render_warning_texture() const -{ - if (!m_warning_texture_enabled) - return; - - m_warning_texture.render(*this); -} - -void GLCanvas3D::_render_legend_texture() const -{ - if (!m_legend_texture_enabled) - return; - - m_legend_texture.render(*this); -} - -void GLCanvas3D::_render_layer_editing_overlay() const -{ - if (m_print == nullptr) - return; - - GLVolume* volume = nullptr; - - for (GLVolume* vol : m_volumes.volumes) - { - if ((vol != nullptr) && vol->selected && vol->has_layer_height_texture()) - { - volume = vol; - break; - } - } - - if (volume == nullptr) - return; - - // If the active object was not allocated at the Print, go away.This should only be a momentary case between an object addition / deletion - // and an update by Platter::async_apply_config. - int object_idx = int(volume->select_group_id / 1000000); - if ((int)m_print->objects().size() < object_idx) - return; - - const PrintObject* print_object = m_print->get_object(object_idx); - if (print_object == nullptr) - return; - - m_layers_editing.render(*this, *print_object, *volume); -} - -void GLCanvas3D::_render_volumes(bool fake_colors) const -{ - static const GLfloat INV_255 = 1.0f / 255.0f; - - if (!fake_colors) - ::glEnable(GL_LIGHTING); - - // do not cull backfaces to show broken geometry, if any - ::glDisable(GL_CULL_FACE); - - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnableClientState(GL_VERTEX_ARRAY); - ::glEnableClientState(GL_NORMAL_ARRAY); - - unsigned int volume_id = 0; - for (GLVolume* vol : m_volumes.volumes) - { - if (fake_colors) - { - // Object picking mode. Render the object with a color encoding the object index. - unsigned int r = (volume_id & 0x000000FF) >> 0; - unsigned int g = (volume_id & 0x0000FF00) >> 8; - unsigned int b = (volume_id & 0x00FF0000) >> 16; - ::glColor3f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255); - } - else - { - vol->set_render_color(); - ::glColor4f(vol->render_color[0], vol->render_color[1], vol->render_color[2], vol->render_color[3]); - } - - vol->render(); - ++volume_id; - } - - ::glDisableClientState(GL_NORMAL_ARRAY); - ::glDisableClientState(GL_VERTEX_ARRAY); - ::glDisable(GL_BLEND); - - ::glEnable(GL_CULL_FACE); - - if (!fake_colors) - ::glDisable(GL_LIGHTING); -} - -void GLCanvas3D::_render_current_gizmo() const -{ - m_gizmos.render_current_gizmo(_selected_volumes_bounding_box()); -} - -void GLCanvas3D::_render_gizmos_overlay() const -{ - m_gizmos.render_overlay(*this); -} - -void GLCanvas3D::_render_toolbar() const -{ - _resize_toolbar(); - m_toolbar.render(); -} - -float GLCanvas3D::_get_layers_editing_cursor_z_relative() const -{ - return m_layers_editing.get_cursor_z_relative(*this); -} - -void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) -{ - int object_idx_selected = m_layers_editing.last_object_id; - if (object_idx_selected == -1) - return; - - if (m_print == nullptr) - return; - - const PrintObject* selected_obj = m_print->get_object(object_idx_selected); - if (selected_obj == nullptr) - return; - - // A volume is selected. Test, whether hovering over a layer thickness bar. - if (evt != nullptr) - { - const Rect& rect = LayersEditing::get_bar_rect_screen(*this); - float b = rect.get_bottom(); - m_layers_editing.last_z = unscale<double>(selected_obj->size(2)) * (b - evt->GetY() - 1.0f) / (b - rect.get_top()); - m_layers_editing.last_action = evt->ShiftDown() ? (evt->RightIsDown() ? 3 : 2) : (evt->RightIsDown() ? 0 : 1); - } - - // Mark the volume as modified, so Print will pick its layer height profile ? Where to mark it ? - // Start a timer to refresh the print ? schedule_background_process() ? - // The PrintObject::adjust_layer_height_profile() call adjusts the profile of its associated ModelObject, it does not modify the profile of the PrintObject itself, - // therefore it is safe to call it while the background processing is running. - const_cast<PrintObject*>(selected_obj)->adjust_layer_height_profile(m_layers_editing.last_z, m_layers_editing.strength, m_layers_editing.band_width, m_layers_editing.last_action); - - // searches the id of the first volume of the selected object - int volume_idx = 0; - for (int i = 0; i < object_idx_selected; ++i) - { - const PrintObject* obj = m_print->get_object(i); - if (obj != nullptr) - { - for (int j = 0; j < (int)obj->region_volumes.size(); ++j) - { - volume_idx += (int)obj->region_volumes[j].size(); - } - } - } - - m_volumes.volumes[volume_idx]->generate_layer_height_texture(selected_obj, 1); - _refresh_if_shown_on_screen(); - - // Automatic action on mouse down with the same coordinate. - _start_timer(); -} - -Vec3d GLCanvas3D::_mouse_to_3d(const Point& mouse_pos, float* z) -{ - if (m_canvas == nullptr) - return Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); - - _camera_tranform(); - - GLint viewport[4]; - ::glGetIntegerv(GL_VIEWPORT, viewport); - GLdouble modelview_matrix[16]; - ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); - GLdouble projection_matrix[16]; - ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - - GLint y = viewport[3] - (GLint)mouse_pos(1); - GLfloat mouse_z; - if (z == nullptr) - ::glReadPixels((GLint)mouse_pos(0), y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, (void*)&mouse_z); - else - mouse_z = *z; - - GLdouble out_x, out_y, out_z; - ::gluUnProject((GLdouble)mouse_pos(0), (GLdouble)y, (GLdouble)mouse_z, modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); - return Vec3d((double)out_x, (double)out_y, (double)out_z); -} - -Vec3d GLCanvas3D::_mouse_to_bed_3d(const Point& mouse_pos) -{ - return mouse_ray(mouse_pos).intersect_plane(0.0); -} - -Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos) -{ - float z0 = 0.0f; - float z1 = 1.0f; - return Linef3(_mouse_to_3d(mouse_pos, &z0), _mouse_to_3d(mouse_pos, &z1)); -} - -void GLCanvas3D::_start_timer() -{ - if (m_timer != nullptr) - m_timer->Start(100, wxTIMER_CONTINUOUS); -} - -void GLCanvas3D::_stop_timer() -{ - if (m_timer != nullptr) - m_timer->Stop(); -} - -int GLCanvas3D::_get_first_selected_object_id() const -{ - if (m_print != nullptr) - { - int objects_count = (int)m_print->objects().size(); - - for (const GLVolume* vol : m_volumes.volumes) - { - if ((vol != nullptr) && vol->selected) - { - int object_id = vol->select_group_id / 1000000; - // Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy. - if (object_id < 10000) - return (object_id >= objects_count) ? -1 : object_id; - } - } - } - return -1; -} - -int GLCanvas3D::_get_first_selected_volume_id(int object_id) const -{ - int volume_id = -1; - - for (const GLVolume* vol : m_volumes.volumes) - { - ++volume_id; - if ((vol != nullptr) && vol->selected && (object_id == vol->select_group_id / 1000000)) - return volume_id; - } - - return -1; -} - -void GLCanvas3D::_load_print_toolpaths() -{ - // ensures this canvas is current - if (!set_current()) - return; - - if (m_print == nullptr) - return; - - if (!m_print->is_step_done(psSkirt) || !m_print->is_step_done(psBrim)) - return; - - if (!m_print->has_skirt() && (m_print->config().brim_width.value == 0)) - return; - - const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish - - // number of skirt layers - size_t total_layer_count = 0; - for (const PrintObject* print_object : m_print->objects()) - { - total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); - } - size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config().skirt_height.value, total_layer_count); - if ((skirt_height == 0) && (m_print->config().brim_width.value > 0)) - skirt_height = 1; - - // get first skirt_height layers (maybe this should be moved to a PrintObject method?) - const PrintObject* object0 = m_print->objects().front(); - std::vector<float> print_zs; - print_zs.reserve(skirt_height * 2); - for (size_t i = 0; i < std::min(skirt_height, object0->layers().size()); ++i) - { - print_zs.push_back(float(object0->layers()[i]->print_z)); - } - //FIXME why there are support layers? - for (size_t i = 0; i < std::min(skirt_height, object0->support_layers().size()); ++i) - { - print_zs.push_back(float(object0->support_layers()[i]->print_z)); - } - sort_remove_duplicates(print_zs); - if (print_zs.size() > skirt_height) - print_zs.erase(print_zs.begin() + skirt_height, print_zs.end()); - - m_volumes.volumes.emplace_back(new GLVolume(color)); - GLVolume& volume = *m_volumes.volumes.back(); - for (size_t i = 0; i < skirt_height; ++i) { - volume.print_zs.push_back(print_zs[i]); - volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size()); - volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size()); - if (i == 0) - _3DScene::extrusionentity_to_verts(m_print->brim(), print_zs[i], Point(0, 0), volume); - - _3DScene::extrusionentity_to_verts(m_print->skirt(), print_zs[i], Point(0, 0), volume); - } - volume.bounding_box = volume.indexed_vertex_array.bounding_box(); - volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); -} - -void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors) -{ - std::vector<float> tool_colors = _parse_colors(str_tool_colors); - - struct Ctxt - { - const Points *shifted_copies; - std::vector<const Layer*> layers; - bool has_perimeters; - bool has_infill; - bool has_support; - const std::vector<float>* tool_colors; - - // Number of vertices (each vertex is 6x4=24 bytes long) - static const size_t alloc_size_max() { return 131072; } // 3.15MB - // static const size_t alloc_size_max () { return 65536; } // 1.57MB - // static const size_t alloc_size_max () { return 32768; } // 786kB - static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } - - static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow - static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish - static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - - // For cloring by a tool, return a parsed color. - bool color_by_tool() const { return tool_colors != nullptr; } - size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } - const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } - int volume_idx(int extruder, int feature) const - { - return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature; - } - } ctxt; - - ctxt.shifted_copies = &print_object.copies(); - - // order layers by print_z - ctxt.layers.reserve(print_object.layers().size() + print_object.support_layers().size()); - for (const Layer *layer : print_object.layers()) - ctxt.layers.push_back(layer); - for (const Layer *layer : print_object.support_layers()) - ctxt.layers.push_back(layer); - std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; }); - - // Maximum size of an allocation block: 32MB / sizeof(float) - ctxt.has_perimeters = print_object.is_step_done(posPerimeters); - ctxt.has_infill = print_object.is_step_done(posInfill); - ctxt.has_support = print_object.is_step_done(posSupportMaterial); - ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start"; - - //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1)); - tbb::spin_mutex new_volume_mutex; - auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* { - auto *volume = new GLVolume(color); - new_volume_mutex.lock(); - m_volumes.volumes.emplace_back(volume); - new_volume_mutex.unlock(); - return volume; - }; - const size_t volumes_cnt_initial = m_volumes.volumes.size(); - std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size()); - tbb::parallel_for( - tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size), - [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { - std::vector<GLVolume*> vols; - if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) - vols.emplace_back(new_volume(ctxt.color_tool(i))); - } - else - vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) }; - for (GLVolume *vol : vols) - vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { - const Layer *layer = ctxt.layers[idx_layer]; - for (size_t i = 0; i < vols.size(); ++i) { - GLVolume &vol = *vols[i]; - if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) { - vol.print_zs.push_back(layer->print_z); - vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - } - } - for (const Point © : *ctxt.shifted_copies) { - for (const LayerRegion *layerm : layer->regions()) { - if (ctxt.has_perimeters) - _3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy, - *vols[ctxt.volume_idx(layerm->region()->config().perimeter_extruder.value, 0)]); - if (ctxt.has_infill) { - for (const ExtrusionEntity *ee : layerm->fills.entities) { - // fill represents infill extrusions of a single island. - const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); - if (!fill->entities.empty()) - _3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy, - *vols[ctxt.volume_idx( - is_solid_infill(fill->entities.front()->role()) ? - layerm->region()->config().solid_infill_extruder : - layerm->region()->config().infill_extruder, - 1)]); - } - } - } - if (ctxt.has_support) { - const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer); - if (support_layer) { - for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities) - _3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy, - *vols[ctxt.volume_idx( - (extrusion_entity->role() == erSupportMaterial) ? - support_layer->object()->config().support_material_extruder : - support_layer->object()->config().support_material_interface_extruder, - 2)]); - } - } - } - for (size_t i = 0; i < vols.size(); ++i) { - GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { - // Store the vertex arrays and restart their containers, - vols[i] = new_volume(vol.color); - GLVolume &vol_new = *vols[i]; - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol.indexed_vertex_array = vol_new.indexed_vertex_array; - // Finalize a bounding box of the old GLVolume. - vol.bounding_box = vol.indexed_vertex_array.bounding_box(); - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - } - } - } - for (GLVolume *vol : vols) { - vol->bounding_box = vol->indexed_vertex_array.bounding_box(); - vol->indexed_vertex_array.shrink_to_fit(); - } - }); - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; - // Remove empty volumes from the newly added volumes. - m_volumes.volumes.erase( - std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - m_volumes.volumes.end()); - for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) - m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); - - BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; -} - -void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors) -{ - if ((m_print == nullptr) || m_print->wipe_tower_data().tool_changes.empty()) - return; - - if (!m_print->is_step_done(psWipeTower)) - return; - - std::vector<float> tool_colors = _parse_colors(str_tool_colors); - - struct Ctxt - { - const Print *print; - const std::vector<float> *tool_colors; - WipeTower::xy wipe_tower_pos; - float wipe_tower_angle; - - // Number of vertices (each vertex is 6x4=24 bytes long) - static const size_t alloc_size_max() { return 131072; } // 3.15MB - static const size_t alloc_size_reserve() { return alloc_size_max() * 2; } - - static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish - - // For cloring by a tool, return a parsed color. - bool color_by_tool() const { return tool_colors != nullptr; } - size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; } - const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; } - int volume_idx(int tool, int feature) const - { - return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature; - } - - const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) { - const auto &tool_changes = print->wipe_tower_data().tool_changes; - return priming.empty() ? - ((idx == tool_changes.size()) ? final : tool_changes[idx]) : - ((idx == 0) ? priming : (idx == tool_changes.size() + 1) ? final : tool_changes[idx - 1]); - } - std::vector<WipeTower::ToolChangeResult> priming; - std::vector<WipeTower::ToolChangeResult> final; - } ctxt; - - ctxt.print = m_print; - ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; - if (m_print->wipe_tower_data().priming && m_print->config().single_extruder_multi_material_priming) - ctxt.priming.emplace_back(*m_print->wipe_tower_data().priming.get()); - if (m_print->wipe_tower_data().final_purge) - ctxt.final.emplace_back(*m_print->wipe_tower_data().final_purge.get()); - - ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI; - ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; - - //FIXME Improve the heuristics for a grain size. - size_t n_items = m_print->wipe_tower_data().tool_changes.size() + (ctxt.priming.empty() ? 0 : 1); - size_t grain_size = std::max(n_items / 128, size_t(1)); - tbb::spin_mutex new_volume_mutex; - auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* { - auto *volume = new GLVolume(color); - new_volume_mutex.lock(); - m_volumes.volumes.emplace_back(volume); - new_volume_mutex.unlock(); - return volume; - }; - const size_t volumes_cnt_initial = m_volumes.volumes.size(); - std::vector<GLVolumeCollection> volumes_per_thread(n_items); - tbb::parallel_for( - tbb::blocked_range<size_t>(0, n_items, grain_size), - [&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) { - // Bounding box of this slab of a wipe tower. - std::vector<GLVolume*> vols; - if (ctxt.color_by_tool()) { - for (size_t i = 0; i < ctxt.number_tools(); ++i) - vols.emplace_back(new_volume(ctxt.color_tool(i))); - } - else - vols = { new_volume(ctxt.color_support()) }; - for (GLVolume *volume : vols) - volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) { - const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer); - for (size_t i = 0; i < vols.size(); ++i) { - GLVolume &vol = *vols[i]; - if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) { - vol.print_zs.push_back(layer.front().print_z); - vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size()); - vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size()); - } - } - for (const WipeTower::ToolChangeResult &extrusions : layer) { - for (size_t i = 1; i < extrusions.extrusions.size();) { - const WipeTower::Extrusion &e = extrusions.extrusions[i]; - if (e.width == 0.) { - ++i; - continue; - } - size_t j = i + 1; - if (ctxt.color_by_tool()) - for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j); - else - for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j); - size_t n_lines = j - i; - Lines lines; - std::vector<double> widths; - std::vector<double> heights; - lines.reserve(n_lines); - widths.reserve(n_lines); - heights.assign(n_lines, extrusions.layer_height); - WipeTower::Extrusion e_prev = extrusions.extrusions[i-1]; - - if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation - e_prev.pos.rotate(ctxt.wipe_tower_angle); - e_prev.pos.translate(ctxt.wipe_tower_pos); - } - - for (; i < j; ++i) { - WipeTower::Extrusion e = extrusions.extrusions[i]; - assert(e.width > 0.f); - if (!extrusions.priming) { - e.pos.rotate(ctxt.wipe_tower_angle); - e.pos.translate(ctxt.wipe_tower_pos); - } - - lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); - widths.emplace_back(e.width); - - e_prev = e; - } - _3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z, - *vols[ctxt.volume_idx(e.tool, 0)]); - } - } - } - for (size_t i = 0; i < vols.size(); ++i) { - GLVolume &vol = *vols[i]; - if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) { - // Store the vertex arrays and restart their containers, - vols[i] = new_volume(vol.color); - GLVolume &vol_new = *vols[i]; - // Assign the large pre-allocated buffers to the new GLVolume. - vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); - // Copy the content back to the old GLVolume. - vol.indexed_vertex_array = vol_new.indexed_vertex_array; - // Finalize a bounding box of the old GLVolume. - vol.bounding_box = vol.indexed_vertex_array.bounding_box(); - // Clear the buffers, but keep them pre-allocated. - vol_new.indexed_vertex_array.clear(); - // Just make sure that clear did not clear the reserved memory. - vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); - } - } - for (GLVolume *vol : vols) { - vol->bounding_box = vol->indexed_vertex_array.bounding_box(); - vol->indexed_vertex_array.shrink_to_fit(); - } - }); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; - // Remove empty volumes from the newly added volumes. - m_volumes.volumes.erase( - std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), - [](const GLVolume *volume) { return volume->empty(); }), - m_volumes.volumes.end()); - for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i) - m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); - - BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; -} - -static inline int hex_digit_to_int(const char c) -{ - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - -void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) -{ - // helper functions to select data in dependence of the extrusion view type - struct Helper - { - static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path) - { - switch (type) - { - case GCodePreviewData::Extrusion::FeatureType: - return (float)path.role(); - case GCodePreviewData::Extrusion::Height: - return path.height; - case GCodePreviewData::Extrusion::Width: - return path.width; - case GCodePreviewData::Extrusion::Feedrate: - return path.feedrate; - case GCodePreviewData::Extrusion::VolumetricRate: - return path.feedrate * (float)path.mm3_per_mm; - case GCodePreviewData::Extrusion::Tool: - return (float)path.extruder_id; - default: - return 0.0f; - } - - return 0.0f; - } - - static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value) - { - switch (data.extrusion.view_type) - { - case GCodePreviewData::Extrusion::FeatureType: - return data.get_extrusion_role_color((ExtrusionRole)(int)value); - case GCodePreviewData::Extrusion::Height: - return data.get_height_color(value); - case GCodePreviewData::Extrusion::Width: - return data.get_width_color(value); - case GCodePreviewData::Extrusion::Feedrate: - return data.get_feedrate_color(value); - case GCodePreviewData::Extrusion::VolumetricRate: - 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)); - return color; - } - default: - return GCodePreviewData::Color::Dummy; - } - - return GCodePreviewData::Color::Dummy; - } - }; - - // Helper structure for filters - struct Filter - { - float value; - ExtrusionRole role; - GLVolume* volume; - - Filter(float value, ExtrusionRole role) - : value(value) - , role(role) - , volume(nullptr) - { - } - - bool operator == (const Filter& other) const - { - if (value != other.value) - return false; - - if (role != other.role) - return false; - - return true; - } - }; - - typedef std::vector<Filter> FiltersList; - size_t initial_volumes_count = m_volumes.volumes.size(); - - // detects filters - FiltersList filters; - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - { - for (const ExtrusionPath& path : layer.paths) - { - ExtrusionRole role = path.role(); - float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); - if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end()) - filters.emplace_back(path_filter, role); - } - } - - // nothing to render, return - if (filters.empty()) - return; - - // creates a new volume for each filter - for (Filter& filter : filters) - { - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes.volumes.size()); - GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba); - if (volume != nullptr) - { - filter.volume = volume; - volume->is_extrusion_path = true; - m_volumes.volumes.emplace_back(volume); - } - else - { - // an error occourred - restore to previous state and return - m_gcode_preview_volume_index.first_volumes.pop_back(); - if (initial_volumes_count != m_volumes.volumes.size()) - { - std::vector<GLVolume*>::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; - std::vector<GLVolume*>::iterator end = m_volumes.volumes.end(); - for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it) - { - GLVolume* volume = *it; - delete volume; - } - m_volumes.volumes.erase(begin, end); - return; - } - } - } - - // populates volumes - for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers) - { - for (const ExtrusionPath& path : layer.paths) - { - float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path); - FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role())); - if (filter != filters.end()) - { - filter->volume->print_zs.push_back(layer.z); - filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size()); - filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size()); - - _3DScene::extrusionentity_to_verts(path, layer.z, *filter->volume); - } - } - } - - // finalize volumes and sends geometry to gpu - if (m_volumes.volumes.size() > initial_volumes_count) - { - for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i) - { - GLVolume* volume = m_volumes.volumes[i]; - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); - } - } -} - -void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) -{ - size_t initial_volumes_count = m_volumes.volumes.size(); - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count); - - bool res = true; - switch (preview_data.extrusion.view_type) - { - case GCodePreviewData::Extrusion::Feedrate: - { - res = _travel_paths_by_feedrate(preview_data); - break; - } - case GCodePreviewData::Extrusion::Tool: - { - res = _travel_paths_by_tool(preview_data, tool_colors); - break; - } - default: - { - res = _travel_paths_by_type(preview_data); - break; - } - } - - if (!res) - { - // an error occourred - restore to previous state and return - if (initial_volumes_count != m_volumes.volumes.size()) - { - std::vector<GLVolume*>::iterator begin = m_volumes.volumes.begin() + initial_volumes_count; - std::vector<GLVolume*>::iterator end = m_volumes.volumes.end(); - for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it) - { - GLVolume* volume = *it; - delete volume; - } - m_volumes.volumes.erase(begin, end); - } - - return; - } - - // finalize volumes and sends geometry to gpu - if (m_volumes.volumes.size() > initial_volumes_count) - { - for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i) - { - GLVolume* volume = m_volumes.volumes[i]; - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); - } - } -} - -bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data) -{ - // Helper structure for types - struct Type - { - GCodePreviewData::Travel::EType value; - GLVolume* volume; - - explicit Type(GCodePreviewData::Travel::EType value) - : value(value) - , volume(nullptr) - { - } - - bool operator == (const Type& other) const - { - return value == other.value; - } - }; - - typedef std::vector<Type> TypesList; - - // colors travels by travel type - - // detects types - TypesList types; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end()) - types.emplace_back(polyline.type); - } - - // nothing to render, return - if (types.empty()) - return true; - - // creates a new volume for each type - for (Type& type : types) - { - GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba); - if (volume == nullptr) - return false; - else - { - type.volume = volume; - m_volumes.volumes.emplace_back(volume); - } - } - - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type)); - if (type != types.end()) - { - type->volume->print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2))); - type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size()); - type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size()); - - _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume); - } - } - - return true; -} - -bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data) -{ - // Helper structure for feedrate - struct Feedrate - { - float value; - GLVolume* volume; - - explicit Feedrate(float value) - : value(value) - , volume(nullptr) - { - } - - bool operator == (const Feedrate& other) const - { - return value == other.value; - } - }; - - typedef std::vector<Feedrate> FeedratesList; - - // colors travels by feedrate - - // detects feedrates - FeedratesList feedrates; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end()) - feedrates.emplace_back(polyline.feedrate); - } - - // nothing to render, return - if (feedrates.empty()) - return true; - - // creates a new volume for each feedrate - for (Feedrate& feedrate : feedrates) - { - GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba); - if (volume == nullptr) - return false; - else - { - feedrate.volume = volume; - m_volumes.volumes.emplace_back(volume); - } - } - - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)); - if (feedrate != feedrates.end()) - { - feedrate->volume->print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2))); - feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size()); - feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size()); - - _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume); - } - } - - return true; -} - -bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) -{ - // Helper structure for tool - struct Tool - { - unsigned int value; - GLVolume* volume; - - explicit Tool(unsigned int value) - : value(value) - , volume(nullptr) - { - } - - bool operator == (const Tool& other) const - { - return value == other.value; - } - }; - - typedef std::vector<Tool> ToolsList; - - // colors travels by tool - - // detects tools - ToolsList tools; - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end()) - tools.emplace_back(polyline.extruder_id); - } - - // nothing to render, return - if (tools.empty()) - return true; - - // creates a new volume for each tool - for (Tool& tool : tools) - { - GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4); - if (volume == nullptr) - return false; - else - { - tool.volume = volume; - m_volumes.volumes.emplace_back(volume); - } - } - - // populates volumes - for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines) - { - ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)); - if (tool != tools.end()) - { - tool->volume->print_zs.push_back(unscale<double>(polyline.polyline.bounding_box().min(2))); - tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size()); - tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size()); - - _3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume); - } - } - - return true; -} - -void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data) -{ - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes.volumes.size()); - - // nothing to render, return - if (preview_data.retraction.positions.empty()) - return; - - GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba); - if (volume != nullptr) - { - m_volumes.volumes.emplace_back(volume); - - GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions); - std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position(2) < p2.position(2); }); - - for (const GCodePreviewData::Retraction::Position& position : copy) - { - volume->print_zs.push_back(unscale<double>(position.position(2))); - volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); - - _3DScene::point3_to_verts(position.position, position.width, position.height, *volume); - } - - // finalize volumes and sends geometry to gpu - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); - } -} - -void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data) -{ - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes.volumes.size()); - - // nothing to render, return - if (preview_data.unretraction.positions.empty()) - return; - - GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba); - if (volume != nullptr) - { - m_volumes.volumes.emplace_back(volume); - - GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions); - std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position(2) < p2.position(2); }); - - for (const GCodePreviewData::Retraction::Position& position : copy) - { - volume->print_zs.push_back(unscale<double>(position.position(2))); - volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size()); - volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size()); - - _3DScene::point3_to_verts(position.position, position.width, position.height, *volume); - } - - // finalize volumes and sends geometry to gpu - volume->bounding_box = volume->indexed_vertex_array.bounding_box(); - volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized); - } -} - -void GLCanvas3D::_load_shells() -{ - size_t initial_volumes_count = m_volumes.volumes.size(); - m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count); - - if (m_print->objects().empty()) - // nothing to render, return - return; - - // adds objects' volumes - unsigned int object_id = 0; - for (const PrintObject* obj : m_print->objects()) - { - const ModelObject* model_obj = obj->model_object(); - - std::vector<int> instance_ids(model_obj->instances.size()); - for (int i = 0; i < (int)model_obj->instances.size(); ++i) - { - instance_ids[i] = i; - } - - m_volumes.load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs && m_initialized); - - ++object_id; - } - - // adds wipe tower's volume - double max_z = m_print->objects()[0]->model_object()->get_model()->bounding_box().max(2); - const PrintConfig& config = m_print->config(); - unsigned int extruders_count = config.nozzle_diameter.size(); - if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { - float depth = m_print->get_wipe_tower_depth(); - if (!m_print->is_step_done(psWipeTower)) - depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1) ; - m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, - m_use_VBOs && m_initialized, !m_print->is_step_done(psWipeTower), m_print->config().nozzle_diameter.values[0] * 1.25f * 4.5f); - } -} - -void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data) -{ - unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size(); - for (unsigned int i = 0; i < size; ++i) - { - std::vector<GLVolume*>::iterator begin = m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id; - std::vector<GLVolume*>::iterator end = (i + 1 < size) ? m_volumes.volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes.volumes.end(); - - for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it) - { - GLVolume* volume = *it; - - switch (m_gcode_preview_volume_index.first_volumes[i].type) - { - case GCodePreviewVolumeIndex::Extrusion: - { - if ((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag == erCustom) - volume->zoom_to_volumes = false; - - volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag); - break; - } - case GCodePreviewVolumeIndex::Travel: - { - volume->is_active = preview_data.travel.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Retraction: - { - volume->is_active = preview_data.retraction.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Unretraction: - { - volume->is_active = preview_data.unretraction.is_visible; - volume->zoom_to_volumes = false; - break; - } - case GCodePreviewVolumeIndex::Shell: - { - volume->is_active = preview_data.shell.is_visible; - volume->color[3] = 0.25f; - volume->zoom_to_volumes = false; - break; - } - default: - { - volume->is_active = false; - volume->zoom_to_volumes = false; - break; - } - } - } - } -} - -void GLCanvas3D::_update_toolpath_volumes_outside_state() -{ - // tolerance to avoid false detection at bed edges - static const double tolerance_x = 0.05; - static const double tolerance_y = 0.05; - - BoundingBoxf3 print_volume; - if (m_config != nullptr) - { - const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape")); - if (opt != nullptr) - { - BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values)); - print_volume = BoundingBoxf3(Vec3d(unscale<double>(bed_box_2D.min(0)) - tolerance_x, unscale<double>(bed_box_2D.min(1)) - tolerance_y, 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)) + tolerance_x, unscale<double>(bed_box_2D.max(1)) + tolerance_y, m_config->opt_float("max_print_height"))); - // Allow the objects to protrude below the print bed - print_volume.min(2) = -1e10; - } - } - - for (GLVolume* volume : m_volumes.volumes) - { - volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box) : false; - } -} - -void GLCanvas3D::_show_warning_texture_if_needed() -{ - if (_is_any_volume_outside()) - { - enable_warning_texture(true); - _generate_warning_texture(L("Detected toolpath outside print volume")); - } - else - { - enable_warning_texture(false); - _reset_warning_texture(); - } -} - -void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs) -{ - if (m_model == nullptr) - return; - - std::set<std::string> done; // prevent moving instances twice - bool object_moved = false; - Vec3d wipe_tower_origin = Vec3d::Zero(); - for (int volume_idx : volume_idxs) - { - GLVolume* volume = m_volumes.volumes[volume_idx]; - int obj_idx = volume->object_idx(); - int instance_idx = volume->instance_idx(); - - // prevent moving instances twice - char done_id[64]; - ::sprintf(done_id, "%d_%d", obj_idx, instance_idx); - if (done.find(done_id) != done.end()) - continue; - - done.insert(done_id); - - if (obj_idx < 1000) - { - // Move a regular object. - ModelObject* model_object = m_model->objects[obj_idx]; - if (model_object != nullptr) - { -#if ENABLE_MODELINSTANCE_3D_OFFSET - model_object->instances[instance_idx]->set_offset(volume->get_offset()); -#else - const Vec3d& offset = volume->get_offset(); - model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1)); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - model_object->invalidate_bounding_box(); - update_position_values(); - object_moved = true; - } - } - else if (obj_idx == 1000) - // Move a wipe tower proxy. - wipe_tower_origin = volume->get_offset(); - } - - if (object_moved) - m_on_instance_moved_callback.call(); - - if (wipe_tower_origin != Vec3d::Zero()) - m_on_wipe_tower_moved_callback.call(wipe_tower_origin(0), wipe_tower_origin(1)); -} - -void GLCanvas3D::_on_select(int volume_idx, int object_idx) -{ - int vol_id = -1; - int obj_id = -1; - - if ((volume_idx != -1) && (volume_idx < (int)m_volumes.volumes.size())) - { - if (m_select_by == "volume") - { - if (m_volumes.volumes[volume_idx]->object_idx() != object_idx) - { - set_select_by("object"); - obj_id = m_volumes.volumes[volume_idx]->object_idx(); - vol_id = -1; - } - else - { - obj_id = object_idx; - vol_id = m_volumes.volumes[volume_idx]->volume_idx(); - } - } - else if (m_select_by == "object") - { - obj_id = m_volumes.volumes[volume_idx]->object_idx(); - vol_id = -1; - } - } - - m_on_select_object_callback.call(obj_id, vol_id); - Slic3r::GUI::select_current_volume(obj_id, vol_id); -} - -std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors) -{ - static const float INV_255 = 1.0f / 255.0f; - - std::vector<float> output(colors.size() * 4, 1.0f); - for (size_t i = 0; i < colors.size(); ++i) - { - const std::string& color = colors[i]; - const char* c = color.data() + 1; - if ((color.size() == 7) && (color.front() == '#')) - { - for (size_t j = 0; j < 3; ++j) - { - int digit1 = hex_digit_to_int(*c++); - int digit2 = hex_digit_to_int(*c++); - if ((digit1 == -1) || (digit2 == -1)) - break; - - output[i * 4 + j] = float(digit1 * 16 + digit2) * INV_255; - } - } - } - return output; -} - -void GLCanvas3D::_generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) -{ - if (!set_current()) - return; - - m_legend_texture.generate(preview_data, tool_colors); -} - -void GLCanvas3D::_generate_warning_texture(const std::string& msg) -{ - if (!set_current()) - return; - - m_warning_texture.generate(msg); -} - -void GLCanvas3D::_reset_warning_texture() -{ - if (!set_current()) - return; - - m_warning_texture.reset(); -} - -bool GLCanvas3D::_is_any_volume_outside() const -{ - for (const GLVolume* volume : m_volumes.volumes) - { - if ((volume != nullptr) && volume->is_outside) - return true; - } - - return false; -} - -void GLCanvas3D::_resize_toolbar() const -{ - Size cnv_size = get_canvas_size(); - float zoom = get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - switch (m_toolbar.get_layout_type()) - { - default: - case GLToolbar::Layout::Horizontal: - { - // centers the toolbar on the top edge of the 3d scene - unsigned int toolbar_width = m_toolbar.get_width(); - float top = (0.5f * (float)cnv_size.get_height() - 2.0f) * inv_zoom; - float left = -0.5f * (float)toolbar_width * inv_zoom; - m_toolbar.set_position(top, left); - break; - } - case GLToolbar::Layout::Vertical: - { - // centers the toolbar on the right edge of the 3d scene - unsigned int toolbar_width = m_toolbar.get_width(); - unsigned int toolbar_height = m_toolbar.get_height(); - float top = 0.5f * (float)toolbar_height * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - toolbar_width - 2.0f) * inv_zoom; - m_toolbar.set_position(top, left); - break; - } - } -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp deleted file mode 100644 index 528f73fc1..000000000 --- a/xs/src/slic3r/GUI/GLCanvas3D.hpp +++ /dev/null @@ -1,765 +0,0 @@ -#ifndef slic3r_GLCanvas3D_hpp_ -#define slic3r_GLCanvas3D_hpp_ - -#include "../../slic3r/GUI/3DScene.hpp" -#include "../../slic3r/GUI/GLToolbar.hpp" - -class wxTimer; -class wxSizeEvent; -class wxIdleEvent; -class wxKeyEvent; -class wxMouseEvent; -class wxTimerEvent; -class wxPaintEvent; - -namespace Slic3r { - -class GLShader; -class ExPolygon; - -namespace GUI { - -class GLGizmoBase; - -class GeometryBuffer -{ - std::vector<float> m_vertices; - std::vector<float> m_tex_coords; - -public: - bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords); - bool set_from_lines(const Lines& lines, float z); - - const float* get_vertices() const; - const float* get_tex_coords() const; - - unsigned int get_vertices_count() const; -}; - -class Size -{ - int m_width; - int m_height; - -public: - Size(); - Size(int width, int height); - - int get_width() const; - void set_width(int width); - - int get_height() const; - void set_height(int height); -}; - -class Rect -{ - float m_left; - float m_top; - float m_right; - float m_bottom; - -public: - Rect(); - Rect(float left, float top, float right, float bottom); - - float get_left() const; - void set_left(float left); - - float get_top() const; - void set_top(float top); - - float get_right() const; - void set_right(float right); - - float get_bottom() const; - void set_bottom(float bottom); -}; - -class GLCanvas3D -{ - struct GCodePreviewVolumeIndex - { - enum EType - { - Extrusion, - Travel, - Retraction, - Unretraction, - Shell, - Num_Geometry_Types - }; - - struct FirstVolume - { - EType type; - unsigned int flag; - // Index of the first volume in a GLVolumeCollection. - unsigned int id; - - FirstVolume(EType type, unsigned int flag, unsigned int id) : type(type), flag(flag), id(id) {} - }; - - std::vector<FirstVolume> first_volumes; - - void reset() { first_volumes.clear(); } - }; - - struct Camera - { - enum EType : unsigned char - { - Unknown, -// Perspective, - Ortho, - Num_types - }; - - EType type; - float zoom; - float phi; -// float distance; - Vec3d target; - - private: - float m_theta; - - public: - Camera(); - - std::string get_type_as_string() const; - - float get_theta() const; - void set_theta(float theta); - }; - - class Bed - { - public: - enum EType : unsigned char - { - MK2, - MK3, - Custom, - Num_Types - }; - - private: - EType m_type; - Pointfs m_shape; - BoundingBoxf3 m_bounding_box; - Polygon m_polygon; - GeometryBuffer m_triangles; - GeometryBuffer m_gridlines; - mutable GLTexture m_top_texture; - mutable GLTexture m_bottom_texture; - - public: - Bed(); - - bool is_prusa() const; - bool is_custom() const; - - const Pointfs& get_shape() const; - // Return true if the bed shape changed, so the calee will update the UI. - bool set_shape(const Pointfs& shape); - - const BoundingBoxf3& get_bounding_box() const; - bool contains(const Point& point) const; - Point point_projection(const Point& point) const; - - void render(float theta) const; - - private: - void _calc_bounding_box(); - void _calc_triangles(const ExPolygon& poly); - void _calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox); - EType _detect_type() const; - void _render_mk2(float theta) const; - void _render_mk3(float theta) const; - void _render_prusa(float theta) const; - void _render_custom() const; - static bool _are_equal(const Pointfs& bed_1, const Pointfs& bed_2); - }; - - struct Axes - { - Vec3d origin; - float length; - - Axes(); - - void render(bool depth_test) const; - }; - - class CuttingPlane - { - float m_z; - GeometryBuffer m_lines; - - public: - CuttingPlane(); - - bool set(float z, const ExPolygons& polygons); - - void render(const BoundingBoxf3& bb) const; - - private: - void _render_plane(const BoundingBoxf3& bb) const; - void _render_contour() const; - }; - - class Shader - { - GLShader* m_shader; - - public: - Shader(); - ~Shader(); - - bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - - bool is_initialized() const; - - bool start_using() const; - void stop_using() const; - - void set_uniform(const std::string& name, float value) const; - void set_uniform(const std::string& name, const float* matrix) const; - - const GLShader* get_shader() const; - - private: - void _reset(); - }; - - class LayersEditing - { - public: - enum EState : unsigned char - { - Unknown, - Editing, - Completed, - Num_States - }; - - private: - bool m_use_legacy_opengl; - bool m_enabled; - Shader m_shader; - unsigned int m_z_texture_id; - mutable GLTexture m_tooltip_texture; - mutable GLTexture m_reset_texture; - - public: - EState state; - float band_width; - float strength; - int last_object_id; - float last_z; - unsigned int last_action; - - LayersEditing(); - ~LayersEditing(); - - bool init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename); - - bool is_allowed() const; - void set_use_legacy_opengl(bool use_legacy_opengl); - - bool is_enabled() const; - void set_enabled(bool enabled); - - unsigned int get_z_texture_id() const; - - void render(const GLCanvas3D& canvas, const PrintObject& print_object, const GLVolume& volume) const; - - int get_shader_program_id() const; - - static float get_cursor_z_relative(const GLCanvas3D& canvas); - static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y); - static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y); - static Rect get_bar_rect_screen(const GLCanvas3D& canvas); - static Rect get_reset_rect_screen(const GLCanvas3D& canvas); - static Rect get_bar_rect_viewport(const GLCanvas3D& canvas); - static Rect get_reset_rect_viewport(const GLCanvas3D& canvas); - - private: - bool _is_initialized() const; - 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; - void _render_active_object_annotations(const GLCanvas3D& canvas, const GLVolume& volume, const PrintObject& print_object, const Rect& bar_rect) const; - void _render_profile(const PrintObject& print_object, const Rect& bar_rect) const; - }; - - struct Mouse - { - struct Drag - { - static const Point Invalid_2D_Point; - static const Vec3d Invalid_3D_Point; - - Point start_position_2D; - Vec3d start_position_3D; - Vec3d volume_center_offset; - - bool move_with_shift; - int move_volume_idx; - int gizmo_volume_idx; - - public: - Drag(); - }; - - bool dragging; - Vec2d position; - Drag drag; - - Mouse(); - - void set_start_position_2D_as_invalid(); - void set_start_position_3D_as_invalid(); - - bool is_start_position_2D_defined() const; - bool is_start_position_3D_defined() const; - }; - - class Gizmos - { - static const float OverlayTexturesScale; - static const float OverlayOffsetX; - static const float OverlayGapY; - - public: - enum EType : unsigned char - { - Undefined, - Move, - Scale, - Rotate, - Flatten, - Num_Types - }; - - private: - bool m_enabled; - typedef std::map<EType, GLGizmoBase*> GizmosMap; - GizmosMap m_gizmos; - EType m_current; - - public: - Gizmos(); - ~Gizmos(); - - bool init(GLCanvas3D& parent); - - bool is_enabled() const; - void set_enabled(bool enable); - - void update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); - void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); - void reset_all_states(); - - void set_hover_id(int id); - - bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; - bool grabber_contains_mouse() const; - void update(const Linef3& mouse_ray); - - EType get_current_type() const; - - bool is_running() const; - - bool is_dragging() const; - void start_dragging(const BoundingBoxf3& box); - void stop_dragging(); - - Vec3d get_position() const; - void set_position(const Vec3d& position); - - float get_scale() const; - void set_scale(float scale); - - float get_angle_z() const; - void set_angle_z(float angle_z); - - void set_flattening_data(const ModelObject* model_object); - Vec3d get_flattening_normal() const; - - void render_current_gizmo(const BoundingBoxf3& box) const; - - void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const; - void render_overlay(const GLCanvas3D& canvas) const; - - private: - void _reset(); - - void _render_overlay(const GLCanvas3D& canvas) const; - void _render_current_gizmo(const BoundingBoxf3& box) const; - - float _get_total_overlay_height() const; - GLGizmoBase* _get_current() const; - }; - - class WarningTexture : public GUI::GLTexture - { - static const unsigned char Background_Color[3]; - static const unsigned char Opacity; - - int m_original_width; - int m_original_height; - - public: - WarningTexture(); - - bool generate(const std::string& msg); - - void render(const GLCanvas3D& canvas) const; - }; - - class LegendTexture : public GUI::GLTexture - { - static const int Px_Title_Offset = 5; - static const int Px_Text_Offset = 5; - static const int Px_Square = 20; - static const int Px_Square_Contour = 1; - static const int Px_Border = Px_Square / 2; - static const unsigned char Squares_Border_Color[3]; - static const unsigned char Background_Color[3]; - static const unsigned char Opacity; - - int m_original_width; - int m_original_height; - - public: - LegendTexture(); - - bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); - - void render(const GLCanvas3D& canvas) const; - }; - - wxGLCanvas* m_canvas; - wxGLContext* m_context; - LegendTexture m_legend_texture; - WarningTexture m_warning_texture; - wxTimer* m_timer; - Camera m_camera; - Bed m_bed; - Axes m_axes; - CuttingPlane m_cutting_plane; - LayersEditing m_layers_editing; - Shader m_shader; - Mouse m_mouse; - mutable Gizmos m_gizmos; - mutable GLToolbar m_toolbar; - - mutable GLVolumeCollection m_volumes; - DynamicPrintConfig* m_config; - Print* m_print; - Model* m_model; - - bool m_dirty; - bool m_initialized; - bool m_use_VBOs; - bool m_force_zoom_to_bed_enabled; - bool m_apply_zoom_to_volumes_filter; - mutable int m_hover_volume_id; - bool m_toolbar_action_running; - bool m_warning_texture_enabled; - bool m_legend_texture_enabled; - bool m_picking_enabled; - bool m_moving_enabled; - bool m_shader_enabled; - bool m_dynamic_background_enabled; - bool m_multisample_allowed; - - std::string m_color_by; - std::string m_select_by; - std::string m_drag_by; - - bool m_reload_delayed; - std::vector<std::vector<int>> m_objects_volumes_idxs; - std::vector<int> m_objects_selections; - - GCodePreviewVolumeIndex m_gcode_preview_volume_index; - - PerlCallback m_on_viewport_changed_callback; - PerlCallback m_on_double_click_callback; - PerlCallback m_on_right_click_callback; - PerlCallback m_on_select_object_callback; - PerlCallback m_on_model_update_callback; - PerlCallback m_on_remove_object_callback; - PerlCallback m_on_arrange_callback; - PerlCallback m_on_rotate_object_left_callback; - PerlCallback m_on_rotate_object_right_callback; - PerlCallback m_on_scale_object_uniformly_callback; - PerlCallback m_on_increase_objects_callback; - PerlCallback m_on_decrease_objects_callback; - PerlCallback m_on_instance_moved_callback; - PerlCallback m_on_wipe_tower_moved_callback; - PerlCallback m_on_enable_action_buttons_callback; - PerlCallback m_on_gizmo_scale_uniformly_callback; - PerlCallback m_on_gizmo_rotate_callback; - PerlCallback m_on_gizmo_flatten_callback; - PerlCallback m_on_update_geometry_info_callback; - - PerlCallback m_action_add_callback; - PerlCallback m_action_delete_callback; - PerlCallback m_action_deleteall_callback; - PerlCallback m_action_arrange_callback; - PerlCallback m_action_more_callback; - PerlCallback m_action_fewer_callback; - PerlCallback m_action_split_callback; - PerlCallback m_action_cut_callback; - PerlCallback m_action_settings_callback; - PerlCallback m_action_layersediting_callback; - PerlCallback m_action_selectbyparts_callback; - -public: - GLCanvas3D(wxGLCanvas* canvas); - ~GLCanvas3D(); - - bool init(bool useVBOs, bool use_legacy_opengl); - - bool set_current(); - - void set_as_dirty(); - - unsigned int get_volumes_count() const; - void reset_volumes(); - void deselect_volumes(); - void select_volume(unsigned int id); - void update_volumes_selection(const std::vector<int>& selections); - int check_volumes_outside_state(const DynamicPrintConfig* config) const; - bool move_volume_up(unsigned int id); - bool move_volume_down(unsigned int id); - - void set_objects_selections(const std::vector<int>& selections); - - void set_config(DynamicPrintConfig* config); - void set_print(Print* print); - void set_model(Model* model); - - // Set the bed shape to a single closed 2D polygon(array of two element arrays), - // triangulate the bed and store the triangles into m_bed.m_triangles, - // fills the m_bed.m_grid_lines and sets m_bed.m_origin. - // Sets m_bed.m_polygon to limit the object placement. - void set_bed_shape(const Pointfs& shape); - // Used by ObjectCutDialog and ObjectPartsPanel to generate a rectangular ground plane to support the scene objects. - void set_auto_bed_shape(); - - void set_axes_length(float length); - - void set_cutting_plane(float z, const ExPolygons& polygons); - - void set_color_by(const std::string& value); - void set_select_by(const std::string& value); - void set_drag_by(const std::string& value); - - const std::string& get_select_by() const; - const std::string& get_drag_by() const; - - float get_camera_zoom() const; - - BoundingBoxf3 volumes_bounding_box() const; - - bool is_layers_editing_enabled() const; - bool is_layers_editing_allowed() const; - bool is_shader_enabled() const; - - bool is_reload_delayed() const; - - void enable_layers_editing(bool enable); - void enable_warning_texture(bool enable); - void enable_legend_texture(bool enable); - void enable_picking(bool enable); - void enable_moving(bool enable); - void enable_gizmos(bool enable); - void enable_toolbar(bool enable); - void enable_shader(bool enable); - void enable_force_zoom_to_bed(bool enable); - void enable_dynamic_background(bool enable); - void allow_multisample(bool allow); - - void enable_toolbar_item(const std::string& name, bool enable); - bool is_toolbar_item_pressed(const std::string& name) const; - - void zoom_to_bed(); - void zoom_to_volumes(); - void select_view(const std::string& direction); - void set_viewport_from_scene(const GLCanvas3D& other); - - void update_volumes_colors_by_extruder(); - void update_gizmos_data(); - - void render(); - - std::vector<double> get_current_print_zs(bool active_only) const; - void set_toolpaths_range(double low, double high); - - std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs); - std::vector<int> load_object(const Model& model, int obj_idx); - - int get_first_volume_id(int obj_idx) const; - int get_in_object_volume_id(int scene_vol_idx) const; - - void reload_scene(bool force); - - void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors); - void load_preview(const std::vector<std::string>& str_tool_colors); - - void register_on_viewport_changed_callback(void* callback); - void register_on_double_click_callback(void* callback); - void register_on_right_click_callback(void* callback); - void register_on_select_object_callback(void* callback); - void register_on_model_update_callback(void* callback); - void register_on_remove_object_callback(void* callback); - void register_on_arrange_callback(void* callback); - void register_on_rotate_object_left_callback(void* callback); - void register_on_rotate_object_right_callback(void* callback); - void register_on_scale_object_uniformly_callback(void* callback); - void register_on_increase_objects_callback(void* callback); - void register_on_decrease_objects_callback(void* callback); - void register_on_instance_moved_callback(void* callback); - void register_on_wipe_tower_moved_callback(void* callback); - void register_on_enable_action_buttons_callback(void* callback); - void register_on_gizmo_scale_uniformly_callback(void* callback); - void register_on_gizmo_rotate_callback(void* callback); - void register_on_gizmo_flatten_callback(void* callback); - void register_on_update_geometry_info_callback(void* callback); - - void register_action_add_callback(void* callback); - void register_action_delete_callback(void* callback); - void register_action_deleteall_callback(void* callback); - void register_action_arrange_callback(void* callback); - void register_action_more_callback(void* callback); - void register_action_fewer_callback(void* callback); - void register_action_split_callback(void* callback); - void register_action_cut_callback(void* callback); - void register_action_settings_callback(void* callback); - void register_action_layersediting_callback(void* callback); - void register_action_selectbyparts_callback(void* callback); - - void bind_event_handlers(); - void unbind_event_handlers(); - - void on_size(wxSizeEvent& evt); - void on_idle(wxIdleEvent& evt); - void on_char(wxKeyEvent& evt); - void on_mouse_wheel(wxMouseEvent& evt); - void on_timer(wxTimerEvent& evt); - void on_mouse(wxMouseEvent& evt); - void on_paint(wxPaintEvent& evt); - void on_key_down(wxKeyEvent& evt); - - Size get_canvas_size() const; - Point get_local_mouse_position() const; - - void reset_legend_texture(); - - void set_tooltip(const std::string& tooltip); - -private: - bool _is_shown_on_screen() const; - void _force_zoom_to_bed(); - - bool _init_toolbar(); - - void _resize(unsigned int w, unsigned int h); - - BoundingBoxf3 _max_bounding_box() const; - BoundingBoxf3 _selected_volumes_bounding_box() const; - - void _zoom_to_bounding_box(const BoundingBoxf3& bbox); - float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const; - - void _deregister_callbacks(); - - void _mark_volumes_for_layer_height() const; - void _refresh_if_shown_on_screen(); - - void _camera_tranform() const; - void _picking_pass() const; - void _render_background() const; - void _render_bed(float theta) const; - void _render_axes(bool depth_test) const; - void _render_objects() const; - void _render_cutting_plane() const; - void _render_warning_texture() const; - void _render_legend_texture() const; - void _render_layer_editing_overlay() const; - void _render_volumes(bool fake_colors) const; - void _render_current_gizmo() const; - void _render_gizmos_overlay() const; - void _render_toolbar() const; - - float _get_layers_editing_cursor_z_relative() const; - void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); - - // Convert the screen space coordinate to an object space coordinate. - // If the Z screen space coordinate is not provided, a depth buffer value is substituted. - Vec3d _mouse_to_3d(const Point& mouse_pos, float* z = nullptr); - - // Convert the screen space coordinate to world coordinate on the bed. - Vec3d _mouse_to_bed_3d(const Point& mouse_pos); - - // Returns the view ray line, in world coordinate, at the given mouse position. - Linef3 mouse_ray(const Point& mouse_pos); - - void _start_timer(); - void _stop_timer(); - - int _get_first_selected_object_id() const; - int _get_first_selected_volume_id(int object_id) const; - - // Create 3D thick extrusion lines for a skirt and brim. - // Adds a new Slic3r::GUI::3DScene::Volume to volumes. - void _load_print_toolpaths(); - // Create 3D thick extrusion lines for object forming extrusions. - // Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes, - // one for perimeters, one for infill and one for supports. - void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors); - // Create 3D thick extrusion lines for wipe tower extrusions - void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors); - - // generates gcode extrusion paths geometry - void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); - // generates gcode travel paths geometry - void _load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); - bool _travel_paths_by_type(const GCodePreviewData& preview_data); - bool _travel_paths_by_feedrate(const GCodePreviewData& preview_data); - bool _travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); - // generates gcode retractions geometry - void _load_gcode_retractions(const GCodePreviewData& preview_data); - // generates gcode unretractions geometry - void _load_gcode_unretractions(const GCodePreviewData& preview_data); - // generates objects and wipe tower geometry - void _load_shells(); - // sets gcode geometry visibility according to user selection - void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data); - void _update_toolpath_volumes_outside_state(); - void _show_warning_texture_if_needed(); - - void _on_move(const std::vector<int>& volume_idxs); - void _on_select(int volume_idx, int object_idx); - - // generates the legend texture in dependence of the current shown view type - void _generate_legend_texture(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors); - - // generates a warning texture containing the given message - void _generate_warning_texture(const std::string& msg); - void _reset_warning_texture(); - - bool _is_any_volume_outside() const; - - void _resize_toolbar() const; - - static std::vector<float> _parse_colors(const std::vector<std::string>& colors); -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLCanvas3D_hpp_ diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp deleted file mode 100644 index 495f49425..000000000 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp +++ /dev/null @@ -1,819 +0,0 @@ -#include "GLCanvas3DManager.hpp" -#include "../../slic3r/GUI/GUI.hpp" -#include "../../slic3r/GUI/AppConfig.hpp" -#include "../../slic3r/GUI/GLCanvas3D.hpp" - -#include <GL/glew.h> - -#include <boost/algorithm/string/split.hpp> -#include <boost/algorithm/string/classification.hpp> - -#include <wx/glcanvas.h> -#include <wx/timer.h> - -#include <vector> -#include <string> -#include <iostream> - -namespace Slic3r { -namespace GUI { - -GLCanvas3DManager::GLInfo::GLInfo() - : version("") - , glsl_version("") - , vendor("") - , renderer("") -{ -} - -void GLCanvas3DManager::GLInfo::detect() -{ - const char* data = (const char*)::glGetString(GL_VERSION); - if (data != nullptr) - version = data; - - data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION); - if (data != nullptr) - glsl_version = data; - - data = (const char*)::glGetString(GL_VENDOR); - if (data != nullptr) - vendor = data; - - data = (const char*)::glGetString(GL_RENDERER); - if (data != nullptr) - renderer = data; -} - -bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const -{ - std::vector<std::string> tokens; - boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on); - - if (tokens.empty()) - return false; - - std::vector<std::string> numbers; - boost::split(numbers, tokens[0], boost::is_any_of("."), boost::token_compress_on); - - unsigned int gl_major = 0; - unsigned int gl_minor = 0; - - if (numbers.size() > 0) - gl_major = ::atoi(numbers[0].c_str()); - - if (numbers.size() > 1) - gl_minor = ::atoi(numbers[1].c_str()); - - if (gl_major < major) - return false; - else if (gl_major > major) - return true; - else - return gl_minor >= minor; -} - -std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const -{ - std::stringstream out; - - std::string h2_start = format_as_html ? "<b>" : ""; - std::string h2_end = format_as_html ? "</b>" : ""; - std::string b_start = format_as_html ? "<b>" : ""; - std::string b_end = format_as_html ? "</b>" : ""; - std::string line_end = format_as_html ? "<br>" : "\n"; - - out << h2_start << "OpenGL installation" << h2_end << line_end; - out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end; - out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end; - out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end; - out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end; - - if (extensions) - { - std::vector<std::string> extensions_list; - std::string extensions_str = (const char*)::glGetString(GL_EXTENSIONS); - boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off); - - if (!extensions_list.empty()) - { - out << h2_start << "Installed extensions:" << h2_end << line_end; - - std::sort(extensions_list.begin(), extensions_list.end()); - for (const std::string& ext : extensions_list) - { - out << ext << line_end; - } - } - } - - return out.str(); -} - -GLCanvas3DManager::GLCanvas3DManager() - : m_current(nullptr) - , m_gl_initialized(false) - , m_use_legacy_opengl(false) - , m_use_VBOs(false) -{ -} - -bool GLCanvas3DManager::add(wxGLCanvas* canvas) -{ - if (canvas == nullptr) - return false; - - if (_get_canvas(canvas) != m_canvases.end()) - return false; - - GLCanvas3D* canvas3D = new GLCanvas3D(canvas); - if (canvas3D == nullptr) - return false; - - canvas3D->bind_event_handlers(); - m_canvases.insert(CanvasesMap::value_type(canvas, canvas3D)); - - return true; -} - -bool GLCanvas3DManager::remove(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it == m_canvases.end()) - return false; - - it->second->unbind_event_handlers(); - delete it->second; - m_canvases.erase(it); - - return true; -} - -void GLCanvas3DManager::remove_all() -{ - for (CanvasesMap::value_type& item : m_canvases) - { - item.second->unbind_event_handlers(); - delete item.second; - } - m_canvases.clear(); -} - -unsigned int GLCanvas3DManager::count() const -{ - return (unsigned int)m_canvases.size(); -} - -void GLCanvas3DManager::init_gl() -{ - if (!m_gl_initialized) - { - glewInit(); - m_gl_info.detect(); - const AppConfig* config = GUI::get_app_config(); - m_use_legacy_opengl = (config == nullptr) || (config->get("use_legacy_opengl") == "1"); - m_use_VBOs = !m_use_legacy_opengl && m_gl_info.is_version_greater_or_equal_to(2, 0); - m_gl_initialized = true; - } -} - -std::string GLCanvas3DManager::get_gl_info(bool format_as_html, bool extensions) const -{ - return m_gl_info.to_string(format_as_html, extensions); -} - -bool GLCanvas3DManager::use_VBOs() const -{ - return m_use_VBOs; -} - -bool GLCanvas3DManager::init(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - return (it->second != nullptr) ? _init(*it->second) : false; - else - return false; -} - -void GLCanvas3DManager::set_as_dirty(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_as_dirty(); -} - -unsigned int GLCanvas3DManager::get_volumes_count(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_volumes_count() : 0; -} - -void GLCanvas3DManager::reset_volumes(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->reset_volumes(); -} - -void GLCanvas3DManager::deselect_volumes(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->deselect_volumes(); -} - -void GLCanvas3DManager::select_volume(wxGLCanvas* canvas, unsigned int id) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->select_volume(id); -} - -void GLCanvas3DManager::update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->update_volumes_selection(selections); -} - -int GLCanvas3DManager::check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->check_volumes_outside_state(config) : false; -} - -bool GLCanvas3DManager::move_volume_up(wxGLCanvas* canvas, unsigned int id) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->move_volume_up(id) : false; -} - -bool GLCanvas3DManager::move_volume_down(wxGLCanvas* canvas, unsigned int id) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->move_volume_down(id) : false; -} - -void GLCanvas3DManager::set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_objects_selections(selections); -} - -void GLCanvas3DManager::set_config(wxGLCanvas* canvas, DynamicPrintConfig* config) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_config(config); -} - -void GLCanvas3DManager::set_print(wxGLCanvas* canvas, Print* print) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_print(print); -} - -void GLCanvas3DManager::set_model(wxGLCanvas* canvas, Model* model) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_model(model); -} - -void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_bed_shape(shape); -} - -void GLCanvas3DManager::set_auto_bed_shape(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_auto_bed_shape(); -} - -BoundingBoxf3 GLCanvas3DManager::get_volumes_bounding_box(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->volumes_bounding_box() : BoundingBoxf3(); -} - -void GLCanvas3DManager::set_axes_length(wxGLCanvas* canvas, float length) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_axes_length(length); -} - -void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_cutting_plane(z, polygons); -} - -void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& value) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_color_by(value); -} - -void GLCanvas3DManager::set_select_by(wxGLCanvas* canvas, const std::string& value) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_select_by(value); -} - -void GLCanvas3DManager::set_drag_by(wxGLCanvas* canvas, const std::string& value) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_drag_by(value); -} - -std::string GLCanvas3DManager::get_select_by(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_select_by() : ""; -} - -bool GLCanvas3DManager::is_layers_editing_enabled(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_layers_editing_enabled() : false; -} - -bool GLCanvas3DManager::is_layers_editing_allowed(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_layers_editing_allowed() : false; -} - -bool GLCanvas3DManager::is_shader_enabled(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_shader_enabled() : false; -} - -bool GLCanvas3DManager::is_reload_delayed(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_reload_delayed() : false; -} - -void GLCanvas3DManager::enable_layers_editing(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_layers_editing(enable); -} - -void GLCanvas3DManager::enable_warning_texture(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_warning_texture(enable); -} - -void GLCanvas3DManager::enable_legend_texture(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_legend_texture(enable); -} - -void GLCanvas3DManager::enable_picking(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_picking(enable); -} - -void GLCanvas3DManager::enable_moving(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_moving(enable); -} - -void GLCanvas3DManager::enable_gizmos(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_gizmos(enable); -} - -void GLCanvas3DManager::enable_toolbar(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_toolbar(enable); -} - -void GLCanvas3DManager::enable_shader(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_shader(enable); -} - -void GLCanvas3DManager::enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_force_zoom_to_bed(enable); -} - -void GLCanvas3DManager::enable_dynamic_background(wxGLCanvas* canvas, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_dynamic_background(enable); -} - -void GLCanvas3DManager::allow_multisample(wxGLCanvas* canvas, bool allow) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->allow_multisample(allow); -} - -void GLCanvas3DManager::enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->enable_toolbar_item(name, enable); -} - -bool GLCanvas3DManager::is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->is_toolbar_item_pressed(name) : false; -} - -void GLCanvas3DManager::zoom_to_bed(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->zoom_to_bed(); -} - -void GLCanvas3DManager::zoom_to_volumes(wxGLCanvas* canvas) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->zoom_to_volumes(); -} - -void GLCanvas3DManager::select_view(wxGLCanvas* canvas, const std::string& direction) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->select_view(direction); -} - -void GLCanvas3DManager::set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - { - CanvasesMap::iterator other_it = _get_canvas(other); - if (other_it != m_canvases.end()) - it->second->set_viewport_from_scene(*other_it->second); - } -} - -void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->update_volumes_colors_by_extruder(); -} - -void GLCanvas3DManager::update_gizmos_data(wxGLCanvas* canvas) -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->update_gizmos_data(); -} - -void GLCanvas3DManager::render(wxGLCanvas* canvas) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->render(); -} - -std::vector<double> GLCanvas3DManager::get_current_print_zs(wxGLCanvas* canvas, bool active_only) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_current_print_zs(active_only) : std::vector<double>(); -} - -void GLCanvas3DManager::set_toolpaths_range(wxGLCanvas* canvas, double low, double high) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->set_toolpaths_range(low, high); -} - -std::vector<int> GLCanvas3DManager::load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs) -{ - if (model_object == nullptr) - return std::vector<int>(); - - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->load_object(*model_object, obj_idx, instance_idxs) : std::vector<int>(); -} - -std::vector<int> GLCanvas3DManager::load_object(wxGLCanvas* canvas, const Model* model, int obj_idx) -{ - if (model == nullptr) - return std::vector<int>(); - - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->load_object(*model, obj_idx) : std::vector<int>(); -} - -int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1; -} - -int GLCanvas3DManager::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const -{ - CanvasesMap::const_iterator it = _get_canvas(canvas); - return (it != m_canvases.end()) ? it->second->get_in_object_volume_id(scene_vol_idx) : -1; -} - -void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->reload_scene(force); -} - -void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors) -{ - if (preview_data == nullptr) - return; - - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->load_gcode_preview(*preview_data, str_tool_colors); -} - -void GLCanvas3DManager::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->load_preview(str_tool_colors); -} - -void GLCanvas3DManager::reset_legend_texture() -{ - for (CanvasesMap::value_type& canvas : m_canvases) - { - if (canvas.second != nullptr) - canvas.second->reset_legend_texture(); - } -} - -void GLCanvas3DManager::register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_viewport_changed_callback(callback); -} - -void GLCanvas3DManager::register_on_double_click_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_double_click_callback(callback); -} - -void GLCanvas3DManager::register_on_right_click_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_right_click_callback(callback); -} - -void GLCanvas3DManager::register_on_select_object_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_select_object_callback(callback); -} - -void GLCanvas3DManager::register_on_model_update_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_model_update_callback(callback); -} - -void GLCanvas3DManager::register_on_remove_object_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_remove_object_callback(callback); -} - -void GLCanvas3DManager::register_on_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_arrange_callback(callback); -} - -void GLCanvas3DManager::register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_rotate_object_left_callback(callback); -} - -void GLCanvas3DManager::register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_rotate_object_right_callback(callback); -} - -void GLCanvas3DManager::register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_scale_object_uniformly_callback(callback); -} - -void GLCanvas3DManager::register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_increase_objects_callback(callback); -} - -void GLCanvas3DManager::register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_decrease_objects_callback(callback); -} - -void GLCanvas3DManager::register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_instance_moved_callback(callback); -} - -void GLCanvas3DManager::register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_wipe_tower_moved_callback(callback); -} - -void GLCanvas3DManager::register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_enable_action_buttons_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_scale_uniformly_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_rotate_callback(callback); -} - -void GLCanvas3DManager::register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_gizmo_flatten_callback(callback); -} - -void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_on_update_geometry_info_callback(callback); -} - -void GLCanvas3DManager::register_action_add_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_add_callback(callback); -} - -void GLCanvas3DManager::register_action_delete_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_delete_callback(callback); -} - -void GLCanvas3DManager::register_action_deleteall_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_deleteall_callback(callback); -} - -void GLCanvas3DManager::register_action_arrange_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_arrange_callback(callback); -} - -void GLCanvas3DManager::register_action_more_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_more_callback(callback); -} - -void GLCanvas3DManager::register_action_fewer_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_fewer_callback(callback); -} - -void GLCanvas3DManager::register_action_split_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_split_callback(callback); -} - -void GLCanvas3DManager::register_action_cut_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_cut_callback(callback); -} - -void GLCanvas3DManager::register_action_settings_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_settings_callback(callback); -} - -void GLCanvas3DManager::register_action_layersediting_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_layersediting_callback(callback); -} - -void GLCanvas3DManager::register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback) -{ - CanvasesMap::iterator it = _get_canvas(canvas); - if (it != m_canvases.end()) - it->second->register_action_selectbyparts_callback(callback); -} - -GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) -{ - return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); -} - -GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) const -{ - return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas); -} - -bool GLCanvas3DManager::_init(GLCanvas3D& canvas) -{ - if (!m_gl_initialized) - init_gl(); - - return canvas.init(m_use_VBOs, m_use_legacy_opengl); -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp deleted file mode 100644 index 4922b6171..000000000 --- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp +++ /dev/null @@ -1,192 +0,0 @@ -#ifndef slic3r_GLCanvas3DManager_hpp_ -#define slic3r_GLCanvas3DManager_hpp_ - -#include "../../libslic3r/BoundingBox.hpp" - -#include <map> -#include <vector> - -class wxGLCanvas; -class wxGLContext; - -namespace Slic3r { - -class DynamicPrintConfig; -class Print; -class Model; -class ExPolygon; -typedef std::vector<ExPolygon> ExPolygons; -class ModelObject; -class PrintObject; -class GCodePreviewData; - -namespace GUI { - -class GLCanvas3D; - -class GLCanvas3DManager -{ - struct GLInfo - { - std::string version; - std::string glsl_version; - std::string vendor; - std::string renderer; - - GLInfo(); - - void detect(); - bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const; - - std::string to_string(bool format_as_html, bool extensions) const; - }; - - typedef std::map<wxGLCanvas*, GLCanvas3D*> CanvasesMap; - - CanvasesMap m_canvases; - wxGLCanvas* m_current; - GLInfo m_gl_info; - bool m_gl_initialized; - bool m_use_legacy_opengl; - bool m_use_VBOs; - -public: - GLCanvas3DManager(); - - bool add(wxGLCanvas* canvas); - bool remove(wxGLCanvas* canvas); - - void remove_all(); - - unsigned int count() const; - - void init_gl(); - std::string get_gl_info(bool format_as_html, bool extensions) const; - - bool use_VBOs() const; - bool layer_editing_allowed() const; - - bool init(wxGLCanvas* canvas); - - void set_as_dirty(wxGLCanvas* canvas); - - unsigned int get_volumes_count(wxGLCanvas* canvas) const; - void reset_volumes(wxGLCanvas* canvas); - void deselect_volumes(wxGLCanvas* canvas); - void select_volume(wxGLCanvas* canvas, unsigned int id); - void update_volumes_selection(wxGLCanvas* canvas, const std::vector<int>& selections); - int check_volumes_outside_state(wxGLCanvas* canvas, const DynamicPrintConfig* config) const; - bool move_volume_up(wxGLCanvas* canvas, unsigned int id); - bool move_volume_down(wxGLCanvas* canvas, unsigned int id); - - void set_objects_selections(wxGLCanvas* canvas, const std::vector<int>& selections); - - void set_config(wxGLCanvas* canvas, DynamicPrintConfig* config); - void set_print(wxGLCanvas* canvas, Print* print); - void set_model(wxGLCanvas* canvas, Model* model); - - void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape); - void set_auto_bed_shape(wxGLCanvas* canvas); - - BoundingBoxf3 get_volumes_bounding_box(wxGLCanvas* canvas); - - void set_axes_length(wxGLCanvas* canvas, float length); - - void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons); - - void set_color_by(wxGLCanvas* canvas, const std::string& value); - void set_select_by(wxGLCanvas* canvas, const std::string& value); - void set_drag_by(wxGLCanvas* canvas, const std::string& value); - - std::string get_select_by(wxGLCanvas* canvas) const; - - bool is_layers_editing_enabled(wxGLCanvas* canvas) const; - bool is_layers_editing_allowed(wxGLCanvas* canvas) const; - bool is_shader_enabled(wxGLCanvas* canvas) const; - - bool is_reload_delayed(wxGLCanvas* canvas) const; - - void enable_layers_editing(wxGLCanvas* canvas, bool enable); - void enable_warning_texture(wxGLCanvas* canvas, bool enable); - void enable_legend_texture(wxGLCanvas* canvas, bool enable); - void enable_picking(wxGLCanvas* canvas, bool enable); - void enable_moving(wxGLCanvas* canvas, bool enable); - void enable_gizmos(wxGLCanvas* canvas, bool enable); - void enable_toolbar(wxGLCanvas* canvas, bool enable); - void enable_shader(wxGLCanvas* canvas, bool enable); - void enable_force_zoom_to_bed(wxGLCanvas* canvas, bool enable); - void enable_dynamic_background(wxGLCanvas* canvas, bool enable); - void allow_multisample(wxGLCanvas* canvas, bool allow); - - void enable_toolbar_item(wxGLCanvas* canvas, const std::string& name, bool enable); - bool is_toolbar_item_pressed(wxGLCanvas* canvas, const std::string& name) const; - - void zoom_to_bed(wxGLCanvas* canvas); - void zoom_to_volumes(wxGLCanvas* canvas); - void select_view(wxGLCanvas* canvas, const std::string& direction); - void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other); - - void update_volumes_colors_by_extruder(wxGLCanvas* canvas); - void update_gizmos_data(wxGLCanvas* canvas); - - void render(wxGLCanvas* canvas) const; - - std::vector<double> get_current_print_zs(wxGLCanvas* canvas, bool active_only) const; - void set_toolpaths_range(wxGLCanvas* canvas, double low, double high); - - std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs); - std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx); - - int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const; - int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const; - - void reload_scene(wxGLCanvas* canvas, bool force); - - void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors); - void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors); - - void reset_legend_texture(); - - void register_on_viewport_changed_callback(wxGLCanvas* canvas, void* callback); - void register_on_double_click_callback(wxGLCanvas* canvas, void* callback); - void register_on_right_click_callback(wxGLCanvas* canvas, void* callback); - void register_on_select_object_callback(wxGLCanvas* canvas, void* callback); - void register_on_model_update_callback(wxGLCanvas* canvas, void* callback); - void register_on_remove_object_callback(wxGLCanvas* canvas, void* callback); - void register_on_arrange_callback(wxGLCanvas* canvas, void* callback); - void register_on_rotate_object_left_callback(wxGLCanvas* canvas, void* callback); - void register_on_rotate_object_right_callback(wxGLCanvas* canvas, void* callback); - void register_on_scale_object_uniformly_callback(wxGLCanvas* canvas, void* callback); - void register_on_increase_objects_callback(wxGLCanvas* canvas, void* callback); - void register_on_decrease_objects_callback(wxGLCanvas* canvas, void* callback); - void register_on_instance_moved_callback(wxGLCanvas* canvas, void* callback); - void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback); - void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback); - void register_on_gizmo_flatten_callback(wxGLCanvas* canvas, void* callback); - void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback); - - void register_action_add_callback(wxGLCanvas* canvas, void* callback); - void register_action_delete_callback(wxGLCanvas* canvas, void* callback); - void register_action_deleteall_callback(wxGLCanvas* canvas, void* callback); - void register_action_arrange_callback(wxGLCanvas* canvas, void* callback); - void register_action_more_callback(wxGLCanvas* canvas, void* callback); - void register_action_fewer_callback(wxGLCanvas* canvas, void* callback); - void register_action_split_callback(wxGLCanvas* canvas, void* callback); - void register_action_cut_callback(wxGLCanvas* canvas, void* callback); - void register_action_settings_callback(wxGLCanvas* canvas, void* callback); - void register_action_layersediting_callback(wxGLCanvas* canvas, void* callback); - void register_action_selectbyparts_callback(wxGLCanvas* canvas, void* callback); - -private: - CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas); - CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const; - - bool _init(GLCanvas3D& canvas); -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLCanvas3DManager_hpp_ diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp deleted file mode 100644 index e23958c1d..000000000 --- a/xs/src/slic3r/GUI/GLGizmo.cpp +++ /dev/null @@ -1,1503 +0,0 @@ -#include "GLGizmo.hpp" - -#include "../../libslic3r/Utils.hpp" -#include "../../slic3r/GUI/GLCanvas3D.hpp" - -#include <Eigen/Dense> -#include "../../libslic3r/Geometry.hpp" - -#include <GL/glew.h> - -#include <iostream> -#include <numeric> - -static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f }; -static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f }; -static const float DEFAULT_HIGHLIGHT_COLOR[3] = { 1.0f, 0.38f, 0.0f }; - -static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; - -namespace Slic3r { -namespace GUI { - -// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center -// coordinates are local to the plane -Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center) -{ - Transform3d m = Transform3d::Identity(); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); - return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0); -} - -// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center -// coordinates are local to the plane -Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center) -{ - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX())); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); - return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1)); -} - -// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center -// coordinates are local to the plane -Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center) -{ - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY())); - m.translate(-center); - Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0)); - - return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0)); -} - -// return an index: -// 0 for plane XY -// 1 for plane XZ -// 2 for plane YZ -// which indicates which plane is best suited for intersecting the given unit vector -// giving precedence to the plane with the given index -unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane) -{ - unsigned int ret = preferred_plane; - - // 1st checks if the given vector is not parallel to the given preferred plane - double dot_to_normal = 0.0; - switch (ret) - { - case 0: // plane xy - { - dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ())); - break; - } - case 1: // plane xz - { - dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY())); - break; - } - case 2: // plane yz - { - dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX())); - break; - } - default: - { - break; - } - } - - // if almost parallel, select the plane whose normal direction is closest to the given vector direction, - // otherwise return the given preferred plane index - if (dot_to_normal < 0.1) - { - typedef std::map<double, unsigned int> ProjsMap; - ProjsMap projs_map; - projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy - projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz - projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz - ret = projs_map.rbegin()->second; - } - - return ret; -} - -const float GLGizmoBase::Grabber::SizeFactor = 0.025f; -const float GLGizmoBase::Grabber::MinHalfSize = 1.5f; -const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f; - -GLGizmoBase::Grabber::Grabber() - : center(Vec3d::Zero()) - , angles(Vec3d::Zero()) - , dragging(false) - , enabled(true) -{ - color[0] = 1.0f; - color[1] = 1.0f; - color[2] = 1.0f; -} - -void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const -{ - float render_color[3]; - if (hover) - { - render_color[0] = 1.0f - color[0]; - render_color[1] = 1.0f - color[1]; - render_color[2] = 1.0f - color[2]; - } - else - ::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float)); - - render(box, render_color, true); -} - -void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const -{ - float max_size = (float)box.max_size(); - float half_size = dragging ? max_size * SizeFactor * DraggingScaleFactor : max_size * SizeFactor; - half_size = std::max(half_size, MinHalfSize); - - if (use_lighting) - ::glEnable(GL_LIGHTING); - - ::glColor3f((GLfloat)render_color[0], (GLfloat)render_color[1], (GLfloat)render_color[2]); - - ::glPushMatrix(); - ::glTranslatef((GLfloat)center(0), (GLfloat)center(1), (GLfloat)center(2)); - - float rad_to_deg = 180.0f / (GLfloat)PI; - ::glRotatef((GLfloat)angles(0) * rad_to_deg, 1.0f, 0.0f, 0.0f); - ::glRotatef((GLfloat)angles(1) * rad_to_deg, 0.0f, 1.0f, 0.0f); - ::glRotatef((GLfloat)angles(2) * rad_to_deg, 0.0f, 0.0f, 1.0f); - - // face min x - ::glPushMatrix(); - ::glTranslatef(-(GLfloat)half_size, 0.0f, 0.0f); - ::glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); - render_face(half_size); - ::glPopMatrix(); - - // face max x - ::glPushMatrix(); - ::glTranslatef((GLfloat)half_size, 0.0f, 0.0f); - ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - render_face(half_size); - ::glPopMatrix(); - - // face min y - ::glPushMatrix(); - ::glTranslatef(0.0f, -(GLfloat)half_size, 0.0f); - ::glRotatef(90.0f, 1.0f, 0.0f, 0.0f); - render_face(half_size); - ::glPopMatrix(); - - // face max y - ::glPushMatrix(); - ::glTranslatef(0.0f, (GLfloat)half_size, 0.0f); - ::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); - render_face(half_size); - ::glPopMatrix(); - - // face min z - ::glPushMatrix(); - ::glTranslatef(0.0f, 0.0f, -(GLfloat)half_size); - ::glRotatef(180.0f, 1.0f, 0.0f, 0.0f); - render_face(half_size); - ::glPopMatrix(); - - // face max z - ::glPushMatrix(); - ::glTranslatef(0.0f, 0.0f, (GLfloat)half_size); - render_face(half_size); - ::glPopMatrix(); - - ::glPopMatrix(); - - if (use_lighting) - ::glDisable(GL_LIGHTING); -} - -void GLGizmoBase::Grabber::render_face(float half_size) const -{ - ::glBegin(GL_TRIANGLES); - ::glNormal3f(0.0f, 0.0f, 1.0f); - ::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f); - ::glVertex3f((GLfloat)half_size, -(GLfloat)half_size, 0.0f); - ::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f); - ::glVertex3f((GLfloat)half_size, (GLfloat)half_size, 0.0f); - ::glVertex3f(-(GLfloat)half_size, (GLfloat)half_size, 0.0f); - ::glVertex3f(-(GLfloat)half_size, -(GLfloat)half_size, 0.0f); - ::glEnd(); -} - -GLGizmoBase::GLGizmoBase(GLCanvas3D& parent) - : m_parent(parent) - , m_group_id(-1) - , m_state(Off) - , m_hover_id(-1) - , m_dragging(false) -{ - ::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 3 * sizeof(float)); - ::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 3 * sizeof(float)); - ::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 3 * sizeof(float)); -} - -void GLGizmoBase::set_hover_id(int id) -{ - if (m_grabbers.empty() || (id < (int)m_grabbers.size())) - { - m_hover_id = id; - on_set_hover_id(); - } -} - -void GLGizmoBase::set_highlight_color(const float* color) -{ - if (color != nullptr) - ::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float)); -} - -void GLGizmoBase::enable_grabber(unsigned int id) -{ - if ((0 <= id) && (id < (unsigned int)m_grabbers.size())) - m_grabbers[id].enabled = true; - - on_enable_grabber(id); -} - -void GLGizmoBase::disable_grabber(unsigned int id) -{ - if ((0 <= id) && (id < (unsigned int)m_grabbers.size())) - m_grabbers[id].enabled = false; - - on_disable_grabber(id); -} - -void GLGizmoBase::start_dragging(const BoundingBoxf3& box) -{ - m_dragging = true; - - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - m_grabbers[i].dragging = (m_hover_id == i); - } - - on_start_dragging(box); -} - -void GLGizmoBase::stop_dragging() -{ - m_dragging = false; - set_tooltip(""); - - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - m_grabbers[i].dragging = false; - } - - on_stop_dragging(); -} - -void GLGizmoBase::update(const Linef3& mouse_ray) -{ - if (m_hover_id != -1) - on_update(mouse_ray); -} - -float GLGizmoBase::picking_color_component(unsigned int id) const -{ - int color = 254 - (int)id; - if (m_group_id > -1) - color -= m_group_id; - - return (float)color / 255.0f; -} - -void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const -{ - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - if (m_grabbers[i].enabled) - m_grabbers[i].render((m_hover_id == i), box); - } -} - -void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const -{ - for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) - { - if (m_grabbers[i].enabled) - { - m_grabbers[i].color[0] = 1.0f; - m_grabbers[i].color[1] = 1.0f; - m_grabbers[i].color[2] = picking_color_component(i); - m_grabbers[i].render_for_picking(box); - } - } -} - -void GLGizmoBase::set_tooltip(const std::string& tooltip) const -{ - m_parent.set_tooltip(tooltip); -} - -std::string GLGizmoBase::format(float value, unsigned int decimals) const -{ - char buf[1024]; - ::sprintf(buf, "%.*f", decimals, value); - return buf; -} - -const float GLGizmoRotate::Offset = 5.0f; -const unsigned int GLGizmoRotate::CircleResolution = 64; -const unsigned int GLGizmoRotate::AngleResolution = 64; -const unsigned int GLGizmoRotate::ScaleStepsCount = 72; -const float GLGizmoRotate::ScaleStepRad = 2.0f * (float)PI / GLGizmoRotate::ScaleStepsCount; -const unsigned int GLGizmoRotate::ScaleLongEvery = 2; -const float GLGizmoRotate::ScaleLongTooth = 2.0f; -const float GLGizmoRotate::ScaleShortTooth = 1.0f; -const unsigned int GLGizmoRotate::SnapRegionsCount = 8; -const float GLGizmoRotate::GrabberOffset = 5.0f; - -GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) - : GLGizmoBase(parent) - , m_axis(axis) - , m_angle(0.0) - , m_center(0.0, 0.0, 0.0) - , m_radius(0.0f) -{ -} - -void GLGizmoRotate::set_angle(double angle) -{ - if (std::abs(angle - 2.0 * (double)PI) < EPSILON) - angle = 0.0; - - m_angle = angle; -} - -bool GLGizmoRotate::on_init() -{ - m_grabbers.push_back(Grabber()); - return true; -} - -void GLGizmoRotate::on_start_dragging(const BoundingBoxf3& box) -{ - m_center = box.center(); - m_radius = Offset + box.radius(); -} - -void GLGizmoRotate::on_update(const Linef3& mouse_ray) -{ - Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(mouse_ray)); - - Vec2d orig_dir = Vec2d::UnitX(); - Vec2d new_dir = mouse_pos.normalized(); - - double theta = ::acos(clamp(-1.0, 1.0, new_dir.dot(orig_dir))); - if (cross2(orig_dir, new_dir) < 0.0) - theta = 2.0 * (double)PI - theta; - - double len = mouse_pos.norm(); - - // snap to snap region - double in_radius = (double)m_radius / 3.0; - double out_radius = 2.0 * (double)in_radius; - if ((in_radius <= len) && (len <= out_radius)) - { - double step = 2.0 * (double)PI / (double)SnapRegionsCount; - theta = step * (double)std::round(theta / step); - } - else - { - // snap to scale - in_radius = (double)m_radius; - out_radius = in_radius + (double)ScaleLongTooth; - if ((in_radius <= len) && (len <= out_radius)) - { - double step = 2.0 * (double)PI / (double)ScaleStepsCount; - theta = step * (double)std::round(theta / step); - } - } - - if (theta == 2.0 * (double)PI) - theta = 0.0; - - m_angle = theta; -} - -void GLGizmoRotate::on_render(const BoundingBoxf3& box) const -{ - if (!m_grabbers[0].enabled) - return; - - if (m_dragging) - set_tooltip(format(m_angle * 180.0f / (float)PI, 4)); - else - { - m_center = box.center(); - m_radius = Offset + box.radius(); - } - - ::glEnable(GL_DEPTH_TEST); - - ::glPushMatrix(); - transform_to_local(); - - ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); - ::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color); - - render_circle(); - - if (m_hover_id != -1) - { - render_scale(); - render_snap_radii(); - render_reference_radius(); - } - - ::glColor3fv(m_highlight_color); - - if (m_hover_id != -1) - render_angle(); - - render_grabber(box); - - ::glPopMatrix(); -} - -void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const -{ - ::glDisable(GL_DEPTH_TEST); - - ::glPushMatrix(); - - transform_to_local(); - render_grabbers_for_picking(box); - - ::glPopMatrix(); -} - -void GLGizmoRotate::render_circle() const -{ - ::glBegin(GL_LINE_LOOP); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float x = ::cos(angle) * m_radius; - float y = ::sin(angle) * m_radius; - float z = 0.0f; - ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); - } - ::glEnd(); -} - -void GLGizmoRotate::render_scale() const -{ - float out_radius_long = m_radius + ScaleLongTooth; - float out_radius_short = m_radius + ScaleShortTooth; - - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < ScaleStepsCount; ++i) - { - float angle = (float)i * ScaleStepRad; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = cosa * m_radius; - float in_y = sina * m_radius; - float in_z = 0.0f; - float out_x = (i % ScaleLongEvery == 0) ? cosa * out_radius_long : cosa * out_radius_short; - float out_y = (i % ScaleLongEvery == 0) ? sina * out_radius_long : sina * out_radius_short; - float out_z = 0.0f; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); - } - ::glEnd(); -} - -void GLGizmoRotate::render_snap_radii() const -{ - float step = 2.0f * (float)PI / (float)SnapRegionsCount; - - float in_radius = m_radius / 3.0f; - float out_radius = 2.0f * in_radius; - - ::glBegin(GL_LINES); - for (unsigned int i = 0; i < SnapRegionsCount; ++i) - { - float angle = (float)i * step; - float cosa = ::cos(angle); - float sina = ::sin(angle); - float in_x = cosa * in_radius; - float in_y = sina * in_radius; - float in_z = 0.0f; - float out_x = cosa * out_radius; - float out_y = sina * out_radius; - float out_z = 0.0f; - ::glVertex3f((GLfloat)in_x, (GLfloat)in_y, (GLfloat)in_z); - ::glVertex3f((GLfloat)out_x, (GLfloat)out_y, (GLfloat)out_z); - } - ::glEnd(); -} - -void GLGizmoRotate::render_reference_radius() const -{ - ::glBegin(GL_LINES); - ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)(m_radius + GrabberOffset), 0.0f, 0.0f); - ::glEnd(); -} - -void GLGizmoRotate::render_angle() const -{ - float step_angle = (float)m_angle / AngleResolution; - float ex_radius = m_radius + GrabberOffset; - - ::glBegin(GL_LINE_STRIP); - for (unsigned int i = 0; i <= AngleResolution; ++i) - { - float angle = (float)i * step_angle; - float x = ::cos(angle) * ex_radius; - float y = ::sin(angle) * ex_radius; - float z = 0.0f; - ::glVertex3f((GLfloat)x, (GLfloat)y, (GLfloat)z); - } - ::glEnd(); -} - -void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const -{ - double grabber_radius = (double)(m_radius + GrabberOffset); - m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0); - m_grabbers[0].angles(2) = m_angle; - - ::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color); - - ::glBegin(GL_LINES); - ::glVertex3f(0.0f, 0.0f, 0.0f); - ::glVertex3f((GLfloat)m_grabbers[0].center(0), (GLfloat)m_grabbers[0].center(1), (GLfloat)m_grabbers[0].center(2)); - ::glEnd(); - - ::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float)); - render_grabbers(box); -} - -void GLGizmoRotate::transform_to_local() const -{ - ::glTranslatef((GLfloat)m_center(0), (GLfloat)m_center(1), (GLfloat)m_center(2)); - - switch (m_axis) - { - case X: - { - ::glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - ::glRotatef(90.0f, 0.0f, 0.0f, 1.0f); - break; - } - case Y: - { - ::glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); - ::glRotatef(180.0f, 0.0f, 0.0f, 1.0f); - break; - } - default: - case Z: - { - // no rotation - break; - } - } -} - -Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray) const -{ - double half_pi = 0.5 * (double)PI; - - Transform3d m = Transform3d::Identity(); - - switch (m_axis) - { - case X: - { - m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitZ())); - m.rotate(Eigen::AngleAxisd(-half_pi, Vec3d::UnitY())); - break; - } - case Y: - { - m.rotate(Eigen::AngleAxisd(-(double)PI, Vec3d::UnitZ())); - m.rotate(Eigen::AngleAxisd(half_pi, Vec3d::UnitX())); - break; - } - default: - case Z: - { - // no rotation applied - break; - } - } - - m.translate(-m_center); - - return transform(mouse_ray, m).intersect_plane(0.0); -} - -GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent) - : GLGizmoBase(parent) -{ - m_gizmos.emplace_back(parent, GLGizmoRotate::X); - m_gizmos.emplace_back(parent, GLGizmoRotate::Y); - m_gizmos.emplace_back(parent, GLGizmoRotate::Z); - - for (unsigned int i = 0; i < 3; ++i) - { - m_gizmos[i].set_group_id(i); - } -} - -bool GLGizmoRotate3D::on_init() -{ - for (GLGizmoRotate& g : m_gizmos) - { - if (!g.init()) - return false; - } - - for (unsigned int i = 0; i < 3; ++i) - { - m_gizmos[i].set_highlight_color(AXES_COLOR[i]); - } - - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "rotate_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "rotate_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "rotate_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - return true; -} - -void GLGizmoRotate3D::on_start_dragging(const BoundingBoxf3& box) -{ - if ((0 <= m_hover_id) && (m_hover_id < 3)) - m_gizmos[m_hover_id].start_dragging(box); -} - -void GLGizmoRotate3D::on_stop_dragging() -{ - if ((0 <= m_hover_id) && (m_hover_id < 3)) - m_gizmos[m_hover_id].stop_dragging(); -} - -void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const -{ - if ((m_hover_id == -1) || (m_hover_id == 0)) - m_gizmos[X].render(box); - - if ((m_hover_id == -1) || (m_hover_id == 1)) - m_gizmos[Y].render(box); - - if ((m_hover_id == -1) || (m_hover_id == 2)) - m_gizmos[Z].render(box); -} - -const float GLGizmoScale3D::Offset = 5.0f; -const Vec3d GLGizmoScale3D::OffsetVec = (double)GLGizmoScale3D::Offset * Vec3d::Ones(); - -GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent) - : GLGizmoBase(parent) - , m_scale(Vec3d::Ones()) - , m_starting_scale(Vec3d::Ones()) - , m_show_starting_box(false) -{ -} - -bool GLGizmoScale3D::on_init() -{ - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "scale_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "scale_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "scale_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - for (int i = 0; i < 10; ++i) - { - m_grabbers.push_back(Grabber()); - } - - double half_pi = 0.5 * (double)PI; - - // x axis - m_grabbers[0].angles(1) = half_pi; - m_grabbers[1].angles(1) = half_pi; - - // y axis - m_grabbers[2].angles(0) = half_pi; - m_grabbers[3].angles(0) = half_pi; - - return true; -} - -void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box) -{ - if (m_hover_id != -1) - { - m_starting_drag_position = m_grabbers[m_hover_id].center; - m_show_starting_box = true; - m_starting_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec); - } -} - -void GLGizmoScale3D::on_update(const Linef3& mouse_ray) -{ - if ((m_hover_id == 0) || (m_hover_id == 1)) - do_scale_x(mouse_ray); - else if ((m_hover_id == 2) || (m_hover_id == 3)) - do_scale_y(mouse_ray); - else if ((m_hover_id == 4) || (m_hover_id == 5)) - do_scale_z(mouse_ray); - else if (m_hover_id >= 6) - do_scale_uniform(mouse_ray); -} - -void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const -{ - if (m_grabbers[0].dragging || m_grabbers[1].dragging) - set_tooltip("X: " + format(100.0f * m_scale(0), 4) + "%"); - else if (m_grabbers[2].dragging || m_grabbers[3].dragging) - set_tooltip("Y: " + format(100.0f * m_scale(1), 4) + "%"); - else if (m_grabbers[4].dragging || m_grabbers[5].dragging) - set_tooltip("Z: " + format(100.0f * m_scale(2), 4) + "%"); - else if (m_grabbers[6].dragging || m_grabbers[7].dragging || m_grabbers[8].dragging || m_grabbers[9].dragging) - { - std::string tooltip = "X: " + format(100.0f * m_scale(0), 4) + "%\n"; - tooltip += "Y: " + format(100.0f * m_scale(1), 4) + "%\n"; - tooltip += "Z: " + format(100.0f * m_scale(2), 4) + "%"; - set_tooltip(tooltip); - } - - ::glEnable(GL_DEPTH_TEST); - - m_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec); - const Vec3d& center = m_box.center(); - - // x axis - m_grabbers[0].center = Vec3d(m_box.min(0), center(1), center(2)); - m_grabbers[1].center = Vec3d(m_box.max(0), center(1), center(2)); - ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); - ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); - - // y axis - m_grabbers[2].center = Vec3d(center(0), m_box.min(1), center(2)); - m_grabbers[3].center = Vec3d(center(0), m_box.max(1), center(2)); - ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); - ::memcpy((void*)m_grabbers[3].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); - - // z axis - m_grabbers[4].center = Vec3d(center(0), center(1), m_box.min(2)); - m_grabbers[5].center = Vec3d(center(0), center(1), m_box.max(2)); - ::memcpy((void*)m_grabbers[4].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); - ::memcpy((void*)m_grabbers[5].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); - - // uniform - m_grabbers[6].center = Vec3d(m_box.min(0), m_box.min(1), m_box.min(2)); - m_grabbers[7].center = Vec3d(m_box.max(0), m_box.min(1), m_box.min(2)); - m_grabbers[8].center = Vec3d(m_box.max(0), m_box.max(1), m_box.min(2)); - m_grabbers[9].center = Vec3d(m_box.min(0), m_box.max(1), m_box.min(2)); - for (int i = 6; i < 10; ++i) - { - ::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float)); - } - - ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); - - if (m_hover_id == -1) - { - // draw box - ::glColor3fv(m_base_color); - render_box(m_box); - // draw connections - if (m_grabbers[0].enabled && m_grabbers[1].enabled) - { - ::glColor3fv(m_grabbers[0].color); - render_grabbers_connection(0, 1); - } - if (m_grabbers[2].enabled && m_grabbers[3].enabled) - { - ::glColor3fv(m_grabbers[2].color); - render_grabbers_connection(2, 3); - } - if (m_grabbers[4].enabled && m_grabbers[5].enabled) - { - ::glColor3fv(m_grabbers[4].color); - render_grabbers_connection(4, 5); - } - // draw grabbers - render_grabbers(m_box); - } - else if ((m_hover_id == 0) || (m_hover_id == 1)) - { - // draw starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); - // draw connection - ::glColor3fv(m_grabbers[0].color); - render_grabbers_connection(0, 1); - // draw grabbers - m_grabbers[0].render(true, m_box); - m_grabbers[1].render(true, m_box); - } - else if ((m_hover_id == 2) || (m_hover_id == 3)) - { - // draw starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); - // draw connection - ::glColor3fv(m_grabbers[2].color); - render_grabbers_connection(2, 3); - // draw grabbers - m_grabbers[2].render(true, m_box); - m_grabbers[3].render(true, m_box); - } - else if ((m_hover_id == 4) || (m_hover_id == 5)) - { - // draw starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); - // draw connection - ::glColor3fv(m_grabbers[4].color); - render_grabbers_connection(4, 5); - // draw grabbers - m_grabbers[4].render(true, m_box); - m_grabbers[5].render(true, m_box); - } - else if (m_hover_id >= 6) - { - // draw starting box - if (m_show_starting_box) - { - ::glColor3fv(m_base_color); - render_box(m_starting_box); - } - // draw current box - ::glColor3fv(m_drag_color); - render_box(m_box); - // draw grabbers - for (int i = 6; i < 10; ++i) - { - m_grabbers[i].render(true, m_box); - } - } -} - -void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const -{ - ::glDisable(GL_DEPTH_TEST); - - render_grabbers_for_picking(box); -} - -void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const -{ - // bottom face - ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); - ::glEnd(); - - // top face - ::glBegin(GL_LINE_LOOP); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glEnd(); - - // vertical edges - ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.min(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.max(1), (GLfloat)box.max(2)); - ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.min(2)); ::glVertex3f((GLfloat)box.max(0), (GLfloat)box.min(1), (GLfloat)box.max(2)); - ::glEnd(); -} - -void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const -{ - unsigned int grabbers_count = (unsigned int)m_grabbers.size(); - if ((id_1 < grabbers_count) && (id_2 < grabbers_count)) - { - ::glBegin(GL_LINES); - ::glVertex3f((GLfloat)m_grabbers[id_1].center(0), (GLfloat)m_grabbers[id_1].center(1), (GLfloat)m_grabbers[id_1].center(2)); - ::glVertex3f((GLfloat)m_grabbers[id_2].center(0), (GLfloat)m_grabbers[id_2].center(1), (GLfloat)m_grabbers[id_2].center(2)); - ::glEnd(); - } -} - -void GLGizmoScale3D::do_scale_x(const Linef3& mouse_ray) -{ - double ratio = calc_ratio(1, mouse_ray, m_starting_box.center()); - - if (ratio > 0.0) - m_scale(0) = m_starting_scale(0) * ratio; -} - -void GLGizmoScale3D::do_scale_y(const Linef3& mouse_ray) -{ - double ratio = calc_ratio(2, mouse_ray, m_starting_box.center()); - - if (ratio > 0.0) - m_scale(0) = m_starting_scale(1) * ratio; // << this is temporary -// m_scale(1) = m_starting_scale(1) * ratio; -} - -void GLGizmoScale3D::do_scale_z(const Linef3& mouse_ray) -{ - double ratio = calc_ratio(1, mouse_ray, m_starting_box.center()); - - if (ratio > 0.0) - m_scale(0) = m_starting_scale(2) * ratio; // << this is temporary -// m_scale(2) = m_starting_scale(2) * ratio; -} - -void GLGizmoScale3D::do_scale_uniform(const Linef3& mouse_ray) -{ - Vec3d center = m_starting_box.center(); - center(2) = m_box.min(2); - double ratio = calc_ratio(0, mouse_ray, center); - - if (ratio > 0.0) - m_scale = m_starting_scale * ratio; -} - -double GLGizmoScale3D::calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const -{ - double ratio = 0.0; - - Vec3d starting_vec = m_starting_drag_position - center; - double len_starting_vec = starting_vec.norm(); - if (len_starting_vec == 0.0) - return ratio; - - Vec3d starting_vec_dir = starting_vec.normalized(); - Vec3d mouse_dir = mouse_ray.unit_vector(); - - unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id); - // ratio is given by the projection of the calculated intersection on the starting vector divided by the starting vector length - switch (plane_id) - { - case 0: - { - ratio = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, center)) / len_starting_vec; - break; - } - case 1: - { - ratio = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, center)) / len_starting_vec; - break; - } - case 2: - { - ratio = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, center)) / len_starting_vec; - break; - } - } - - return ratio; -} - -const double GLGizmoMove3D::Offset = 10.0; - -GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent) - : GLGizmoBase(parent) - , m_position(Vec3d::Zero()) - , m_starting_drag_position(Vec3d::Zero()) - , m_starting_box_center(Vec3d::Zero()) - , m_starting_box_bottom_center(Vec3d::Zero()) -{ -} - -bool GLGizmoMove3D::on_init() -{ - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "move_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "move_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "move_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - for (int i = 0; i < 3; ++i) - { - m_grabbers.push_back(Grabber()); - } - - return true; -} - -void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box) -{ - if (m_hover_id != -1) - { - m_starting_drag_position = m_grabbers[m_hover_id].center; - m_starting_box_center = box.center(); - m_starting_box_bottom_center = box.center(); - m_starting_box_bottom_center(2) = box.min(2); - } -} - -void GLGizmoMove3D::on_update(const Linef3& mouse_ray) -{ - if (m_hover_id == 0) - m_position(0) = 2.0 * m_starting_box_center(0) + calc_projection(X, 1, mouse_ray) - m_starting_drag_position(0); - else if (m_hover_id == 1) - m_position(1) = 2.0 * m_starting_box_center(1) + calc_projection(Y, 2, mouse_ray) - m_starting_drag_position(1); - else if (m_hover_id == 2) - m_position(2) = 2.0 * m_starting_box_bottom_center(2) + calc_projection(Z, 1, mouse_ray) - m_starting_drag_position(2); -} - -void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const -{ - if (m_grabbers[0].dragging) - set_tooltip("X: " + format(m_position(0), 2)); - else if (m_grabbers[1].dragging) - set_tooltip("Y: " + format(m_position(1), 2)); - else if (m_grabbers[2].dragging) - set_tooltip("Z: " + format(m_position(2), 2)); - - ::glEnable(GL_DEPTH_TEST); - - const Vec3d& center = box.center(); - - // x axis - m_grabbers[0].center = Vec3d(box.max(0) + Offset, center(1), center(2)); - ::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 3 * sizeof(float)); - - // y axis - m_grabbers[1].center = Vec3d(center(0), box.max(1) + Offset, center(2)); - ::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 3 * sizeof(float)); - - // z axis - m_grabbers[2].center = Vec3d(center(0), center(1), box.max(2) + Offset); - ::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 3 * sizeof(float)); - - ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); - - if (m_hover_id == -1) - { - // draw axes - for (unsigned int i = 0; i < 3; ++i) - { - if (m_grabbers[i].enabled) - { - ::glColor3fv(AXES_COLOR[i]); - ::glBegin(GL_LINES); - ::glVertex3f(center(0), center(1), center(2)); - ::glVertex3f((GLfloat)m_grabbers[i].center(0), (GLfloat)m_grabbers[i].center(1), (GLfloat)m_grabbers[i].center(2)); - ::glEnd(); - } - } - - // draw grabbers - render_grabbers(box); - } - else - { - // draw axis - ::glColor3fv(AXES_COLOR[m_hover_id]); - ::glBegin(GL_LINES); - ::glVertex3f(center(0), center(1), center(2)); - ::glVertex3f((GLfloat)m_grabbers[m_hover_id].center(0), (GLfloat)m_grabbers[m_hover_id].center(1), (GLfloat)m_grabbers[m_hover_id].center(2)); - ::glEnd(); - - // draw grabber - m_grabbers[m_hover_id].render(true, box); - } -} - -void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const -{ - ::glDisable(GL_DEPTH_TEST); - - render_grabbers_for_picking(box); -} - -double GLGizmoMove3D::calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const -{ - double projection = 0.0; - - Vec3d starting_vec = (axis == Z) ? m_starting_drag_position - m_starting_box_bottom_center : m_starting_drag_position - m_starting_box_center; - double len_starting_vec = starting_vec.norm(); - if (len_starting_vec == 0.0) - return projection; - - Vec3d starting_vec_dir = starting_vec.normalized(); - Vec3d mouse_dir = mouse_ray.unit_vector(); - - unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id); - - switch (plane_id) - { - case 0: - { - projection = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center)); - break; - } - case 1: - { - projection = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center)); - break; - } - case 2: - { - projection = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center)); - break; - } - } - - return projection; -} - -GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent) - : GLGizmoBase(parent) - , m_normal(Vec3d::Zero()) - , m_starting_center(Vec3d::Zero()) -{ -} - -bool GLGizmoFlatten::on_init() -{ - std::string path = resources_dir() + "/icons/overlay/"; - - std::string filename = path + "layflat_off.png"; - if (!m_textures[Off].load_from_file(filename, false)) - return false; - - filename = path + "layflat_hover.png"; - if (!m_textures[Hover].load_from_file(filename, false)) - return false; - - filename = path + "layflat_on.png"; - if (!m_textures[On].load_from_file(filename, false)) - return false; - - return true; -} - -void GLGizmoFlatten::on_start_dragging(const BoundingBoxf3& box) -{ - if (m_hover_id != -1) - { - m_normal = m_planes[m_hover_id].normal; - m_starting_center = box.center(); - } -} - -void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const -{ - // the dragged_offset is a vector measuring where was the object moved - // with the gizmo being on. This is reset in set_flattening_data and - // does not work correctly when there are multiple copies. - Vec3d dragged_offset(Vec3d::Zero()); - if (m_dragging) - dragged_offset = box.center() - m_starting_center; - - ::glEnable(GL_BLEND); - ::glEnable(GL_DEPTH_TEST); - - for (int i=0; i<(int)m_planes.size(); ++i) { - if (i == m_hover_id) - ::glColor4f(0.9f, 0.9f, 0.9f, 0.75f); - else - ::glColor4f(0.9f, 0.9f, 0.9f, 0.5f); - -#if ENABLE_MODELINSTANCE_3D_OFFSET - for (Vec3d offset : m_instances_positions) { - offset += dragged_offset; -#else - for (Vec2d offset : m_instances_positions) { - offset += to_2d(dragged_offset); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - ::glPushMatrix(); -#if ENABLE_MODELINSTANCE_3D_OFFSET - ::glTranslated(offset(0), offset(1), offset(2)); -#else - ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - ::glBegin(GL_POLYGON); - for (const Vec3d& vertex : m_planes[i].vertices) - ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2)); - ::glEnd(); - ::glPopMatrix(); - } - } - - ::glDisable(GL_BLEND); -} - -void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const -{ - ::glEnable(GL_DEPTH_TEST); - - for (unsigned int i = 0; i < m_planes.size(); ++i) - { - ::glColor3f(1.0f, 1.0f, picking_color_component(i)); -#if ENABLE_MODELINSTANCE_3D_OFFSET - for (const Vec3d& offset : m_instances_positions) { -#else - for (const Vec2d& offset : m_instances_positions) { -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - ::glPushMatrix(); -#if ENABLE_MODELINSTANCE_3D_OFFSET - ::glTranslated(offset(0), offset(1), offset(2)); -#else - ::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - ::glBegin(GL_POLYGON); - for (const Vec3d& vertex : m_planes[i].vertices) - ::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2)); - ::glEnd(); - ::glPopMatrix(); - } - } -} - -void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object) -{ - m_model_object = model_object; - - // ...and save the updated positions of the object instances: - if (m_model_object && !m_model_object->instances.empty()) { - m_instances_positions.clear(); - for (const auto* instance : m_model_object->instances) -#if ENABLE_MODELINSTANCE_3D_OFFSET - m_instances_positions.emplace_back(instance->get_offset()); -#else - m_instances_positions.emplace_back(instance->offset); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - } - - if (is_plane_update_necessary()) - update_planes(); -} - -void GLGizmoFlatten::update_planes() -{ - TriangleMesh ch; - for (const ModelVolume* vol : m_model_object->volumes) - ch.merge(vol->get_convex_hull()); - ch = ch.convex_hull_3d(); - ch.scale(m_model_object->instances.front()->scaling_factor); - ch.rotate_z(m_model_object->instances.front()->rotation); - - m_planes.clear(); - - // Now we'll go through all the facets and append Points of facets sharing the same normal: - const int num_of_facets = ch.stl.stats.number_of_facets; - std::vector<int> facet_queue(num_of_facets, 0); - std::vector<bool> facet_visited(num_of_facets, false); - int facet_queue_cnt = 0; - const stl_normal* normal_ptr = nullptr; - while (1) { - // Find next unvisited triangle: - int facet_idx = 0; - for (; facet_idx < num_of_facets; ++ facet_idx) - if (!facet_visited[facet_idx]) { - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - normal_ptr = &ch.stl.facet_start[facet_idx].normal; - m_planes.emplace_back(); - break; - } - if (facet_idx == num_of_facets) - break; // Everything was visited already - - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - const stl_normal& this_normal = ch.stl.facet_start[facet_idx].normal; - if (std::abs(this_normal(0) - (*normal_ptr)(0)) < 0.001 && std::abs(this_normal(1) - (*normal_ptr)(1)) < 0.001 && std::abs(this_normal(2) - (*normal_ptr)(2)) < 0.001) { - stl_vertex* first_vertex = ch.stl.facet_start[facet_idx].vertex; - for (int j=0; j<3; ++j) - m_planes.back().vertices.emplace_back(first_vertex[j](0), first_vertex[j](1), first_vertex[j](2)); - - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) { - int neighbor_idx = ch.stl.neighbors_start[facet_idx].neighbor[j]; - if (! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - } - m_planes.back().normal = Vec3d((double)(*normal_ptr)(0), (double)(*normal_ptr)(1), (double)(*normal_ptr)(2)); - - // if this is a just a very small triangle, remove it to speed up further calculations (it would be rejected anyway): - if (m_planes.back().vertices.size() == 3 && - (m_planes.back().vertices[0] - m_planes.back().vertices[1]).norm() < 1.f - || (m_planes.back().vertices[0] - m_planes.back().vertices[2]).norm() < 1.f) - m_planes.pop_back(); - } - - // Now we'll go through all the polygons, transform the points into xy plane to process them: - for (unsigned int polygon_id=0; polygon_id < m_planes.size(); ++polygon_id) { - Pointf3s& polygon = m_planes[polygon_id].vertices; - const Vec3d& normal = m_planes[polygon_id].normal; - - // We are going to rotate about z and y to flatten the plane - float angle_z = 0.f; - float angle_y = 0.f; - if (std::abs(normal(1)) > 0.001) - angle_z = -atan2(normal(1), normal(0)); // angle to rotate so that normal ends up in xz-plane - if (std::abs(normal(0)*cos(angle_z) - normal(1)*sin(angle_z)) > 0.001) - angle_y = -atan2(normal(0)*cos(angle_z) - normal(1)*sin(angle_z), normal(2)); // angle to rotate to make normal point upwards - else { - // In case it already was in z-direction, we must ensure it is not the wrong way: - angle_y = normal(2) > 0.f ? 0.f : -PI; - } - - // Rotate all points to the xy plane: - Transform3d m = Transform3d::Identity(); - m.rotate(Eigen::AngleAxisd((double)angle_y, Vec3d::UnitY())); - m.rotate(Eigen::AngleAxisd((double)angle_z, Vec3d::UnitZ())); - polygon = transform(polygon, m); - - polygon = Slic3r::Geometry::convex_hull(polygon); // To remove the inner points - - // We will calculate area of the polygon and discard ones that are too small - // The limit is more forgiving in case the normal is in the direction of the coordinate axes - const float minimal_area = (std::abs(normal(0)) > 0.999f || std::abs(normal(1)) > 0.999f || std::abs(normal(2)) > 0.999f) ? 1.f : 20.f; - float& area = m_planes[polygon_id].area; - area = 0.f; - for (unsigned int i = 0; i < polygon.size(); i++) // Shoelace formula - area += polygon[i](0)*polygon[i + 1 < polygon.size() ? i + 1 : 0](1) - polygon[i + 1 < polygon.size() ? i + 1 : 0](0)*polygon[i](1); - area = std::abs(area / 2.f); - if (area < minimal_area) { - m_planes.erase(m_planes.begin()+(polygon_id--)); - continue; - } - - // We will shrink the polygon a little bit so it does not touch the object edges: - Vec3d centroid = std::accumulate(polygon.begin(), polygon.end(), Vec3d(0.0, 0.0, 0.0)); - centroid /= (double)polygon.size(); - for (auto& vertex : polygon) - vertex = 0.9f*vertex + 0.1f*centroid; - - // Polygon is now simple and convex, we'll round the corners to make them look nicer. - // The algorithm takes a vertex, calculates middles of respective sides and moves the vertex - // towards their average (controlled by 'aggressivity'). This is repeated k times. - // In next iterations, the neighbours are not always taken at the middle (to increase the - // rounding effect at the corners, where we need it most). - const unsigned int k = 10; // number of iterations - const float aggressivity = 0.2f; // agressivity - const unsigned int N = polygon.size(); - std::vector<std::pair<unsigned int, unsigned int>> neighbours; - if (k != 0) { - Pointf3s points_out(2*k*N); // vector long enough to store the future vertices - for (unsigned int j=0; j<N; ++j) { - points_out[j*2*k] = polygon[j]; - neighbours.push_back(std::make_pair((int)(j*2*k-k) < 0 ? (N-1)*2*k+k : j*2*k-k, j*2*k+k)); - } - - for (unsigned int i=0; i<k; ++i) { - // Calculate middle of each edge so that neighbours points to something useful: - for (unsigned int j=0; j<N; ++j) - if (i==0) - points_out[j*2*k+k] = 0.5f * (points_out[j*2*k] + points_out[j==N-1 ? 0 : (j+1)*2*k]); - else { - float r = 0.2+0.3/(k-1)*i; // the neighbours are not always taken in the middle - points_out[neighbours[j].first] = r*points_out[j*2*k] + (1-r) * points_out[neighbours[j].first-1]; - points_out[neighbours[j].second] = r*points_out[j*2*k] + (1-r) * points_out[neighbours[j].second+1]; - } - // Now we have a triangle and valid neighbours, we can do an iteration: - for (unsigned int j=0; j<N; ++j) - points_out[2*k*j] = (1-aggressivity) * points_out[2*k*j] + - aggressivity*0.5f*(points_out[neighbours[j].first] + points_out[neighbours[j].second]); - - for (auto& n : neighbours) { - ++n.first; - --n.second; - } - } - polygon = points_out; // replace the coarse polygon with the smooth one that we just created - } - - // Transform back to 3D; - for (auto& b : polygon) { - b(2) += 0.1f; // raise a bit above the object surface to avoid flickering - } - - m = m.inverse(); - polygon = transform(polygon, m); - } - - // We'll sort the planes by area and only keep the 254 largest ones (because of the picking pass limitations): - std::sort(m_planes.rbegin(), m_planes.rend(), [](const PlaneData& a, const PlaneData& b) { return a.area < b.area; }); - m_planes.resize(std::min((int)m_planes.size(), 254)); - - // Planes are finished - let's save what we calculated it from: - m_source_data.bounding_boxes.clear(); - for (const auto& vol : m_model_object->volumes) - m_source_data.bounding_boxes.push_back(vol->get_convex_hull().bounding_box()); - m_source_data.scaling_factor = m_model_object->instances.front()->scaling_factor; - m_source_data.rotation = m_model_object->instances.front()->rotation; - const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); - m_source_data.mesh_first_point = Vec3d((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); -} - -// Check if the bounding boxes of each volume's convex hull is the same as before -// and that scaling and rotation has not changed. In that case we don't have to recalculate it. -bool GLGizmoFlatten::is_plane_update_necessary() const -{ - if (m_state != On || !m_model_object || m_model_object->instances.empty()) - return false; - - if (m_model_object->volumes.size() != m_source_data.bounding_boxes.size() - || m_model_object->instances.front()->scaling_factor != m_source_data.scaling_factor - || m_model_object->instances.front()->rotation != m_source_data.rotation) - return true; - - // now compare the bounding boxes: - for (unsigned int i=0; i<m_model_object->volumes.size(); ++i) - if (m_model_object->volumes[i]->get_convex_hull().bounding_box() != m_source_data.bounding_boxes[i]) - return true; - - const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); - Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); - if (first_point != m_source_data.mesh_first_point) - return true; - - return false; -} - -Vec3d GLGizmoFlatten::get_flattening_normal() const { - Vec3d normal = m_model_object->instances.front()->world_matrix(true).matrix().block(0, 0, 3, 3).inverse() * m_normal; - m_normal = Vec3d::Zero(); - return normal.normalized(); -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp deleted file mode 100644 index 2430b5af5..000000000 --- a/xs/src/slic3r/GUI/GLGizmo.hpp +++ /dev/null @@ -1,366 +0,0 @@ -#ifndef slic3r_GLGizmo_hpp_ -#define slic3r_GLGizmo_hpp_ - -#include "../../slic3r/GUI/GLTexture.hpp" -#include "../../libslic3r/Point.hpp" -#include "../../libslic3r/BoundingBox.hpp" - -#include <vector> - -namespace Slic3r { - -class BoundingBoxf3; -class Linef3; -class ModelObject; - -namespace GUI { - -class GLCanvas3D; - -class GLGizmoBase -{ -protected: - struct Grabber - { - static const float SizeFactor; - static const float MinHalfSize; - static const float DraggingScaleFactor; - - Vec3d center; - Vec3d angles; - float color[3]; - bool enabled; - bool dragging; - - Grabber(); - - void render(bool hover, const BoundingBoxf3& box) const; - void render_for_picking(const BoundingBoxf3& box) const { render(box, color, false); } - - private: - void render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const; - void render_face(float half_size) const; - }; - -public: - enum EState - { - Off, - Hover, - On, - Num_States - }; - -protected: - GLCanvas3D& m_parent; - - int m_group_id; - EState m_state; - // textures are assumed to be square and all with the same size in pixels, no internal check is done - GLTexture m_textures[Num_States]; - int m_hover_id; - bool m_dragging; - float m_base_color[3]; - float m_drag_color[3]; - float m_highlight_color[3]; - mutable std::vector<Grabber> m_grabbers; - -public: - explicit GLGizmoBase(GLCanvas3D& parent); - virtual ~GLGizmoBase() {} - - bool init() { return on_init(); } - - int get_group_id() const { return m_group_id; } - void set_group_id(int id) { m_group_id = id; } - - EState get_state() const { return m_state; } - void set_state(EState state) { m_state = state; on_set_state(); } - - unsigned int get_texture_id() const { return m_textures[m_state].get_id(); } - int get_textures_size() const { return m_textures[Off].get_width(); } - - int get_hover_id() const { return m_hover_id; } - void set_hover_id(int id); - - void set_highlight_color(const float* color); - - void enable_grabber(unsigned int id); - void disable_grabber(unsigned int id); - - void start_dragging(const BoundingBoxf3& box); - void stop_dragging(); - bool is_dragging() const { return m_dragging; } - - void update(const Linef3& mouse_ray); - - void render(const BoundingBoxf3& box) const { on_render(box); } - void render_for_picking(const BoundingBoxf3& box) const { on_render_for_picking(box); } - -protected: - virtual bool on_init() = 0; - virtual void on_set_state() {} - virtual void on_set_hover_id() {} - virtual void on_enable_grabber(unsigned int id) {} - virtual void on_disable_grabber(unsigned int id) {} - virtual void on_start_dragging(const BoundingBoxf3& box) {} - virtual void on_stop_dragging() {} - virtual void on_update(const Linef3& mouse_ray) = 0; - virtual void on_render(const BoundingBoxf3& box) const = 0; - virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0; - - float picking_color_component(unsigned int id) const; - void render_grabbers(const BoundingBoxf3& box) const; - void render_grabbers_for_picking(const BoundingBoxf3& box) const; - - void set_tooltip(const std::string& tooltip) const; - std::string format(float value, unsigned int decimals) const; -}; - -class GLGizmoRotate : public GLGizmoBase -{ - static const float Offset; - static const unsigned int CircleResolution; - static const unsigned int AngleResolution; - static const unsigned int ScaleStepsCount; - static const float ScaleStepRad; - static const unsigned int ScaleLongEvery; - static const float ScaleLongTooth; - static const float ScaleShortTooth; - static const unsigned int SnapRegionsCount; - static const float GrabberOffset; - -public: - enum Axis : unsigned char - { - X, - Y, - Z - }; - -private: - Axis m_axis; - double m_angle; - - mutable Vec3d m_center; - mutable float m_radius; - -public: - GLGizmoRotate(GLCanvas3D& parent, Axis axis); - - double get_angle() const { return m_angle; } - void set_angle(double angle); - -protected: - virtual bool on_init(); - virtual void on_start_dragging(const BoundingBoxf3& box); - virtual void on_update(const Linef3& mouse_ray); - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const; - -private: - void render_circle() const; - void render_scale() const; - void render_snap_radii() const; - void render_reference_radius() const; - void render_angle() const; - void render_grabber(const BoundingBoxf3& box) const; - - void transform_to_local() const; - // returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate - Vec3d mouse_position_in_local_plane(const Linef3& mouse_ray) const; -}; - -class GLGizmoRotate3D : public GLGizmoBase -{ - std::vector<GLGizmoRotate> m_gizmos; - -public: - explicit GLGizmoRotate3D(GLCanvas3D& parent); - - double get_angle_x() const { return m_gizmos[X].get_angle(); } - void set_angle_x(double angle) { m_gizmos[X].set_angle(angle); } - - double get_angle_y() const { return m_gizmos[Y].get_angle(); } - void set_angle_y(double angle) { m_gizmos[Y].set_angle(angle); } - - double get_angle_z() const { return m_gizmos[Z].get_angle(); } - void set_angle_z(double angle) { m_gizmos[Z].set_angle(angle); } - -protected: - virtual bool on_init(); - virtual void on_set_state() - { - for (GLGizmoRotate& g : m_gizmos) - { - g.set_state(m_state); - } - } - virtual void on_set_hover_id() - { - for (unsigned int i = 0; i < 3; ++i) - { - m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); - } - } - virtual void on_enable_grabber(unsigned int id) - { - if ((0 <= id) && (id < 3)) - m_gizmos[id].enable_grabber(0); - } - virtual void on_disable_grabber(unsigned int id) - { - if ((0 <= id) && (id < 3)) - m_gizmos[id].disable_grabber(0); - } - virtual void on_start_dragging(const BoundingBoxf3& box); - virtual void on_stop_dragging(); - virtual void on_update(const Linef3& mouse_ray) - { - for (GLGizmoRotate& g : m_gizmos) - { - g.update(mouse_ray); - } - } - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const - { - for (const GLGizmoRotate& g : m_gizmos) - { - g.render_for_picking(box); - } - } -}; - -class GLGizmoScale3D : public GLGizmoBase -{ - static const float Offset; - static const Vec3d OffsetVec; - - mutable BoundingBoxf3 m_box; - - Vec3d m_scale; - - Vec3d m_starting_scale; - Vec3d m_starting_drag_position; - bool m_show_starting_box; - BoundingBoxf3 m_starting_box; - -public: - explicit GLGizmoScale3D(GLCanvas3D& parent); - - double get_scale_x() const { return m_scale(0); } - void set_scale_x(double scale) { m_starting_scale(0) = scale; } - - double get_scale_y() const { return m_scale(1); } - void set_scale_y(double scale) { m_starting_scale(1) = scale; } - - double get_scale_z() const { return m_scale(2); } - void set_scale_z(double scale) { m_starting_scale(2) = scale; } - - void set_scale(double scale) { m_starting_scale = scale * Vec3d::Ones(); } - -protected: - virtual bool on_init(); - virtual void on_start_dragging(const BoundingBoxf3& box); - virtual void on_stop_dragging() { m_show_starting_box = false; } - virtual void on_update(const Linef3& mouse_ray); - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const; - -private: - void render_box(const BoundingBoxf3& box) const; - void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; - - void do_scale_x(const Linef3& mouse_ray); - void do_scale_y(const Linef3& mouse_ray); - void do_scale_z(const Linef3& mouse_ray); - void do_scale_uniform(const Linef3& mouse_ray); - - double calc_ratio(unsigned int preferred_plane_id, const Linef3& mouse_ray, const Vec3d& center) const; -}; - -class GLGizmoMove3D : public GLGizmoBase -{ - static const double Offset; - - Vec3d m_position; - Vec3d m_starting_drag_position; - Vec3d m_starting_box_center; - Vec3d m_starting_box_bottom_center; - -public: - explicit GLGizmoMove3D(GLCanvas3D& parent); - - const Vec3d& get_position() const { return m_position; } - void set_position(const Vec3d& position) { m_position = position; } - -protected: - virtual bool on_init(); - virtual void on_start_dragging(const BoundingBoxf3& box); - virtual void on_update(const Linef3& mouse_ray); - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const; - -private: - double calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const; -}; - -class GLGizmoFlatten : public GLGizmoBase -{ -// This gizmo does not use grabbers. The m_hover_id relates to polygon managed by the class itself. - -private: - mutable Vec3d m_normal; - - struct PlaneData { - std::vector<Vec3d> vertices; - Vec3d normal; - float area; - }; - struct SourceDataSummary { - std::vector<BoundingBoxf3> bounding_boxes; // bounding boxes of convex hulls of individual volumes - float scaling_factor; - float rotation; - Vec3d mesh_first_point; - }; - - // This holds information to decide whether recalculation is necessary: - SourceDataSummary m_source_data; - - std::vector<PlaneData> m_planes; -#if ENABLE_MODELINSTANCE_3D_OFFSET - Pointf3s m_instances_positions; -#else - std::vector<Vec2d> m_instances_positions; -#endif // ENABLE_MODELINSTANCE_3D_OFFSET - Vec3d m_starting_center; - const ModelObject* m_model_object = nullptr; - - void update_planes(); - bool is_plane_update_necessary() const; - -public: - explicit GLGizmoFlatten(GLCanvas3D& parent); - - void set_flattening_data(const ModelObject* model_object); - Vec3d get_flattening_normal() const; - -protected: - virtual bool on_init(); - virtual void on_start_dragging(const BoundingBoxf3& box); - virtual void on_update(const Linef3& mouse_ray) {} - virtual void on_render(const BoundingBoxf3& box) const; - virtual void on_render_for_picking(const BoundingBoxf3& box) const; - virtual void on_set_state() - { - if (m_state == On && is_plane_update_necessary()) - update_planes(); - } -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLGizmo_hpp_ - diff --git a/xs/src/slic3r/GUI/GLShader.cpp b/xs/src/slic3r/GUI/GLShader.cpp deleted file mode 100644 index e2995f7c3..000000000 --- a/xs/src/slic3r/GUI/GLShader.cpp +++ /dev/null @@ -1,256 +0,0 @@ -#include <GL/glew.h> - -#include "GLShader.hpp" - -#include "../../libslic3r/Utils.hpp" -#include <boost/nowide/fstream.hpp> - -#include <string> -#include <utility> -#include <assert.h> - -namespace Slic3r { - -GLShader::~GLShader() -{ - assert(fragment_program_id == 0); - assert(vertex_program_id == 0); - assert(shader_program_id == 0); -} - -// A safe wrapper around glGetString to report a "N/A" string in case glGetString returns nullptr. -inline std::string gl_get_string_safe(GLenum param) -{ - const char *value = (const char*)glGetString(param); - return std::string(value ? value : "N/A"); -} - -bool GLShader::load_from_text(const char *fragment_shader, const char *vertex_shader) -{ - std::string gl_version = gl_get_string_safe(GL_VERSION); - int major = atoi(gl_version.c_str()); - //int minor = atoi(gl_version.c_str() + gl_version.find('.') + 1); - if (major < 2) { - // Cannot create a shader object on OpenGL 1.x. - // Form an error message. - std::string gl_vendor = gl_get_string_safe(GL_VENDOR); - std::string gl_renderer = gl_get_string_safe(GL_RENDERER); - std::string glsl_version = gl_get_string_safe(GL_SHADING_LANGUAGE_VERSION); - last_error = "Your computer does not support OpenGL shaders.\n"; -#ifdef _WIN32 - if (gl_vendor == "Microsoft Corporation" && gl_renderer == "GDI Generic") { - last_error = "Windows is using a software OpenGL renderer.\n" - "You are either connected over remote desktop,\n" - "or a hardware acceleration is not available.\n"; - } -#endif - last_error += "GL version: " + gl_version + "\n"; - last_error += "vendor: " + gl_vendor + "\n"; - last_error += "renderer: " + gl_renderer + "\n"; - last_error += "GLSL version: " + glsl_version + "\n"; - return false; - } - - if (fragment_shader != nullptr) { - this->fragment_program_id = glCreateShader(GL_FRAGMENT_SHADER); - if (this->fragment_program_id == 0) { - last_error = "glCreateShader(GL_FRAGMENT_SHADER) failed."; - return false; - } - GLint len = (GLint)strlen(fragment_shader); - glShaderSource(this->fragment_program_id, 1, &fragment_shader, &len); - glCompileShader(this->fragment_program_id); - GLint params; - glGetShaderiv(this->fragment_program_id, GL_COMPILE_STATUS, ¶ms); - if (params == GL_FALSE) { - // Compilation failed. Get the log. - glGetShaderiv(this->fragment_program_id, GL_INFO_LOG_LENGTH, ¶ms); - std::vector<char> msg(params); - glGetShaderInfoLog(this->fragment_program_id, params, ¶ms, msg.data()); - this->last_error = std::string("Fragment shader compilation failed:\n") + msg.data(); - this->release(); - return false; - } - } - - if (vertex_shader != nullptr) { - this->vertex_program_id = glCreateShader(GL_VERTEX_SHADER); - if (this->vertex_program_id == 0) { - last_error = "glCreateShader(GL_VERTEX_SHADER) failed."; - this->release(); - return false; - } - GLint len = (GLint)strlen(vertex_shader); - glShaderSource(this->vertex_program_id, 1, &vertex_shader, &len); - glCompileShader(this->vertex_program_id); - GLint params; - glGetShaderiv(this->vertex_program_id, GL_COMPILE_STATUS, ¶ms); - if (params == GL_FALSE) { - // Compilation failed. Get the log. - glGetShaderiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms); - std::vector<char> msg(params); - glGetShaderInfoLog(this->vertex_program_id, params, ¶ms, msg.data()); - this->last_error = std::string("Vertex shader compilation failed:\n") + msg.data(); - this->release(); - return false; - } - } - - // Link shaders - this->shader_program_id = glCreateProgram(); - if (this->shader_program_id == 0) { - last_error = "glCreateProgram() failed."; - this->release(); - return false; - } - - if (this->fragment_program_id) - glAttachShader(this->shader_program_id, this->fragment_program_id); - if (this->vertex_program_id) - glAttachShader(this->shader_program_id, this->vertex_program_id); - glLinkProgram(this->shader_program_id); - - GLint params; - glGetProgramiv(this->shader_program_id, GL_LINK_STATUS, ¶ms); - if (params == GL_FALSE) { - // Linking failed. Get the log. - glGetProgramiv(this->vertex_program_id, GL_INFO_LOG_LENGTH, ¶ms); - std::vector<char> msg(params); - glGetProgramInfoLog(this->vertex_program_id, params, ¶ms, msg.data()); - this->last_error = std::string("Shader linking failed:\n") + msg.data(); - this->release(); - return false; - } - - last_error.clear(); - return true; -} - -bool GLShader::load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename) -{ - const std::string& path = resources_dir() + "/shaders/"; - - boost::nowide::ifstream vs(path + std::string(vertex_shader_filename), boost::nowide::ifstream::binary); - if (!vs.good()) - return false; - - vs.seekg(0, vs.end); - int file_length = vs.tellg(); - vs.seekg(0, vs.beg); - std::string vertex_shader(file_length, '\0'); - vs.read(const_cast<char*>(vertex_shader.data()), file_length); - if (!vs.good()) - return false; - - vs.close(); - - boost::nowide::ifstream fs(path + std::string(fragment_shader_filename), boost::nowide::ifstream::binary); - if (!fs.good()) - return false; - - fs.seekg(0, fs.end); - file_length = fs.tellg(); - fs.seekg(0, fs.beg); - std::string fragment_shader(file_length, '\0'); - fs.read(const_cast<char*>(fragment_shader.data()), file_length); - if (!fs.good()) - return false; - - fs.close(); - - return load_from_text(fragment_shader.c_str(), vertex_shader.c_str()); -} - -void GLShader::release() -{ - if (this->shader_program_id) { - if (this->vertex_program_id) - glDetachShader(this->shader_program_id, this->vertex_program_id); - if (this->fragment_program_id) - glDetachShader(this->shader_program_id, this->fragment_program_id); - glDeleteProgram(this->shader_program_id); - this->shader_program_id = 0; - } - - if (this->vertex_program_id) { - glDeleteShader(this->vertex_program_id); - this->vertex_program_id = 0; - } - if (this->fragment_program_id) { - glDeleteShader(this->fragment_program_id); - this->fragment_program_id = 0; - } -} - -void GLShader::enable() const -{ - glUseProgram(this->shader_program_id); -} - -void GLShader::disable() const -{ - glUseProgram(0); -} - -// Return shader vertex attribute ID -int GLShader::get_attrib_location(const char *name) const -{ - return this->shader_program_id ? glGetAttribLocation(this->shader_program_id, name) : -1; -} - -// Return shader uniform variable ID -int GLShader::get_uniform_location(const char *name) const -{ - return this->shader_program_id ? glGetUniformLocation(this->shader_program_id, name) : -1; -} - -bool GLShader::set_uniform(const char *name, float value) const -{ - int id = this->get_uniform_location(name); - if (id >= 0) { - glUniform1fARB(id, value); - return true; - } - return false; -} - -bool GLShader::set_uniform(const char* name, const float* matrix) const -{ - int id = get_uniform_location(name); - if (id >= 0) - { - ::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix); - return true; - } - return false; -} - -/* -# Set shader vector -sub SetVector -{ - my($self,$var,@values) = @_; - - my $id = $self->Map($var); - return 'Unable to map $var' if (!defined($id)); - - my $count = scalar(@values); - eval('glUniform'.$count.'fARB($id,@values)'); - - return ''; -} - -# Set shader 4x4 matrix -sub SetMatrix -{ - my($self,$var,$oga) = @_; - - my $id = $self->Map($var); - return 'Unable to map $var' if (!defined($id)); - - glUniformMatrix4fvARB_c($id,1,0,$oga->ptr()); - return ''; -} -*/ - -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLShader.hpp b/xs/src/slic3r/GUI/GLShader.hpp deleted file mode 100644 index 803b2f154..000000000 --- a/xs/src/slic3r/GUI/GLShader.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef slic3r_GLShader_hpp_ -#define slic3r_GLShader_hpp_ - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Point.hpp" - -namespace Slic3r { - -class GLShader -{ -public: - GLShader() : - fragment_program_id(0), - vertex_program_id(0), - shader_program_id(0) - {} - ~GLShader(); - - bool load_from_text(const char *fragment_shader, const char *vertex_shader); - bool load_from_file(const char* fragment_shader_filename, const char* vertex_shader_filename); - - void release(); - - int get_attrib_location(const char *name) const; - int get_uniform_location(const char *name) const; - - bool set_uniform(const char *name, float value) const; - bool set_uniform(const char* name, const float* matrix) const; - - void enable() const; - void disable() const; - - unsigned int fragment_program_id; - unsigned int vertex_program_id; - unsigned int shader_program_id; - std::string last_error; -}; - -} - -#endif /* slic3r_GLShader_hpp_ */ diff --git a/xs/src/slic3r/GUI/GLTexture.cpp b/xs/src/slic3r/GUI/GLTexture.cpp deleted file mode 100644 index 235e3d93b..000000000 --- a/xs/src/slic3r/GUI/GLTexture.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "GLTexture.hpp" - -#include <GL/glew.h> - -#include <wx/image.h> - -#include <boost/filesystem.hpp> - -#include <vector> -#include <algorithm> - -namespace Slic3r { -namespace GUI { - -GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } }; - -GLTexture::GLTexture() - : m_id(0) - , m_width(0) - , m_height(0) - , m_source("") -{ -} - -GLTexture::~GLTexture() -{ - reset(); -} - -bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmaps) -{ - reset(); - - if (!boost::filesystem::exists(filename)) - return false; - - // Load a PNG with an alpha channel. - wxImage image; - if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) - { - reset(); - return false; - } - - m_width = image.GetWidth(); - m_height = image.GetHeight(); - int n_pixels = m_width * m_height; - - if (n_pixels <= 0) - { - reset(); - return false; - } - - // Get RGB & alpha raw data from wxImage, pack them into an array. - unsigned char* img_rgb = image.GetData(); - if (img_rgb == nullptr) - { - reset(); - return false; - } - - unsigned char* img_alpha = image.GetAlpha(); - - std::vector<unsigned char> data(n_pixels * 4, 0); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - // sends data to gpu - ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - ::glGenTextures(1, &m_id); - ::glBindTexture(GL_TEXTURE_2D, m_id); - ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - if (generate_mipmaps) - { - // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards - unsigned int levels_count = _generate_mipmaps(image); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - } - else - { - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - } - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - ::glBindTexture(GL_TEXTURE_2D, 0); - - m_source = filename; - return true; -} - -void GLTexture::reset() -{ - if (m_id != 0) - ::glDeleteTextures(1, &m_id); - - m_id = 0; - m_width = 0; - m_height = 0; - m_source = ""; -} - -unsigned int GLTexture::get_id() const -{ - return m_id; -} - -int GLTexture::get_width() const -{ - return m_width; -} - -int GLTexture::get_height() const -{ - return m_height; -} - -const std::string& GLTexture::get_source() const -{ - return m_source; -} - -void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top) -{ - render_sub_texture(tex_id, left, right, bottom, top, FullTextureUVs); -} - -void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const GLTexture::Quad_UVs& uvs) -{ - ::glEnable(GL_BLEND); - ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - ::glEnable(GL_TEXTURE_2D); - ::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - - ::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id); - - ::glBegin(GL_QUADS); - ::glTexCoord2f(uvs.left_bottom.u, uvs.left_bottom.v); ::glVertex2f(left, bottom); - ::glTexCoord2f(uvs.right_bottom.u, uvs.right_bottom.v); ::glVertex2f(right, bottom); - ::glTexCoord2f(uvs.right_top.u, uvs.right_top.v); ::glVertex2f(right, top); - ::glTexCoord2f(uvs.left_top.u, uvs.left_top.v); ::glVertex2f(left, top); - ::glEnd(); - - ::glBindTexture(GL_TEXTURE_2D, 0); - - ::glDisable(GL_TEXTURE_2D); - ::glDisable(GL_BLEND); -} - -unsigned int GLTexture::_generate_mipmaps(wxImage& image) -{ - int w = image.GetWidth(); - int h = image.GetHeight(); - GLint level = 0; - std::vector<unsigned char> data(w * h * 4, 0); - - while ((w > 1) || (h > 1)) - { - ++level; - - w = std::max(w / 2, 1); - h = std::max(h / 2, 1); - - int n_pixels = w * h; - - image = image.ResampleBicubic(w, h); - - unsigned char* img_rgb = image.GetData(); - unsigned char* img_alpha = image.GetAlpha(); - - data.resize(n_pixels * 4); - for (int i = 0; i < n_pixels; ++i) - { - int data_id = i * 4; - int img_id = i * 3; - data[data_id + 0] = img_rgb[img_id + 0]; - data[data_id + 1] = img_rgb[img_id + 1]; - data[data_id + 2] = img_rgb[img_id + 2]; - data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255; - } - - ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); - } - - return (unsigned int)level; -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLTexture.hpp b/xs/src/slic3r/GUI/GLTexture.hpp deleted file mode 100644 index e027bd152..000000000 --- a/xs/src/slic3r/GUI/GLTexture.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef slic3r_GLTexture_hpp_ -#define slic3r_GLTexture_hpp_ - -#include <string> - -class wxImage; - -namespace Slic3r { -namespace GUI { - - class GLTexture - { - public: - struct UV - { - float u; - float v; - }; - - struct Quad_UVs - { - UV left_bottom; - UV right_bottom; - UV right_top; - UV left_top; - }; - - static Quad_UVs FullTextureUVs; - - protected: - unsigned int m_id; - int m_width; - int m_height; - std::string m_source; - - public: - GLTexture(); - virtual ~GLTexture(); - - bool load_from_file(const std::string& filename, bool generate_mipmaps); - void reset(); - - unsigned int get_id() const; - int get_width() const; - int get_height() const; - - const std::string& get_source() const; - - static void render_texture(unsigned int tex_id, float left, float right, float bottom, float top); - static void render_sub_texture(unsigned int tex_id, float left, float right, float bottom, float top, const Quad_UVs& uvs); - - protected: - unsigned int _generate_mipmaps(wxImage& image); - }; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLTexture_hpp_ - diff --git a/xs/src/slic3r/GUI/GLToolbar.cpp b/xs/src/slic3r/GUI/GLToolbar.cpp deleted file mode 100644 index 388868b12..000000000 --- a/xs/src/slic3r/GUI/GLToolbar.cpp +++ /dev/null @@ -1,722 +0,0 @@ -#include "../../libslic3r/Point.hpp" -#include "GLToolbar.hpp" - -#include "../../libslic3r/libslic3r.h" -#include "../../slic3r/GUI/GLCanvas3D.hpp" - -#include <GL/glew.h> - -#include <wx/bitmap.h> -#include <wx/dcmemory.h> -#include <wx/settings.h> - -namespace Slic3r { -namespace GUI { - -GLToolbarItem::Data::Data() - : name("") - , tooltip("") - , sprite_id(-1) - , is_toggable(false) - , action_callback(nullptr) -{ -} - -GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Data& data) - : m_type(type) - , m_state(Disabled) - , m_data(data) -{ -} - -GLToolbarItem::EState GLToolbarItem::get_state() const -{ - return m_state; -} - -void GLToolbarItem::set_state(GLToolbarItem::EState state) -{ - m_state = state; -} - -const std::string& GLToolbarItem::get_name() const -{ - return m_data.name; -} - -const std::string& GLToolbarItem::get_tooltip() const -{ - return m_data.tooltip; -} - -void GLToolbarItem::do_action() -{ - if (m_data.action_callback != nullptr) - m_data.action_callback->call(); -} - -bool GLToolbarItem::is_enabled() const -{ - return m_state != Disabled; -} - -bool GLToolbarItem::is_hovered() const -{ - return (m_state == Hover) || (m_state == HoverPressed); -} - -bool GLToolbarItem::is_pressed() const -{ - return (m_state == Pressed) || (m_state == HoverPressed); -} - -bool GLToolbarItem::is_toggable() const -{ - return m_data.is_toggable; -} - -bool GLToolbarItem::is_separator() const -{ - return m_type == Separator; -} - -void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const -{ - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(texture_size, border_size, icon_size, gap_size)); -} - -GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const -{ - GLTexture::Quad_UVs uvs; - - float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; - - float scaled_icon_size = (float)icon_size * inv_texture_size; - float scaled_border_size = (float)border_size * inv_texture_size; - float scaled_gap_size = (float)gap_size * inv_texture_size; - float stride = scaled_icon_size + scaled_gap_size; - - float left = scaled_border_size + (float)m_state * stride; - float right = left + scaled_icon_size; - float top = scaled_border_size + (float)m_data.sprite_id * stride; - float bottom = top + scaled_icon_size; - - uvs.left_top = { left, top }; - uvs.left_bottom = { left, bottom }; - uvs.right_bottom = { right, bottom }; - uvs.right_top = { right, top }; - - return uvs; -} - -GLToolbar::ItemsIconsTexture::ItemsIconsTexture() - : items_icon_size(0) - , items_icon_border_size(0) - , items_icon_gap_size(0) -{ -} - -GLToolbar::Layout::Layout() - : type(Horizontal) - , top(0.0f) - , left(0.0f) - , separator_size(0.0f) - , gap_size(0.0f) -{ -} - -GLToolbar::GLToolbar(GLCanvas3D& parent) - : m_parent(parent) - , m_enabled(false) -{ -} - -bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) -{ - std::string path = resources_dir() + "/icons/"; - bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false); - if (res) - { - m_icons_texture.items_icon_size = items_icon_size; - m_icons_texture.items_icon_border_size = items_icon_border_size; - m_icons_texture.items_icon_gap_size = items_icon_gap_size; - } - - return res; -} - -GLToolbar::Layout::Type GLToolbar::get_layout_type() const -{ - return m_layout.type; -} - -void GLToolbar::set_layout_type(GLToolbar::Layout::Type type) -{ - m_layout.type = type; -} - -void GLToolbar::set_position(float top, float left) -{ - m_layout.top = top; - m_layout.left = left; -} - -void GLToolbar::set_separator_size(float size) -{ - m_layout.separator_size = size; -} - -void GLToolbar::set_gap_size(float size) -{ - m_layout.gap_size = size; -} - -bool GLToolbar::is_enabled() const -{ - return m_enabled; -} - -void GLToolbar::set_enabled(bool enable) -{ - m_enabled = true; -} - -bool GLToolbar::add_item(const GLToolbarItem::Data& data) -{ - GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Action, data); - if (item == nullptr) - return false; - - m_items.push_back(item); - return true; -} - -bool GLToolbar::add_separator() -{ - GLToolbarItem::Data data; - GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Separator, data); - if (item == nullptr) - return false; - - m_items.push_back(item); - return true; -} - -float GLToolbar::get_width() const -{ - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return get_width_horizontal(); - } - case Layout::Vertical: - { - return get_width_vertical(); - } - } -} - -float GLToolbar::get_height() const -{ - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return get_height_horizontal(); - } - case Layout::Vertical: - { - return get_height_vertical(); - } - } -} - -void GLToolbar::enable_item(const std::string& name) -{ - for (GLToolbarItem* item : m_items) - { - if ((item->get_name() == name) && (item->get_state() == GLToolbarItem::Disabled)) - { - item->set_state(GLToolbarItem::Normal); - return; - } - } -} - -void GLToolbar::disable_item(const std::string& name) -{ - for (GLToolbarItem* item : m_items) - { - if (item->get_name() == name) - { - item->set_state(GLToolbarItem::Disabled); - return; - } - } -} - -bool GLToolbar::is_item_pressed(const std::string& name) const -{ - for (GLToolbarItem* item : m_items) - { - if (item->get_name() == name) - return item->is_pressed(); - } - - return false; -} - -void GLToolbar::update_hover_state(const Vec2d& mouse_pos) -{ - if (!m_enabled) - return; - - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - update_hover_state_horizontal(mouse_pos); - break; - } - case Layout::Vertical: - { - update_hover_state_vertical(mouse_pos); - break; - } - } -} - -int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const -{ - if (!m_enabled) - return -1; - - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return contains_mouse_horizontal(mouse_pos); - } - case Layout::Vertical: - { - return contains_mouse_vertical(mouse_pos); - } - } -} - -void GLToolbar::do_action(unsigned int item_id) -{ - if (item_id < (unsigned int)m_items.size()) - { - GLToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && !item->is_separator() && item->is_hovered()) - { - if (item->is_toggable()) - { - GLToolbarItem::EState state = item->get_state(); - if (state == GLToolbarItem::Hover) - item->set_state(GLToolbarItem::HoverPressed); - else if (state == GLToolbarItem::HoverPressed) - item->set_state(GLToolbarItem::Hover); - - m_parent.render(); - item->do_action(); - } - else - { - item->set_state(GLToolbarItem::HoverPressed); - m_parent.render(); - item->do_action(); - if (item->get_state() != GLToolbarItem::Disabled) - { - // the item may get disabled during the action, if not, set it back to hover state - item->set_state(GLToolbarItem::Hover); - m_parent.render(); - } - } - } - } -} - -void GLToolbar::render() const -{ - if (!m_enabled || m_items.empty()) - return; - - ::glDisable(GL_DEPTH_TEST); - - ::glPushMatrix(); - ::glLoadIdentity(); - - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - render_horizontal(); - break; - } - case Layout::Vertical: - { - render_vertical(); - break; - } - } - - ::glPopMatrix(); -} - -float GLToolbar::get_width_horizontal() const -{ - return get_main_size(); -} - -float GLToolbar::get_width_vertical() const -{ - return m_icons_texture.items_icon_size; -} - -float GLToolbar::get_height_horizontal() const -{ - return m_icons_texture.items_icon_size; -} - -float GLToolbar::get_height_vertical() const -{ - return get_main_size(); -} - -float GLToolbar::get_main_size() const -{ - float size = 0.0f; - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - { - if (m_items[i]->is_separator()) - size += m_layout.separator_size; - else - size += (float)m_icons_texture.items_icon_size; - } - - if (m_items.size() > 1) - size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; - - return size; -} - -void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) -{ - float zoom = m_parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = m_parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - float scaled_separator_size = m_layout.separator_size * inv_zoom; - float scaled_gap_size = m_layout.gap_size * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - - std::string tooltip = ""; - - for (GLToolbarItem* item : m_items) - { - if (item->is_separator()) - left += separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - GLToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); - - switch (state) - { - case GLToolbarItem::Normal: - { - if (inside) - item->set_state(GLToolbarItem::Hover); - - break; - } - case GLToolbarItem::Hover: - { - if (inside) - tooltip = item->get_tooltip(); - else - item->set_state(GLToolbarItem::Normal); - - break; - } - case GLToolbarItem::Pressed: - { - if (inside) - item->set_state(GLToolbarItem::HoverPressed); - - break; - } - case GLToolbarItem::HoverPressed: - { - if (inside) - tooltip = item->get_tooltip(); - else - item->set_state(GLToolbarItem::Pressed); - - break; - } - default: - case GLToolbarItem::Disabled: - { - break; - } - } - - left += icon_stride; - } - } - - m_parent.set_tooltip(tooltip); -} - -void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) -{ - float zoom = m_parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = m_parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - float scaled_separator_size = m_layout.separator_size * inv_zoom; - float scaled_gap_size = m_layout.gap_size * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - - std::string tooltip = ""; - - for (GLToolbarItem* item : m_items) - { - if (item->is_separator()) - top -= separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - GLToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); - - switch (state) - { - case GLToolbarItem::Normal: - { - if (inside) - item->set_state(GLToolbarItem::Hover); - - break; - } - case GLToolbarItem::Hover: - { - if (inside) - tooltip = item->get_tooltip(); - else - item->set_state(GLToolbarItem::Normal); - - break; - } - case GLToolbarItem::Pressed: - { - if (inside) - item->set_state(GLToolbarItem::HoverPressed); - - break; - } - case GLToolbarItem::HoverPressed: - { - if (inside) - tooltip = item->get_tooltip(); - else - item->set_state(GLToolbarItem::Pressed); - - break; - } - default: - case GLToolbarItem::Disabled: - { - break; - } - } - - top -= icon_stride; - } - } - - m_parent.set_tooltip(tooltip); -} - -int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const -{ - float zoom = m_parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = m_parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - float scaled_separator_size = m_layout.separator_size * inv_zoom; - float scaled_gap_size = m_layout.gap_size * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - - int id = -1; - - for (GLToolbarItem* item : m_items) - { - ++id; - - if (item->is_separator()) - left += separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - left += icon_stride; - } - } - - return -1; -} - -int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const -{ - float zoom = m_parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = m_parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - float scaled_separator_size = m_layout.separator_size * inv_zoom; - float scaled_gap_size = m_layout.gap_size * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - - int id = -1; - - for (GLToolbarItem* item : m_items) - { - ++id; - - if (item->is_separator()) - top -= separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - top -= icon_stride; - } - } - - return -1; -} - -void GLToolbar::render_horizontal() const -{ - unsigned int tex_id = m_icons_texture.texture.get_id(); - int tex_size = m_icons_texture.texture.get_width(); - - if ((tex_id == 0) || (tex_size <= 0)) - return; - - float zoom = m_parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - float scaled_separator_size = m_layout.separator_size * inv_zoom; - float scaled_gap_size = m_layout.gap_size * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - - // renders icons - for (const GLToolbarItem* item : m_items) - { - if (item->is_separator()) - left += separator_stride; - else - { - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); - left += icon_stride; - } - } -} - -void GLToolbar::render_vertical() const -{ - unsigned int tex_id = m_icons_texture.texture.get_id(); - int tex_size = m_icons_texture.texture.get_width(); - - if ((tex_id == 0) || (tex_size <= 0)) - return; - - float zoom = m_parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - float scaled_separator_size = m_layout.separator_size * inv_zoom; - float scaled_gap_size = m_layout.gap_size * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - - // renders icons - for (const GLToolbarItem* item : m_items) - { - if (item->is_separator()) - top -= separator_stride; - else - { - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); - top -= icon_stride; - } - } -} - -} // namespace GUI -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/GLToolbar.hpp b/xs/src/slic3r/GUI/GLToolbar.hpp deleted file mode 100644 index 65d6748ff..000000000 --- a/xs/src/slic3r/GUI/GLToolbar.hpp +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef slic3r_GLToolbar_hpp_ -#define slic3r_GLToolbar_hpp_ - -#include "../../slic3r/GUI/GLTexture.hpp" -#include "../../callback.hpp" - -#include <string> -#include <vector> - -namespace Slic3r { -namespace GUI { - -class GLCanvas3D; - -class GLToolbarItem -{ -public: - enum EType : unsigned char - { - Action, - Separator, - Num_Types - }; - - enum EState : unsigned char - { - Normal, - Pressed, - Disabled, - Hover, - HoverPressed, - Num_States - }; - - struct Data - { - std::string name; - std::string tooltip; - unsigned int sprite_id; - bool is_toggable; - PerlCallback* action_callback; - - Data(); - }; - -private: - EType m_type; - EState m_state; - Data m_data; - -public: - GLToolbarItem(EType type, const Data& data); - - EState get_state() const; - void set_state(EState state); - - const std::string& get_name() const; - const std::string& get_tooltip() const; - - void do_action(); - - bool is_enabled() const; - bool is_hovered() const; - bool is_pressed() const; - - bool is_toggable() const; - bool is_separator() const; - - void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; - -private: - GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; -}; - -class GLToolbar -{ -public: - // items icon textures are assumed to be square and all with the same size in pixels, no internal check is done - // icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState - // from left to right - struct ItemsIconsTexture - { - GLTexture texture; - // size of the square icons, in pixels - unsigned int items_icon_size; - // distance from the border, in pixels - unsigned int items_icon_border_size; - // distance between two adjacent icons (to avoid filtering artifacts), in pixels - unsigned int items_icon_gap_size; - - ItemsIconsTexture(); - }; - - struct Layout - { - enum Type : unsigned char - { - Horizontal, - Vertical, - Num_Types - }; - - Type type; - float top; - float left; - float separator_size; - float gap_size; - - Layout(); - }; - -private: - typedef std::vector<GLToolbarItem*> ItemsList; - - GLCanvas3D& m_parent; - bool m_enabled; - ItemsIconsTexture m_icons_texture; - Layout m_layout; - - ItemsList m_items; - -public: - explicit GLToolbar(GLCanvas3D& parent); - - bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); - - Layout::Type get_layout_type() const; - void set_layout_type(Layout::Type type); - - void set_position(float top, float left); - void set_separator_size(float size); - void set_gap_size(float size); - - bool is_enabled() const; - void set_enabled(bool enable); - - bool add_item(const GLToolbarItem::Data& data); - bool add_separator(); - - float get_width() const; - float get_height() const; - - void enable_item(const std::string& name); - void disable_item(const std::string& name); - - bool is_item_pressed(const std::string& name) const; - - void update_hover_state(const Vec2d& mouse_pos); - - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos) const; - - void do_action(unsigned int item_id); - - void render() const; - -private: - float get_width_horizontal() const; - float get_width_vertical() const; - float get_height_horizontal() const; - float get_height_vertical() const; - float get_main_size() const; - void update_hover_state_horizontal(const Vec2d& mouse_pos); - void update_hover_state_vertical(const Vec2d& mouse_pos); - int contains_mouse_horizontal(const Vec2d& mouse_pos) const; - int contains_mouse_vertical(const Vec2d& mouse_pos) const; - - void render_horizontal() const; - void render_vertical() const; -}; - -} // namespace GUI -} // namespace Slic3r - -#endif // slic3r_GLToolbar_hpp_ diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp deleted file mode 100644 index decdb5691..000000000 --- a/xs/src/slic3r/GUI/GUI.cpp +++ /dev/null @@ -1,1387 +0,0 @@ -#include "GUI.hpp" -#include "WipeTowerDialog.hpp" - -#include <assert.h> -#include <cmath> - -#include <boost/lexical_cast.hpp> -#include <boost/algorithm/string.hpp> -#include <boost/format.hpp> -#include <boost/lexical_cast.hpp> - -#if __APPLE__ -#import <IOKit/pwr_mgt/IOPMLib.h> -#elif _WIN32 -#include <Windows.h> -// Undefine min/max macros incompatible with the standard library -// For example, std::numeric_limits<std::streamsize>::max() -// produces some weird errors -#ifdef min -#undef min -#endif -#ifdef max -#undef max -#endif -#include "boost/nowide/convert.hpp" -#endif - -#include <wx/app.h> -#include <wx/button.h> -#include <wx/dir.h> -#include <wx/filename.h> -#include <wx/frame.h> -#include <wx/menu.h> -#include <wx/notebook.h> -#include <wx/panel.h> -#include <wx/sizer.h> -#include <wx/combo.h> -#include <wx/window.h> -#include <wx/msgdlg.h> -#include <wx/settings.h> -#include <wx/display.h> -#include <wx/collpane.h> -#include <wx/wupdlock.h> - -#include "wxExtensions.hpp" - -#include "Tab.hpp" -#include "TabIface.hpp" -#include "AboutDialog.hpp" -#include "AppConfig.hpp" -#include "ConfigSnapshotDialog.hpp" -#include "ProgressStatusBar.hpp" -#include "Utils.hpp" -#include "MsgDialog.hpp" -#include "ConfigWizard.hpp" -#include "Preferences.hpp" -#include "PresetBundle.hpp" -#include "UpdateDialogs.hpp" -#include "FirmwareDialog.hpp" -#include "GUI_ObjectParts.hpp" - -#include "../Utils/PresetUpdater.hpp" -#include "../Config/Snapshot.hpp" - -#include "3DScene.hpp" -#include "libslic3r/I18N.hpp" -#include "Model.hpp" -#include "LambdaObjectDialog.hpp" - -#include "../../libslic3r/Print.hpp" - -namespace Slic3r { namespace GUI { - -#if __APPLE__ -IOPMAssertionID assertionID; -#endif - -void disable_screensaver() -{ - #if __APPLE__ - CFStringRef reasonForActivity = CFSTR("Slic3r"); - IOReturn success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, - kIOPMAssertionLevelOn, reasonForActivity, &assertionID); - // ignore result: success == kIOReturnSuccess - #elif _WIN32 - SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_CONTINUOUS); - #endif -} - -void enable_screensaver() -{ - #if __APPLE__ - IOReturn success = IOPMAssertionRelease(assertionID); - #elif _WIN32 - SetThreadExecutionState(ES_CONTINUOUS); - #endif -} - -bool debugged() -{ - #ifdef _WIN32 - return IsDebuggerPresent(); - #else - return false; - #endif /* _WIN32 */ -} - -void break_to_debugger() -{ - #ifdef _WIN32 - if (IsDebuggerPresent()) - DebugBreak(); - #endif /* _WIN32 */ -} - -// Passing the wxWidgets GUI classes instantiated by the Perl part to C++. -wxApp *g_wxApp = nullptr; -wxFrame *g_wxMainFrame = nullptr; -ProgressStatusBar *g_progress_status_bar = nullptr; -wxNotebook *g_wxTabPanel = nullptr; -wxPanel *g_wxPlater = nullptr; -AppConfig *g_AppConfig = nullptr; -PresetBundle *g_PresetBundle= nullptr; -PresetUpdater *g_PresetUpdater = nullptr; -wxColour g_color_label_modified; -wxColour g_color_label_sys; -wxColour g_color_label_default; - -std::vector<Tab *> g_tabs_list; - -wxLocale* g_wxLocale; - -wxFont g_small_font; -wxFont g_bold_font; - -std::vector <std::shared_ptr<ConfigOptionsGroup>> m_optgroups; -double m_brim_width = 0.0; -size_t m_label_width = 100; -wxButton* g_wiping_dialog_button = nullptr; - -//showed/hided controls according to the view mode -wxWindow *g_right_panel = nullptr; -wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr; -wxBoxSizer *g_info_sizer = nullptr; -wxBoxSizer *g_object_list_sizer = nullptr; -std::vector<wxButton*> g_buttons; -wxStaticBitmap *g_manifold_warning_icon = nullptr; -bool g_show_print_info = false; -bool g_show_manifold_warning_icon = false; - -static void init_label_colours() -{ - auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - if (luma >= 128) { - g_color_label_modified = wxColour(252, 77, 1); - g_color_label_sys = wxColour(26, 132, 57); - } else { - g_color_label_modified = wxColour(253, 111, 40); - g_color_label_sys = wxColour(115, 220, 103); - } - g_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT); -} - -void update_label_colours_from_appconfig() -{ - if (g_AppConfig->has("label_clr_sys")){ - auto str = g_AppConfig->get("label_clr_sys"); - if (str != "") - g_color_label_sys = wxColour(str); - } - - if (g_AppConfig->has("label_clr_modified")){ - auto str = g_AppConfig->get("label_clr_modified"); - if (str != "") - g_color_label_modified = wxColour(str); - } -} - -static void init_fonts() -{ - g_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - g_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); -#ifdef __WXMAC__ - g_small_font.SetPointSize(11); - g_bold_font.SetPointSize(13); -#endif /*__WXMAC__*/ -} - -static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } - -void set_wxapp(wxApp *app) -{ - g_wxApp = app; - // Let the libslic3r know the callback, which will translate messages on demand. - Slic3r::I18N::set_translate_callback(libslic3r_translate_callback); - init_label_colours(); - init_fonts(); -} - -void set_main_frame(wxFrame *main_frame) -{ - g_wxMainFrame = main_frame; -} - -wxFrame* get_main_frame() { return g_wxMainFrame; } - -void set_progress_status_bar(ProgressStatusBar *prsb) -{ - g_progress_status_bar = prsb; -} - -ProgressStatusBar* get_progress_status_bar() { return g_progress_status_bar; } - -void set_tab_panel(wxNotebook *tab_panel) -{ - g_wxTabPanel = tab_panel; -} - -void set_plater(wxPanel *plater) -{ - g_wxPlater = plater; -} - -void set_app_config(AppConfig *app_config) -{ - g_AppConfig = app_config; -} - -void set_preset_bundle(PresetBundle *preset_bundle) -{ - g_PresetBundle = preset_bundle; -} - -void set_preset_updater(PresetUpdater *updater) -{ - g_PresetUpdater = updater; -} - -enum ActionButtons -{ - abExportGCode, - abReslice, - abPrint, - abSendGCode, -}; - -void set_objects_from_perl( wxWindow* parent, - wxBoxSizer *frequently_changed_parameters_sizer, - wxBoxSizer *info_sizer, - wxButton *btn_export_gcode, - wxButton *btn_reslice, - wxButton *btn_print, - wxButton *btn_send_gcode, - wxStaticBitmap *manifold_warning_icon) -{ - g_right_panel = parent->GetParent(); - g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer; - g_info_sizer = info_sizer; - - g_buttons.push_back(btn_export_gcode); - g_buttons.push_back(btn_reslice); - g_buttons.push_back(btn_print); - g_buttons.push_back(btn_send_gcode); - - // Update font style for buttons - for (auto btn : g_buttons) - btn->SetFont(bold_font()); - - g_manifold_warning_icon = manifold_warning_icon; -} - -void set_show_print_info(bool show) -{ - g_show_print_info = show; -} - -void set_show_manifold_warning_icon(bool show) -{ - g_show_manifold_warning_icon = show; - if (!g_manifold_warning_icon) - return; - - // update manifold_warning_icon showing - if (show && !g_info_sizer->IsShown(static_cast<size_t>(0))) - g_show_manifold_warning_icon = false; - - g_manifold_warning_icon->Show(g_show_manifold_warning_icon); - g_manifold_warning_icon->GetParent()->Layout(); -} - -void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){ - g_object_list_sizer = objects_list_sizer; -} - -std::vector<Tab *>& get_tabs_list() -{ - return g_tabs_list; -} - -bool checked_tab(Tab* tab) -{ - bool ret = true; - if (find(g_tabs_list.begin(), g_tabs_list.end(), tab) == g_tabs_list.end()) - ret = false; - return ret; -} - -void delete_tab_from_list(Tab* tab) -{ - std::vector<Tab *>::iterator itr = find(g_tabs_list.begin(), g_tabs_list.end(), tab); - if (itr != g_tabs_list.end()) - g_tabs_list.erase(itr); -} - -bool select_language(wxArrayString & names, - wxArrayLong & identifiers) -{ - wxCHECK_MSG(names.Count() == identifiers.Count(), false, - _(L("Array of language names and identifiers should have the same size."))); - int init_selection = 0; - long current_language = g_wxLocale ? g_wxLocale->GetLanguage() : wxLANGUAGE_UNKNOWN; - for (auto lang : identifiers){ - if (lang == current_language) - break; - else - ++init_selection; - } - if (init_selection == identifiers.size()) - init_selection = 0; - long index = wxGetSingleChoiceIndex(_(L("Select the language")), _(L("Language")), - names, init_selection); - if (index != -1) - { - g_wxLocale = new wxLocale; - g_wxLocale->Init(identifiers[index]); - g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir())); - g_wxLocale->AddCatalog(g_wxApp->GetAppName()); - wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); - return true; - } - return false; -} - -bool load_language() -{ - wxString language = wxEmptyString; - if (g_AppConfig->has("translation_language")) - language = g_AppConfig->get("translation_language"); - - if (language.IsEmpty()) - return false; - wxArrayString names; - wxArrayLong identifiers; - get_installed_languages(names, identifiers); - for (size_t i = 0; i < identifiers.Count(); i++) - { - if (wxLocale::GetLanguageCanonicalName(identifiers[i]) == language) - { - g_wxLocale = new wxLocale; - g_wxLocale->Init(identifiers[i]); - g_wxLocale->AddCatalogLookupPathPrefix(wxPathOnly(localization_dir())); - g_wxLocale->AddCatalog(g_wxApp->GetAppName()); - wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); - return true; - } - } - return false; -} - -void save_language() -{ - wxString language = wxEmptyString; - if (g_wxLocale) - language = g_wxLocale->GetCanonicalName(); - - g_AppConfig->set("translation_language", language.ToStdString()); - g_AppConfig->save(); -} - -void get_installed_languages(wxArrayString & names, - wxArrayLong & identifiers) -{ - names.Clear(); - identifiers.Clear(); - - wxDir dir(wxPathOnly(localization_dir())); - wxString filename; - const wxLanguageInfo * langinfo; - wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT); - if (!name.IsEmpty()) - { - names.Add(_(L("Default"))); - identifiers.Add(wxLANGUAGE_DEFAULT); - } - for (bool cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS); - cont; cont = dir.GetNext(&filename)) - { - langinfo = wxLocale::FindLanguageInfo(filename); - if (langinfo != NULL) - { - auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + - filename + wxFileName::GetPathSeparator() + - g_wxApp->GetAppName() + wxT(".mo"); - if (wxFileExists(full_file_name)) - { - names.Add(langinfo->Description); - identifiers.Add(langinfo->Language); - } - } - } -} - -enum ConfigMenuIDs { - ConfigMenuWizard, - ConfigMenuSnapshots, - ConfigMenuTakeSnapshot, - ConfigMenuUpdate, - ConfigMenuPreferences, - ConfigMenuModeSimple, - ConfigMenuModeExpert, - ConfigMenuLanguage, - ConfigMenuFlashFirmware, - ConfigMenuCnt, -}; - -ConfigMenuIDs get_view_mode() -{ - if (!g_AppConfig->has("view_mode")) - return ConfigMenuModeSimple; - - const auto mode = g_AppConfig->get("view_mode"); - return mode == "expert" ? ConfigMenuModeExpert : ConfigMenuModeSimple; -} - -static wxString dots("…", wxConvUTF8); - -void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_language_change) -{ - auto local_menu = new wxMenu(); - wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt); - - const auto config_wizard_name = _(ConfigWizard::name().wx_str()); - const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name); - // Cmd+, is standard on OS X - what about other operating systems? - local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip); - local_menu->Append(config_id_base + ConfigMenuSnapshots, _(L("Configuration Snapshots"))+dots, _(L("Inspect / activate configuration snapshots"))); - local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration Snapshot")), _(L("Capture a configuration snapshot"))); -// local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); - local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("Preferences"))+dots+"\tCtrl+,", _(L("Application preferences"))); - local_menu->AppendSeparator(); - auto mode_menu = new wxMenu(); - mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("&Simple")), _(L("Simple View Mode"))); - mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeExpert, _(L("&Expert")), _(L("Expert View Mode"))); - mode_menu->Check(config_id_base + get_view_mode(), true); - local_menu->AppendSubMenu(mode_menu, _(L("&Mode")), _(L("Slic3r View Mode"))); - local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuLanguage, _(L("Change Application Language"))); - local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuFlashFirmware, _(L("Flash printer firmware")), _(L("Upload a firmware image into an Arduino based printer"))); - // TODO: for when we're able to flash dictionaries - // local_menu->Append(config_id_base + FirmwareMenuDict, _(L("Flash language file")), _(L("Upload a language dictionary file into a Prusa printer"))); - - local_menu->Bind(wxEVT_MENU, [config_id_base, event_language_change, event_preferences_changed](wxEvent &event){ - switch (event.GetId() - config_id_base) { - case ConfigMenuWizard: - config_wizard(ConfigWizard::RR_USER); - break; - case ConfigMenuTakeSnapshot: - // Take a configuration snapshot. - if (check_unsaved_changes()) { - wxTextEntryDialog dlg(nullptr, _(L("Taking configuration snapshot")), _(L("Snapshot name"))); - if (dlg.ShowModal() == wxID_OK) - g_AppConfig->set("on_snapshot", - Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot( - *g_AppConfig, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()).id); - } - break; - case ConfigMenuSnapshots: - if (check_unsaved_changes()) { - std::string on_snapshot; - if (Config::SnapshotDB::singleton().is_on_snapshot(*g_AppConfig)) - on_snapshot = g_AppConfig->get("on_snapshot"); - ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot); - dlg.ShowModal(); - if (! dlg.snapshot_to_activate().empty()) { - if (! Config::SnapshotDB::singleton().is_on_snapshot(*g_AppConfig)) - Config::SnapshotDB::singleton().take_snapshot(*g_AppConfig, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK); - g_AppConfig->set("on_snapshot", - Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *g_AppConfig).id); - g_PresetBundle->load_presets(*g_AppConfig); - // Load the currently selected preset into the GUI, update the preset selection box. - load_current_presets(); - } - } - break; - case ConfigMenuPreferences: - { - PreferencesDialog dlg(g_wxMainFrame, event_preferences_changed); - dlg.ShowModal(); - break; - } - case ConfigMenuLanguage: - { - wxArrayString names; - wxArrayLong identifiers; - get_installed_languages(names, identifiers); - if (select_language(names, identifiers)) { - save_language(); - show_info(g_wxTabPanel, _(L("Application will be restarted")), _(L("Attention!"))); - if (event_language_change > 0) { - _3DScene::remove_all_canvases();// remove all canvas before recreate GUI - wxCommandEvent event(event_language_change); - g_wxApp->ProcessEvent(event); - } - } - break; - } - case ConfigMenuFlashFirmware: - FirmwareDialog::run(g_wxMainFrame); - break; - default: - break; - } - }); - mode_menu->Bind(wxEVT_MENU, [config_id_base](wxEvent& event) { - std::string mode = event.GetId() - config_id_base == ConfigMenuModeExpert ? - "expert" : "simple"; - g_AppConfig->set("view_mode", mode); - g_AppConfig->save(); - update_mode(); - }); - menu->Append(local_menu, _(L("&Configuration"))); -} - -void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change) -{ - add_config_menu(menu, event_preferences_changed, event_language_change); -} - -void open_model(wxWindow *parent, wxArrayString& input_files){ - t_file_wild_card vec_FILE_WILDCARDS = get_file_wild_card(); - std::vector<std::string> file_types = { "known", "stl", "obj", "amf", "3mf", "prusa" }; - wxString MODEL_WILDCARD; - for (auto file_type : file_types) - MODEL_WILDCARD += vec_FILE_WILDCARDS.at(file_type) + "|"; - - auto dlg_title = _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")); - auto dialog = new wxFileDialog(parent /*? parent : GetTopWindow(g_wxMainFrame)*/, dlg_title, - g_AppConfig->get_last_dir(), "", - MODEL_WILDCARD, wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); - if (dialog->ShowModal() != wxID_OK) { - dialog->Destroy(); - return ; - } - - dialog->GetPaths(input_files); - dialog->Destroy(); -} - -// This is called when closing the application, when loading a config file or when starting the config wizard -// to notify the user whether he is aware that some preset changes will be lost. -bool check_unsaved_changes() -{ - std::string dirty; - for (Tab *tab : g_tabs_list) - if (tab->current_preset_is_dirty()) - if (dirty.empty()) - dirty = tab->name(); - else - dirty += std::string(", ") + tab->name(); - if (dirty.empty()) - // No changes, the application may close or reload presets. - return true; - // Ask the user. - auto dialog = new wxMessageDialog(g_wxMainFrame, - _(L("You have unsaved changes ")) + dirty + _(L(". Discard changes and continue anyway?")), - _(L("Unsaved Presets")), - wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT); - return dialog->ShowModal() == wxID_YES; -} - -bool config_wizard_startup(bool app_config_exists) -{ - if (! app_config_exists || g_PresetBundle->printers.size() <= 1) { - config_wizard(ConfigWizard::RR_DATA_EMPTY); - return true; - } else if (g_AppConfig->legacy_datadir()) { - // Looks like user has legacy pre-vendorbundle data directory, - // explain what this is and run the wizard - - MsgDataLegacy dlg; - dlg.ShowModal(); - - config_wizard(ConfigWizard::RR_DATA_LEGACY); - return true; - } - return false; -} - -void config_wizard(int reason) -{ - // Exit wizard if there are unsaved changes and the user cancels the action. - if (! check_unsaved_changes()) - return; - - try { - ConfigWizard wizard(nullptr, static_cast<ConfigWizard::RunReason>(reason)); - wizard.run(g_PresetBundle, g_PresetUpdater); - } - catch (const std::exception &e) { - show_error(nullptr, e.what()); - } - - // Load the currently selected preset into the GUI, update the preset selection box. - load_current_presets(); -} - -void open_preferences_dialog(int event_preferences) -{ - auto dlg = new PreferencesDialog(g_wxMainFrame, event_preferences); - dlg->ShowModal(); -} - -void create_preset_tabs(int event_value_change, int event_presets_changed) -{ - update_label_colours_from_appconfig(); - add_created_tab(new TabPrint (g_wxTabPanel), event_value_change, event_presets_changed); - add_created_tab(new TabFilament (g_wxTabPanel), event_value_change, event_presets_changed); - add_created_tab(new TabSLAMaterial (g_wxTabPanel), event_value_change, event_presets_changed); - add_created_tab(new TabPrinter (g_wxTabPanel), event_value_change, event_presets_changed); -} - -std::vector<PresetTab> preset_tabs = { - { "print", nullptr, ptFFF }, - { "filament", nullptr, ptFFF }, - { "sla_material", nullptr, ptSLA } -}; -const std::vector<PresetTab>& get_preset_tabs() { - return preset_tabs; -} - -Tab* get_tab(const std::string& name) -{ - std::vector<PresetTab>::iterator it = std::find_if(preset_tabs.begin(), preset_tabs.end(), - [name](PresetTab& tab){ return name == tab.name; }); - return it != preset_tabs.end() ? it->panel : nullptr; -} - -TabIface* get_preset_tab_iface(char *name) -{ - Tab* tab = get_tab(name); - if (tab) return new TabIface(tab); - - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++ i) { - Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i)); - if (! tab) - continue; - if (tab->name() == name) { - return new TabIface(tab); - } - } - return new TabIface(nullptr); -} - -// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) -void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) -{ - try{ - switch (config.def()->get(opt_key)->type){ - case coFloatOrPercent:{ - std::string str = boost::any_cast<std::string>(value); - bool percent = false; - if (str.back() == '%'){ - str.pop_back(); - percent = true; - } - double val = stod(str); - config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent)); - break;} - case coPercent: - config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast<double>(value))); - break; - case coFloat:{ - double& val = config.opt_float(opt_key); - val = boost::any_cast<double>(value); - break; - } - case coPercents:{ - ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast<double>(value) }; - config.option<ConfigOptionPercents>(opt_key)->set_at(vec_new, opt_index, opt_index); - break; - } - case coFloats:{ - ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast<double>(value) }; - config.option<ConfigOptionFloats>(opt_key)->set_at(vec_new, opt_index, opt_index); - break; - } - case coString: - config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast<std::string>(value))); - break; - case coStrings:{ - if (opt_key.compare("compatible_printers") == 0) { - config.option<ConfigOptionStrings>(opt_key)->values = - boost::any_cast<std::vector<std::string>>(value); - } - else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0){ - std::string str = boost::any_cast<std::string>(value); - if (str.back() == ';') str.pop_back(); - // Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values. - // Currently used for the post_process config value only. - std::vector<std::string> values; - boost::split(values, str, boost::is_any_of(";")); - if (values.size() == 1 && values[0] == "") - break; - config.option<ConfigOptionStrings>(opt_key)->values = values; - } - else{ - ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast<std::string>(value) }; - config.option<ConfigOptionStrings>(opt_key)->set_at(vec_new, opt_index, 0); - } - } - break; - case coBool: - config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast<bool>(value))); - break; - case coBools:{ - ConfigOptionBools* vec_new = new ConfigOptionBools{ (bool)boost::any_cast<unsigned char>(value) }; - config.option<ConfigOptionBools>(opt_key)->set_at(vec_new, opt_index, 0); - break;} - case coInt: - config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast<int>(value))); - break; - case coInts:{ - ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast<int>(value) }; - config.option<ConfigOptionInts>(opt_key)->set_at(vec_new, opt_index, 0); - } - break; - case coEnum:{ - if (opt_key.compare("external_fill_pattern") == 0 || - opt_key.compare("fill_pattern") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum<InfillPattern>(boost::any_cast<InfillPattern>(value))); - else if (opt_key.compare("gcode_flavor") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum<GCodeFlavor>(boost::any_cast<GCodeFlavor>(value))); - else if (opt_key.compare("support_material_pattern") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value))); - else if (opt_key.compare("seam_position") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value))); - else if (opt_key.compare("host_type") == 0) - config.set_key_value(opt_key, new ConfigOptionEnum<PrintHostType>(boost::any_cast<PrintHostType>(value))); - } - break; - case coPoints:{ - if (opt_key.compare("bed_shape") == 0){ - config.option<ConfigOptionPoints>(opt_key)->values = boost::any_cast<std::vector<Vec2d>>(value); - break; - } - ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast<Vec2d>(value) }; - config.option<ConfigOptionPoints>(opt_key)->set_at(vec_new, opt_index, 0); - } - break; - case coNone: - break; - default: - break; - } - } - catch (const std::exception &e) - { - int i = 0;//no reason, just experiment - } -} - -void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed) -{ - panel->create_preset_tab(g_PresetBundle); - - // Load the currently selected preset into the GUI, update the preset selection box. - panel->load_current_preset(); - - panel->set_event_value_change(wxEventType(event_value_change)); - panel->set_event_presets_changed(wxEventType(event_presets_changed)); - - const wxString& tab_name = panel->GetName(); - bool add_panel = true; - - auto it = std::find_if( preset_tabs.begin(), preset_tabs.end(), - [tab_name](PresetTab& tab){return tab.name == tab_name; }); - if (it != preset_tabs.end()) { - it->panel = panel; - add_panel = it->technology == g_PresetBundle->printers.get_edited_preset().printer_technology(); - } - - if (add_panel) - g_wxTabPanel->AddPage(panel, panel->title()); -} - -void load_current_presets() -{ - for (Tab *tab : g_tabs_list) { - tab->load_current_preset(); - } -} - -void show_error(wxWindow* parent, const wxString& message) { - ErrorDialog msg(parent, message); - msg.ShowModal(); -} - -void show_error_id(int id, const std::string& message) { - auto *parent = id != 0 ? wxWindow::FindWindowById(id) : nullptr; - show_error(parent, wxString::FromUTF8(message.data())); -} - -void show_info(wxWindow* parent, const wxString& message, const wxString& title){ - wxMessageDialog msg_wingow(parent, message, title.empty() ? _(L("Notice")) : title, wxOK | wxICON_INFORMATION); - msg_wingow.ShowModal(); -} - -void warning_catcher(wxWindow* parent, const wxString& message){ - if (message == "GLUquadricObjPtr | " + _(L("Attempt to free unreferenced scalar")) ) - return; - wxMessageDialog msg(parent, message, _(L("Warning")), wxOK | wxICON_WARNING); - msg.ShowModal(); -} - -// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID -// to deliver a progress status message. -void set_print_callback_event(Print *print, int id) -{ - print->set_status_callback([id](int percent, const std::string &message){ - wxCommandEvent event(id); - event.SetInt(percent); - event.SetString(message); - wxQueueEvent(g_wxMainFrame, event.Clone()); - }); -} - -wxApp* get_app(){ - return g_wxApp; -} - -PresetBundle* get_preset_bundle() -{ - return g_PresetBundle; -} - -const wxColour& get_label_clr_modified() { - return g_color_label_modified; -} - -const wxColour& get_label_clr_sys() { - return g_color_label_sys; -} - -void set_label_clr_modified(const wxColour& clr) { - g_color_label_modified = clr; - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); - std::string str = clr_str.ToStdString(); - g_AppConfig->set("label_clr_modified", str); - g_AppConfig->save(); -} - -void set_label_clr_sys(const wxColour& clr) { - g_color_label_sys = clr; - auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), clr.Red(), clr.Green(), clr.Blue()); - std::string str = clr_str.ToStdString(); - g_AppConfig->set("label_clr_sys", str); - g_AppConfig->save(); -} - -const wxFont& small_font(){ - return g_small_font; -} - -const wxFont& bold_font(){ - return g_bold_font; -} - -const wxColour& get_label_clr_default() { - return g_color_label_default; -} - -unsigned get_colour_approx_luma(const wxColour &colour) -{ - double r = colour.Red(); - double g = colour.Green(); - double b = colour.Blue(); - - return std::round(std::sqrt( - r * r * .241 + - g * g * .691 + - b * b * .068 - )); -} - -wxWindow* get_right_panel(){ - return g_right_panel; -} - -wxNotebook * get_tab_panel() { - return g_wxTabPanel; -} - -const size_t& label_width(){ - return m_label_width; -} - -void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value) -{ - if (comboCtrl == nullptr) - return; - - wxCheckListBoxComboPopup* popup = new wxCheckListBoxComboPopup; - if (popup != nullptr) - { - // FIXME If the following line is removed, the combo box popup list will not react to mouse clicks. - // On the other side, with this line the combo box popup cannot be closed by clicking on the combo button on Windows 10. - comboCtrl->UseAltPopupWindow(); - - comboCtrl->EnablePopupAnimation(false); - comboCtrl->SetPopupControl(popup); - popup->SetStringValue(from_u8(text)); - popup->Bind(wxEVT_CHECKLISTBOX, [popup](wxCommandEvent& evt) { popup->OnCheckListBox(evt); }); - popup->Bind(wxEVT_LISTBOX, [popup](wxCommandEvent& evt) { popup->OnListBoxSelection(evt); }); - popup->Bind(wxEVT_KEY_DOWN, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); - popup->Bind(wxEVT_KEY_UP, [popup](wxKeyEvent& evt) { popup->OnKeyEvent(evt); }); - - std::vector<std::string> items_str; - boost::split(items_str, items, boost::is_any_of("|"), boost::token_compress_off); - - for (const std::string& item : items_str) - { - popup->Append(from_u8(item)); - } - - for (unsigned int i = 0; i < popup->GetCount(); ++i) - { - popup->Check(i, initial_value); - } - } -} - -int combochecklist_get_flags(wxComboCtrl* comboCtrl) -{ - int flags = 0; - - wxCheckListBoxComboPopup* popup = wxDynamicCast(comboCtrl->GetPopupControl(), wxCheckListBoxComboPopup); - if (popup != nullptr) - { - for (unsigned int i = 0; i < popup->GetCount(); ++i) - { - if (popup->IsChecked(i)) - flags |= 1 << i; - } - } - - return flags; -} - -AppConfig* get_app_config() -{ - return g_AppConfig; -} - -wxString L_str(const std::string &str) -{ - //! Explicitly specify that the source string is already in UTF-8 encoding - return wxGetTranslation(wxString(str.c_str(), wxConvUTF8)); -} - -wxString from_u8(const std::string &str) -{ - return wxString::FromUTF8(str.c_str()); -} - -void set_model_events_from_perl(Model &model, - int event_object_selection_changed, - int event_object_settings_changed, - int event_remove_object, - int event_update_scene) -{ - set_event_object_selection_changed(event_object_selection_changed); - set_event_object_settings_changed(event_object_settings_changed); - set_event_remove_object(event_remove_object); - set_event_update_scene(event_update_scene); - set_objects_from_model(model); - init_mesh_icons(); - -// wxWindowUpdateLocker noUpdates(parent); - -// add_objects_list(parent, sizer); - -// add_collapsible_panes(parent, sizer); -} - -void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer) -{ - DynamicPrintConfig* config = &g_PresetBundle->prints.get_edited_preset().config; - std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(parent, "", config); - const wxArrayInt& ar = preset_sizer->GetColWidths(); - m_label_width = ar.IsEmpty() ? 100 : ar.front()-4; - optgroup->label_width = m_label_width; - - //Frequently changed parameters - optgroup->m_on_change = [config](t_config_option_key opt_key, boost::any value){ - TabPrint* tab_print = nullptr; - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { - Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i)); - if (!tab) - continue; - if (tab->name() == "print"){ - tab_print = static_cast<TabPrint*>(tab); - break; - } - } - if (tab_print == nullptr) - return; - - if (opt_key == "fill_density"){ - value = m_optgroups[ogFrequentlyChangingParameters]->get_config_value(*config, opt_key); - tab_print->set_value(opt_key, value); - tab_print->update(); - } - else{ - DynamicPrintConfig new_conf = *config; - if (opt_key == "brim"){ - double new_val; - double brim_width = config->opt_float("brim_width"); - if (boost::any_cast<bool>(value) == true) - { - new_val = m_brim_width == 0.0 ? 10 : - m_brim_width < 0.0 ? m_brim_width * (-1) : - m_brim_width; - } - else{ - m_brim_width = brim_width * (-1); - new_val = 0; - } - new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val)); - } - else{ //(opt_key == "support") - const wxString& selection = boost::any_cast<wxString>(value); - - auto support_material = selection == _("None") ? false : true; - new_conf.set_key_value("support_material", new ConfigOptionBool(support_material)); - - if (selection == _("Everywhere")) - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(false)); - else if (selection == _("Support on build plate only")) - new_conf.set_key_value("support_material_buildplate_only", new ConfigOptionBool(true)); - } - tab_print->load_config(new_conf); - } - - tab_print->update_dirty(); - }; - - Option option = optgroup->get_option("fill_density"); - option.opt.sidetext = ""; - option.opt.full_width = true; - optgroup->append_single_option_line(option); - - ConfigOptionDef def; - - def.label = L("Support"); - def.type = coStrings; - def.gui_type = "select_open"; - def.tooltip = L("Select what kind of support do you need"); - def.enum_labels.push_back(L("None")); - def.enum_labels.push_back(L("Support on build plate only")); - def.enum_labels.push_back(L("Everywhere")); - std::string selection = !config->opt_bool("support_material") ? - "None" : - config->opt_bool("support_material_buildplate_only") ? - "Support on build plate only" : - "Everywhere"; - def.default_value = new ConfigOptionStrings { selection }; - option = Option(def, "support"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - - m_brim_width = config->opt_float("brim_width"); - def.label = L("Brim"); - def.type = coBool; - def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer."); - def.gui_type = ""; - def.default_value = new ConfigOptionBool{ m_brim_width > 0.0 ? true : false }; - option = Option(def, "brim"); - optgroup->append_single_option_line(option); - - - Line line = { "", "" }; - line.widget = [config](wxWindow* parent){ - g_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(g_wiping_dialog_button); - g_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) - { - auto &config = g_PresetBundle->project_config; - const std::vector<double> &init_matrix = (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values; - const std::vector<double> &init_extruders = (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values; - - WipingDialog dlg(parent,cast<float>(init_matrix),cast<float>(init_extruders)); - - if (dlg.ShowModal() == wxID_OK) { - std::vector<float> matrix = dlg.get_matrix(); - std::vector<float> extruders = dlg.get_extruders(); - (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(),matrix.end()); - (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(),extruders.end()); - g_on_request_update_callback.call(); - } - })); - return sizer; - }; - optgroup->append_line(line); - - sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2); - - m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters - - // Object List - add_objects_list(parent, sizer); - - // Frequently Object Settings - add_object_settings(parent, sizer); -} - -void show_frequently_changed_parameters(bool show) -{ - g_frequently_changed_parameters_sizer->Show(show); - if (!show) return; - - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { - Tab *tab = dynamic_cast<Tab*>(g_wxTabPanel->GetPage(i)); - if (!tab) - continue; - tab->update_wiping_button_visibility(); - break; - } -} - -void show_buttons(bool show) -{ - g_buttons[abReslice]->Show(show); - for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) { - TabPrinter *tab = dynamic_cast<TabPrinter*>(g_wxTabPanel->GetPage(i)); - if (!tab) - continue; - if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) { - g_buttons[abPrint]->Show(show && !tab->m_config->opt_string("serial_port").empty()); - g_buttons[abSendGCode]->Show(show && !tab->m_config->opt_string("print_host").empty()); - } - break; - } -} - -void show_info_sizer(const bool show) -{ - g_info_sizer->Show(static_cast<size_t>(0), show); - g_info_sizer->Show(1, show && g_show_print_info); - g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon); -} - -void show_object_name(bool show) -{ - wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer(); - grid_sizer->Show(static_cast<size_t>(0), show); - grid_sizer->Show(static_cast<size_t>(1), show); -} - -void update_mode() -{ - wxWindowUpdateLocker noUpdates(g_right_panel->GetParent()); - - ConfigMenuIDs mode = get_view_mode(); - - g_object_list_sizer->Show(mode == ConfigMenuModeExpert); - show_info_sizer(mode == ConfigMenuModeExpert); - show_buttons(mode == ConfigMenuModeExpert); - show_object_name(mode == ConfigMenuModeSimple); - show_manipulation_sizer(mode == ConfigMenuModeSimple); - - // TODO There is a not the best place of it! - // *** Update showing of the collpane_settings -// show_collpane_settings(mode == ConfigMenuModeExpert); - // ************************* - g_right_panel->Layout(); - g_right_panel->GetParent()->Layout(); -} - -bool is_expert_mode(){ - return get_view_mode() == ConfigMenuModeExpert; -} - -ConfigOptionsGroup* get_optgroup(size_t i) -{ - return m_optgroups[i].get(); -} - -std::vector <std::shared_ptr<ConfigOptionsGroup>>& get_optgroups() { - return m_optgroups; -} - -wxButton* get_wiping_dialog_button() -{ - return g_wiping_dialog_button; -} - -wxWindow* export_option_creator(wxWindow* parent) -{ - wxPanel* panel = new wxPanel(parent, -1); - wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL); - wxCheckBox* cbox = new wxCheckBox(panel, wxID_HIGHEST + 1, L("Export print config")); - cbox->SetValue(true); - sizer->AddSpacer(5); - sizer->Add(cbox, 0, wxEXPAND | wxALL | wxALIGN_CENTER_VERTICAL, 5); - panel->SetSizer(sizer); - sizer->SetSizeHints(panel); - return panel; -} - -void add_export_option(wxFileDialog* dlg, const std::string& format) -{ - if ((dlg != nullptr) && (format == "AMF") || (format == "3MF")) - { - if (dlg->SupportsExtraControl()) - dlg->SetExtraControlCreator(export_option_creator); - } -} - -int get_export_option(wxFileDialog* dlg) -{ - if (dlg != nullptr) - { - wxWindow* wnd = dlg->GetExtraControl(); - if (wnd != nullptr) - { - wxPanel* panel = dynamic_cast<wxPanel*>(wnd); - if (panel != nullptr) - { - wxWindow* child = panel->FindWindow(wxID_HIGHEST + 1); - if (child != nullptr) - { - wxCheckBox* cbox = dynamic_cast<wxCheckBox*>(child); - if (cbox != nullptr) - return cbox->IsChecked() ? 1 : 0; - } - } - } - } - - return 0; - -} - -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) -{ - const auto idx = wxDisplay::GetFromWindow(window); - if (idx == wxNOT_FOUND) { - return false; - } - - wxDisplay display(idx); - const auto disp_size = display.GetClientArea(); - width = disp_size.GetWidth(); - height = disp_size.GetHeight(); - - return true; -} - -void save_window_size(wxTopLevelWindow *window, const std::string &name) -{ - const wxSize size = window->GetSize(); - const wxPoint pos = window->GetPosition(); - const auto maximized = window->IsMaximized() ? "1" : "0"; - - g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str()); - g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized); -} - -void restore_window_size(wxTopLevelWindow *window, const std::string &name) -{ - // XXX: This still doesn't behave nicely in some situations (mostly on Linux). - // The problem is that it's hard to obtain window position with respect to screen geometry reliably - // from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which - // it's actually visible. I suspect this has something to do with window initialization (maybe we - // restore window geometry too early), but haven't yet found a workaround. - - const auto display_idx = wxDisplay::GetFromWindow(window); - if (display_idx == wxNOT_FOUND) { return; } - - const auto display = wxDisplay(display_idx).GetClientArea(); - std::vector<std::string> pair; - - try { - const auto key_size = (boost::format("window_%1%_size") % name).str(); - if (g_AppConfig->has(key_size)) { - if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) { - auto width = boost::lexical_cast<int>(pair[0]); - auto height = boost::lexical_cast<int>(pair[1]); - - window->SetSize(width, height); - } - } - } catch(const boost::bad_lexical_cast &) {} - - // Maximizing should be the last thing to do. - // This ensure the size and position are sane when the user un-maximizes the window. - const auto key_maximized = (boost::format("window_%1%_maximized") % name).str(); - if (g_AppConfig->get(key_maximized) == "1") { - window->Maximize(true); - } -} - -void enable_action_buttons(bool enable) -{ - if (g_buttons.empty()) - return; - - // Update background colour for buttons - const wxColour bgrd_color = enable ? wxColour(224, 224, 224/*255, 96, 0*/) : wxColour(204, 204, 204); - - for (auto btn : g_buttons) { - btn->Enable(enable); - btn->SetBackgroundColour(bgrd_color); - } -} - -void about() -{ - AboutDialog dlg; - dlg.ShowModal(); - dlg.Destroy(); -} - -void desktop_open_datadir_folder() -{ - // Execute command to open a file explorer, platform dependent. - // FIXME: The const_casts aren't needed in wxWidgets 3.1, remove them when we upgrade. - - const auto path = data_dir(); -#ifdef _WIN32 - const auto widepath = wxString::FromUTF8(path.data()); - const wchar_t *argv[] = { L"explorer", widepath.GetData(), nullptr }; - ::wxExecute(const_cast<wchar_t**>(argv), wxEXEC_ASYNC, nullptr); -#elif __APPLE__ - const char *argv[] = { "open", path.data(), nullptr }; - ::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr); -#else - const char *argv[] = { "xdg-open", path.data(), nullptr }; - - // Check if we're running in an AppImage container, if so, we need to remove AppImage's env vars, - // because they may mess up the environment expected by the file manager. - // Mostly this is about LD_LIBRARY_PATH, but we remove a few more too for good measure. - if (wxGetEnv("APPIMAGE", nullptr)) { - // We're running from AppImage - wxEnvVariableHashMap env_vars; - wxGetEnvMap(&env_vars); - - env_vars.erase("APPIMAGE"); - env_vars.erase("APPDIR"); - env_vars.erase("LD_LIBRARY_PATH"); - env_vars.erase("LD_PRELOAD"); - env_vars.erase("UNION_PRELOAD"); - - wxExecuteEnv exec_env; - exec_env.env = std::move(env_vars); - - wxString owd; - if (wxGetEnv("OWD", &owd)) { - // This is the original work directory from which the AppImage image was run, - // set it as CWD for the child process: - exec_env.cwd = std::move(owd); - } - - ::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, &exec_env); - } else { - // Looks like we're NOT running from AppImage, we'll make no changes to the environment. - ::wxExecute(const_cast<char**>(argv), wxEXEC_ASYNC, nullptr, nullptr); - } -#endif -} - -} } diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp deleted file mode 100644 index 998b572b9..000000000 --- a/xs/src/slic3r/GUI/GUI.hpp +++ /dev/null @@ -1,255 +0,0 @@ -#ifndef slic3r_GUI_hpp_ -#define slic3r_GUI_hpp_ - -#include <string> -#include <vector> -#include "PrintConfig.hpp" -#include "../../callback.hpp" -#include "GUI_ObjectParts.hpp" - -#include <wx/intl.h> -#include <wx/string.h> - -class wxApp; -class wxWindow; -class wxFrame; -class wxMenuBar; -class wxNotebook; -class wxPanel; -class wxComboCtrl; -class wxString; -class wxArrayString; -class wxArrayLong; -class wxColour; -class wxBoxSizer; -class wxFlexGridSizer; -class wxButton; -class wxFileDialog; -class wxStaticBitmap; -class wxFont; -class wxTopLevelWindow; - -namespace Slic3r { - -class PresetBundle; -class PresetCollection; -class Print; -class ProgressStatusBar; -class AppConfig; -class PresetUpdater; -class DynamicPrintConfig; -class TabIface; - -#define _(s) Slic3r::GUI::I18N::translate((s)) - -namespace GUI { namespace I18N { - inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); } - inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); } - inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); } - inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); } -} } - -// !!! If you needed to translate some wxString, -// !!! please use _(L(string)) -// !!! _() - is a standard wxWidgets macro to translate -// !!! L() is used only for marking localizable string -// !!! It will be used in "xgettext" to create a Locating Message Catalog. -#define L(s) s - -//! macro used to localization, return wxScopedCharBuffer -//! With wxConvUTF8 explicitly specify that the source string is already in UTF-8 encoding -#define _CHB(s) wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str() - -// Minimal buffer length for translated string (char buf[MIN_BUF_LENGTH_FOR_L]) -#define MIN_BUF_LENGTH_FOR_L 512 - -namespace GUI { - -class Tab; -class ConfigOptionsGroup; -// Map from an file_type name to full file wildcard name. -typedef std::map<std::string, std::string> t_file_wild_card; -inline t_file_wild_card& get_file_wild_card() { - static t_file_wild_card FILE_WILDCARDS; - if (FILE_WILDCARDS.empty()){ - FILE_WILDCARDS["known"] = "Known files (*.stl, *.obj, *.amf, *.xml, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.prusa;*.PRUSA"; - FILE_WILDCARDS["stl"] = "STL files (*.stl)|*.stl;*.STL"; - FILE_WILDCARDS["obj"] = "OBJ files (*.obj)|*.obj;*.OBJ"; - FILE_WILDCARDS["amf"] = "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML"; - FILE_WILDCARDS["3mf"] = "3MF files (*.3mf)|*.3mf;*.3MF;"; - FILE_WILDCARDS["prusa"] = "Prusa Control files (*.prusa)|*.prusa;*.PRUSA"; - FILE_WILDCARDS["ini"] = "INI files *.ini|*.ini;*.INI"; - FILE_WILDCARDS["gcode"] = "G-code files (*.gcode, *.gco, *.g, *.ngc)|*.gcode;*.GCODE;*.gco;*.GCO;*.g;*.G;*.ngc;*.NGC"; - FILE_WILDCARDS["svg"] = "SVG files *.svg|*.svg;*.SVG"; - } - return FILE_WILDCARDS; -} - -struct PresetTab { - std::string name; - Tab* panel; - PrinterTechnology technology; -}; - - -void disable_screensaver(); -void enable_screensaver(); -bool debugged(); -void break_to_debugger(); - -// Passing the wxWidgets GUI classes instantiated by the Perl part to C++. -void set_wxapp(wxApp *app); -void set_main_frame(wxFrame *main_frame); -void set_progress_status_bar(ProgressStatusBar *prsb); -void set_tab_panel(wxNotebook *tab_panel); -void set_plater(wxPanel *plater); -void set_app_config(AppConfig *app_config); -void set_preset_bundle(PresetBundle *preset_bundle); -void set_preset_updater(PresetUpdater *updater); -void set_objects_from_perl( wxWindow* parent, - wxBoxSizer *frequently_changed_parameters_sizer, - wxBoxSizer *info_sizer, - wxButton *btn_export_gcode, - wxButton *btn_reslice, - wxButton *btn_print, - wxButton *btn_send_gcode, - wxStaticBitmap *manifold_warning_icon); -void set_show_print_info(bool show); -void set_show_manifold_warning_icon(bool show); -void set_objects_list_sizer(wxBoxSizer *objects_list_sizer); - -AppConfig* get_app_config(); -wxApp* get_app(); -PresetBundle* get_preset_bundle(); -wxFrame* get_main_frame(); -ProgressStatusBar* get_progress_status_bar(); -wxNotebook * get_tab_panel(); -wxNotebook* get_tab_panel(); - -const wxColour& get_label_clr_modified(); -const wxColour& get_label_clr_sys(); -const wxColour& get_label_clr_default(); -unsigned get_colour_approx_luma(const wxColour &colour); -void set_label_clr_modified(const wxColour& clr); -void set_label_clr_sys(const wxColour& clr); - -const wxFont& small_font(); -const wxFont& bold_font(); - -void open_model(wxWindow *parent, wxArrayString& input_files); - -wxWindow* get_right_panel(); -const size_t& label_width(); - -Tab* get_tab(const std::string& name); -const std::vector<PresetTab>& get_preset_tabs(); - -extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); - -// This is called when closing the application, when loading a config file or when starting the config wizard -// to notify the user whether he is aware that some preset changes will be lost. -extern bool check_unsaved_changes(); - -// Checks if configuration wizard needs to run, calls config_wizard if so. -// Returns whether the Wizard ran. -extern bool config_wizard_startup(bool app_config_exists); - -// Opens the configuration wizard, returns true if wizard is finished & accepted. -// The run_reason argument is actually ConfigWizard::RunReason, but int is used here because of Perl. -extern void config_wizard(int run_reason); - -// Create "Preferences" dialog after selecting menu "Preferences" in Perl part -extern void open_preferences_dialog(int event_preferences); - -// Create a new preset tab (print, filament and printer), -void create_preset_tabs(int event_value_change, int event_presets_changed); -TabIface* get_preset_tab_iface(char *name); - -// add it at the end of the tab panel. -void add_created_tab(Tab* panel, int event_value_change, int event_presets_changed); -// Change option value in config -void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); - -// Update UI / Tabs to reflect changes in the currently loaded presets -void load_current_presets(); - -void show_error(wxWindow* parent, const wxString& message); -void show_error_id(int id, const std::string& message); // For Perl -void show_info(wxWindow* parent, const wxString& message, const wxString& title); -void warning_catcher(wxWindow* parent, const wxString& message); - -// Assign a Lambda to the print object to emit a wxWidgets Command with the provided ID -// to deliver a progress status message. -void set_print_callback_event(Print *print, int id); - -// load language saved at application config -bool load_language(); -// save language at application config -void save_language(); -// get list of installed languages -void get_installed_languages(wxArrayString & names, wxArrayLong & identifiers); -// select language from the list of installed languages -bool select_language(wxArrayString & names, wxArrayLong & identifiers); -// update right panel of the Plater according to view mode -void update_mode(); - -void show_info_sizer(const bool show); - -std::vector<Tab *>& get_tabs_list(); -bool checked_tab(Tab* tab); -void delete_tab_from_list(Tab* tab); - -// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items. -// Items are all initialized to the given value. -// Items must be separated by '|', for example "Item1|Item2|Item3", and so on. -void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string items, bool initial_value); - -// Returns the current state of the items listed in the wxCheckListBoxComboPopup contained in the given wxComboCtrl, -// encoded inside an int. -int combochecklist_get_flags(wxComboCtrl* comboCtrl); - -// Return translated std::string as a wxString -wxString L_str(const std::string &str); -// Return wxString from std::string in UTF8 -wxString from_u8(const std::string &str); - -void set_model_events_from_perl(Model &model, - int event_object_selection_changed, - int event_object_settings_changed, - int event_remove_object, - int event_update_scene); -void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer); -// Update view mode according to selected menu -void update_mode(); -bool is_expert_mode(); - -// Callback to trigger a configuration update timer on the Plater. -static PerlCallback g_on_request_update_callback; - -ConfigOptionsGroup* get_optgroup(size_t i); -std::vector <std::shared_ptr<ConfigOptionsGroup>>& get_optgroups(); -wxButton* get_wiping_dialog_button(); - -void add_export_option(wxFileDialog* dlg, const std::string& format); -int get_export_option(wxFileDialog* dlg); - -// Returns the dimensions of the screen on which the main frame is displayed -bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); - -// Save window size and maximized status into AppConfig -void save_window_size(wxTopLevelWindow *window, const std::string &name); -// Restore the above -void restore_window_size(wxTopLevelWindow *window, const std::string &name); - -// Update buttons view according to enable/disable -void enable_action_buttons(bool enable); - -// Display an About dialog -extern void about(); -// Ask the destop to open the datadir using the default file explorer. -extern void desktop_open_datadir_folder(); - -} // namespace GUI -} // namespace Slic3r - -#endif diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp deleted file mode 100644 index ae34359ce..000000000 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp +++ /dev/null @@ -1,2041 +0,0 @@ -#include "GUI.hpp" -#include "OptionsGroup.hpp" -#include "PresetBundle.hpp" -#include "GUI_ObjectParts.hpp" -#include "Model.hpp" -#include "wxExtensions.hpp" -#include "LambdaObjectDialog.hpp" -#include "../../libslic3r/Utils.hpp" - -#include <wx/msgdlg.h> -#include <boost/filesystem.hpp> -#include <boost/algorithm/string.hpp> -#include "Geometry.hpp" -#include "slic3r/Utils/FixModelByWin10.hpp" - -#include <wx/glcanvas.h> -#include "3DScene.hpp" - -namespace Slic3r -{ -namespace GUI -{ -wxSizer *m_sizer_object_buttons = nullptr; -wxSizer *m_sizer_part_buttons = nullptr; -wxSizer *m_sizer_object_movers = nullptr; -wxDataViewCtrl *m_objects_ctrl = nullptr; -PrusaObjectDataViewModel *m_objects_model = nullptr; -wxCollapsiblePane *m_collpane_settings = nullptr; -PrusaDoubleSlider *m_slider = nullptr; -wxGLCanvas *m_preview_canvas = nullptr; - -wxBitmap m_icon_modifiermesh; -wxBitmap m_icon_solidmesh; -wxBitmap m_icon_manifold_warning; -wxBitmap m_bmp_cog; -wxBitmap m_bmp_split; - -wxSlider* m_mover_x = nullptr; -wxSlider* m_mover_y = nullptr; -wxSlider* m_mover_z = nullptr; -wxButton* m_btn_move_up = nullptr; -wxButton* m_btn_move_down = nullptr; -Vec3d m_move_options; -Vec3d m_last_coords; -int m_selected_object_id = -1; - -bool g_prevent_list_events = false; // We use this flag to avoid circular event handling Select() - // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler - // calls this method again and again and again -bool g_is_percent_scale = false; // It indicates if scale unit is percentage -bool g_is_uniform_scale = false; // It indicates if scale is uniform -ModelObjectPtrs* m_objects; -std::shared_ptr<DynamicPrintConfig*> m_config; -std::shared_ptr<DynamicPrintConfig> m_default_config; -wxBoxSizer* m_option_sizer = nullptr; - -// option groups for settings -std::vector <std::shared_ptr<ConfigOptionsGroup>> m_og_settings; - -int m_event_object_selection_changed = 0; -int m_event_object_settings_changed = 0; -int m_event_remove_object = 0; -int m_event_update_scene = 0; - -bool m_parts_changed = false; -bool m_part_settings_changed = false; - -#ifdef __WXOSX__ - wxString g_selected_extruder = ""; -#endif //__WXOSX__ - -inline t_category_icon& get_category_icon() { - static t_category_icon CATEGORY_ICON; - if (CATEGORY_ICON.empty()){ - CATEGORY_ICON[L("Layers and Perimeters")] = wxBitmap(from_u8(Slic3r::var("layers.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Infill")] = wxBitmap(from_u8(Slic3r::var("infill.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Support material")] = wxBitmap(from_u8(Slic3r::var("building.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Speed")] = wxBitmap(from_u8(Slic3r::var("time.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Extruders")] = wxBitmap(from_u8(Slic3r::var("funnel.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Extrusion Width")] = wxBitmap(from_u8(Slic3r::var("funnel.png")), wxBITMAP_TYPE_PNG); -// CATEGORY_ICON[L("Skirt and brim")] = wxBitmap(from_u8(Slic3r::var("box.png")), wxBITMAP_TYPE_PNG); -// CATEGORY_ICON[L("Speed > Acceleration")] = wxBitmap(from_u8(Slic3r::var("time.png")), wxBITMAP_TYPE_PNG); - CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(Slic3r::var("wand.png")), wxBITMAP_TYPE_PNG); - } - return CATEGORY_ICON; -} - -std::vector<std::string> get_options(const bool is_part) -{ - PrintRegionConfig reg_config; - auto options = reg_config.keys(); - if (!is_part) { - PrintObjectConfig obj_config; - std::vector<std::string> obj_options = obj_config.keys(); - options.insert(options.end(), obj_options.begin(), obj_options.end()); - } - return options; -} - -// category -> vector ( option ; label ) -typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy; -void get_options_menu(settings_menu_hierarchy& settings_menu, bool is_part) -{ - auto options = get_options(is_part); - - auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : - get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size(); - - DynamicPrintConfig config; - for (auto& option : options) - { - auto const opt = config.def()->get(option); - auto category = opt->category; - if (category.empty() || - (category == "Extruders" && extruders_cnt == 1)) continue; - - std::pair<std::string, std::string> option_label(option, opt->label); - std::vector< std::pair<std::string, std::string> > new_category; - auto& cat_opt_label = settings_menu.find(category) == settings_menu.end() ? new_category : settings_menu.at(category); - cat_opt_label.push_back(option_label); - if (cat_opt_label.size() == 1) - settings_menu[category] = cat_opt_label; - } -} - -void set_event_object_selection_changed(const int& event){ - m_event_object_selection_changed = event; -} -void set_event_object_settings_changed(const int& event){ - m_event_object_settings_changed = event; -} -void set_event_remove_object(const int& event){ - m_event_remove_object = event; -} -void set_event_update_scene(const int& event){ - m_event_update_scene = event; -} - -void set_objects_from_model(Model &model) { - m_objects = &(model.objects); -} - -void init_mesh_icons(){ - m_icon_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG); - m_icon_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG); - - // init icon for manifold warning - m_icon_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG); - - // init bitmap for "Split to sub-objects" context menu - m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG); - - // init bitmap for "Add Settings" context menu - m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); -} - -bool is_parts_changed(){return m_parts_changed;} -bool is_part_settings_changed(){ return m_part_settings_changed; } - -static wxString dots("…", wxConvUTF8); - -void set_tooltip_for_item(const wxPoint& pt) -{ - wxDataViewItem item; - wxDataViewColumn* col; - m_objects_ctrl->HitTest(pt, item, col); - if (!item) return; - - if (col->GetTitle() == " ") - m_objects_ctrl->GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings"))); - else if (col->GetTitle() == _("Name") && - m_objects_model->GetIcon(item).GetRefData() == m_icon_manifold_warning.GetRefData()) { - int obj_idx = m_objects_model->GetIdByItem(item); - auto& stats = (*m_objects)[obj_idx]->volumes[0]->mesh.stl.stats; - int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + - stats.facets_added + stats.facets_reversed + stats.backwards_edges; - - wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors); - - std::map<std::string, int> error_msg; - error_msg[L("degenerate facets")] = stats.degenerate_facets; - error_msg[L("edges fixed")] = stats.edges_fixed; - error_msg[L("facets removed")] = stats.facets_removed; - error_msg[L("facets added")] = stats.facets_added; - error_msg[L("facets reversed")] = stats.facets_reversed; - error_msg[L("backwards edges")] = stats.backwards_edges; - - for (auto error : error_msg) - { - if (error.second > 0) - tooltip += wxString::Format(_("\t%d %s\n"), error.second, error.first); - } -// OR -// tooltip += wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, " -// "%d facets added, %d facets reversed, %d backwards edges")), -// stats.degenerate_facets, stats.edges_fixed, stats.facets_removed, -// stats.facets_added, stats.facets_reversed, stats.backwards_edges); - - if (is_windows10()) - tooltip += _(L("Right button click the icon to fix STL through Netfabb")); - - m_objects_ctrl->GetMainWindow()->SetToolTip(tooltip); - } - else - m_objects_ctrl->GetMainWindow()->SetToolTip(""); // hide tooltip -} - -wxPoint get_mouse_position_in_control() { - const wxPoint& pt = wxGetMousePosition(); - wxWindow* win = m_objects_ctrl->GetMainWindow(); - return wxPoint(pt.x - win->GetScreenPosition().x, - pt.y - win->GetScreenPosition().y); -} - -bool is_mouse_position_in_control(wxPoint& pt) { - pt = get_mouse_position_in_control(); - const wxSize& cz = m_objects_ctrl->GetSize(); - if (pt.x > 0 && pt.x < cz.x && - pt.y > 0 && pt.y < cz.y) - return true; - return false; -} - -wxDataViewColumn* object_ctrl_create_extruder_column(int extruders_count) -{ - wxArrayString choices; - choices.Add("default"); - for (int i = 1; i <= extruders_count; ++i) - choices.Add(wxString::Format("%d", i)); - wxDataViewChoiceRenderer *c = - new wxDataViewChoiceRenderer(choices, wxDATAVIEW_CELL_EDITABLE, wxALIGN_CENTER_HORIZONTAL); - wxDataViewColumn* column = new wxDataViewColumn(_(L("Extruder")), c, 2, 60, wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); - return column; -} - -void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz) -{ - m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize); - m_objects_ctrl->SetMinSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects - - objects_sz = new wxBoxSizer(wxVERTICAL); - objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20); - - m_objects_model = new PrusaObjectDataViewModel; - m_objects_ctrl->AssociateModel(m_objects_model); -#if wxUSE_DRAG_AND_DROP && wxUSE_UNICODE - m_objects_ctrl->EnableDragSource(wxDF_UNICODETEXT); - m_objects_ctrl->EnableDropTarget(wxDF_UNICODETEXT); -#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE - - // column 0(Icon+Text) of the view control: - // And Icon can be consisting of several bitmaps - m_objects_ctrl->AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(), - 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE)); - - // column 1 of the view control: - m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45, - wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); - - // column 2 of the view control: - m_objects_ctrl->AppendColumn(object_ctrl_create_extruder_column(4)); - - // column 3 of the view control: - m_objects_ctrl->AppendBitmapColumn(" ", 3, wxDATAVIEW_CELL_INERT, 25, - wxALIGN_CENTER_HORIZONTAL, wxDATAVIEW_COL_RESIZABLE); -} - -// ****** from GUI.cpp -wxBoxSizer* create_objects_list(wxWindow *win) -{ - wxBoxSizer* objects_sz; - // create control - create_objects_ctrl(win, objects_sz); - - // describe control behavior - m_objects_ctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [](wxEvent& event) { - object_ctrl_selection_changed(); -#ifndef __WXMSW__ - set_tooltip_for_item(get_mouse_position_in_control()); -#endif //__WXMSW__ - }); - - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) { - object_ctrl_context_menu(); -// event.Skip(); - }); - - m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX - -#ifdef __WXMSW__ - // Extruder value changed - m_objects_ctrl->Bind(wxEVT_CHOICE, [](wxCommandEvent& event) { update_extruder_in_config(event.GetString()); }); - - m_objects_ctrl->GetMainWindow()->Bind(wxEVT_MOTION, [](wxMouseEvent& event) { - set_tooltip_for_item(event.GetPosition()); - event.Skip(); - }); -#else - // equivalent to wxEVT_CHOICE on __WXMSW__ - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, [](wxDataViewEvent& event) { object_ctrl_item_value_change(event); }); -#endif //__WXMSW__ - - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_BEGIN_DRAG, [](wxDataViewEvent& e) {on_begin_drag(e);}); - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_DROP_POSSIBLE, [](wxDataViewEvent& e) {on_drop_possible(e); }); - m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_DROP, [](wxDataViewEvent& e) {on_drop(e);}); - return objects_sz; -} - -wxBoxSizer* create_edit_object_buttons(wxWindow* win) -{ - auto sizer = new wxBoxSizer(wxVERTICAL); - - auto btn_load_part = new wxButton(win, wxID_ANY, /*Load */"part" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_load_modifier = new wxButton(win, wxID_ANY, /*Load */"modifier" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_load_lambda_modifier = new wxButton(win, wxID_ANY, /*Load */"generic" + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_delete = new wxButton(win, wxID_ANY, "Delete"/*" part"*/, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - auto btn_split = new wxButton(win, wxID_ANY, "Split"/*" part"*/, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER/*wxBU_LEFT*/); - m_btn_move_up = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize/*wxSize(30, -1)*/, wxBU_LEFT); - m_btn_move_down = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize/*wxSize(30, -1)*/, wxBU_LEFT); - - //*** button's functions - btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) { -// on_btn_load(win); - }); - - btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) { -// on_btn_load(win, true); - }); - - btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) { -// on_btn_load(win, true, true); - }); - - btn_delete ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); }); - btn_split ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_split(true); }); - m_btn_move_up ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_move_up(); }); - m_btn_move_down ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_move_down(); }); - //*** - - m_btn_move_up->SetMinSize(wxSize(20, -1)); - m_btn_move_down->SetMinSize(wxSize(20, -1)); - btn_load_part->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG)); - btn_load_modifier->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG)); - btn_load_lambda_modifier->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_add.png")), wxBITMAP_TYPE_PNG)); - btn_delete->SetBitmap(wxBitmap(from_u8(Slic3r::var("brick_delete.png")), wxBITMAP_TYPE_PNG)); - btn_split->SetBitmap(wxBitmap(from_u8(Slic3r::var("shape_ungroup.png")), wxBITMAP_TYPE_PNG)); - m_btn_move_up->SetBitmap(wxBitmap(from_u8(Slic3r::var("bullet_arrow_up.png")), wxBITMAP_TYPE_PNG)); - m_btn_move_down->SetBitmap(wxBitmap(from_u8(Slic3r::var("bullet_arrow_down.png")), wxBITMAP_TYPE_PNG)); - - m_sizer_object_buttons = new wxGridSizer(1, 3, 0, 0); - m_sizer_object_buttons->Add(btn_load_part, 0, wxEXPAND); - m_sizer_object_buttons->Add(btn_load_modifier, 0, wxEXPAND); - m_sizer_object_buttons->Add(btn_load_lambda_modifier, 0, wxEXPAND); - m_sizer_object_buttons->Show(false); - - m_sizer_part_buttons = new wxGridSizer(1, 3, 0, 0); - m_sizer_part_buttons->Add(btn_delete, 0, wxEXPAND); - m_sizer_part_buttons->Add(btn_split, 0, wxEXPAND); - { - auto up_down_sizer = new wxGridSizer(1, 2, 0, 0); - up_down_sizer->Add(m_btn_move_up, 1, wxEXPAND); - up_down_sizer->Add(m_btn_move_down, 1, wxEXPAND); - m_sizer_part_buttons->Add(up_down_sizer, 0, wxEXPAND); - } - m_sizer_part_buttons->Show(false); - - btn_load_part->SetFont(Slic3r::GUI::small_font()); - btn_load_modifier->SetFont(Slic3r::GUI::small_font()); - btn_load_lambda_modifier->SetFont(Slic3r::GUI::small_font()); - btn_delete->SetFont(Slic3r::GUI::small_font()); - btn_split->SetFont(Slic3r::GUI::small_font()); - m_btn_move_up->SetFont(Slic3r::GUI::small_font()); - m_btn_move_down->SetFont(Slic3r::GUI::small_font()); - - sizer->Add(m_sizer_object_buttons, 0, wxEXPAND | wxLEFT, 20); - sizer->Add(m_sizer_part_buttons, 0, wxEXPAND | wxLEFT, 20); - return sizer; -} - -void update_after_moving() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item || m_selected_object_id<0) - return; - - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) - return; - - auto d = m_move_options - m_last_coords; - auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - volume->mesh.translate(d(0), d(1), d(2)); - m_last_coords = m_move_options; - - m_parts_changed = true; - parts_changed(m_selected_object_id); -} - -wxSizer* object_movers(wxWindow *win) -{ -// DynamicPrintConfig* config = &get_preset_bundle()->/*full_config();//*/printers.get_edited_preset().config; // TODO get config from Model_volume - std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(win, "Move"/*, config*/); - optgroup->label_width = 20; - optgroup->m_on_change = [](t_config_option_key opt_key, boost::any value){ - int val = boost::any_cast<int>(value); - bool update = false; - if (opt_key == "x" && m_move_options(0) != val){ - update = true; - m_move_options(0) = val; - } - else if (opt_key == "y" && m_move_options(1) != val){ - update = true; - m_move_options(1) = val; - } - else if (opt_key == "z" && m_move_options(2) != val){ - update = true; - m_move_options(2) = val; - } - if (update) update_after_moving(); - }; - - ConfigOptionDef def; - def.label = L("X"); - def.type = coInt; - def.gui_type = "slider"; - def.default_value = new ConfigOptionInt(0); - - Option option = Option(def, "x"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - m_mover_x = dynamic_cast<wxSlider*>(optgroup->get_field("x")->getWindow()); - - def.label = L("Y"); - option = Option(def, "y"); - optgroup->append_single_option_line(option); - m_mover_y = dynamic_cast<wxSlider*>(optgroup->get_field("y")->getWindow()); - - def.label = L("Z"); - option = Option(def, "z"); - optgroup->append_single_option_line(option); - m_mover_z = dynamic_cast<wxSlider*>(optgroup->get_field("z")->getWindow()); - - get_optgroups().push_back(optgroup); // ogObjectMovers - - m_sizer_object_movers = optgroup->sizer; - m_sizer_object_movers->Show(false); - - m_move_options = Vec3d(0, 0, 0); - m_last_coords = Vec3d(0, 0, 0); - - return optgroup->sizer; -} - -wxBoxSizer* content_settings(wxWindow *win) -{ - DynamicPrintConfig* config = &get_preset_bundle()->/*full_config();//*/printers.get_edited_preset().config; // TODO get config from Model_volume - std::shared_ptr<ConfigOptionsGroup> optgroup = std::make_shared<ConfigOptionsGroup>(win, "Extruders", config); - optgroup->label_width = label_width(); - - Option option = optgroup->get_option("extruder"); - option.opt.default_value = new ConfigOptionInt(1); - optgroup->append_single_option_line(option); - - get_optgroups().push_back(optgroup); // ogObjectSettings - - auto sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(create_edit_object_buttons(win), 0, wxEXPAND, 0); // *** Edit Object Buttons*** - - sizer->Add(optgroup->sizer, 1, wxEXPAND | wxLEFT, 20); - - auto add_btn = new wxButton(win, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - if (wxMSW) add_btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - add_btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("add.png")), wxBITMAP_TYPE_PNG)); - sizer->Add(add_btn, 0, wxALIGN_LEFT | wxLEFT, 20); - - sizer->Add(object_movers(win), 0, wxEXPAND | wxLEFT, 20); - - return sizer; -} - -void add_objects_list(wxWindow* parent, wxBoxSizer* sizer) -{ - const auto ol_sizer = create_objects_list(parent); - sizer->Add(ol_sizer, 1, wxEXPAND | wxTOP, 20); - set_objects_list_sizer(ol_sizer); -} - -Line add_og_to_object_settings(const std::string& option_name, const std::string& sidetext, int def_value = 0) -{ - Line line = { _(option_name), "" }; - if (option_name == "Scale") { - line.near_label_widget = [](wxWindow* parent) { - auto btn = new PrusaLockButton(parent, wxID_ANY); - btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event){ - event.Skip(); - wxTheApp->CallAfter([btn]() { set_uniform_scaling(btn->IsLocked()); }); - }); - return btn; - }; - } - - ConfigOptionDef def; - def.type = coInt; - def.default_value = new ConfigOptionInt(def_value); - def.width = 55; - - if (option_name == "Rotation") - def.min = -360; - - const std::string lower_name = boost::algorithm::to_lower_copy(option_name); - - std::vector<std::string> axes{ "x", "y", "z" }; - for (auto axis : axes) { - if (axis == "z" && option_name != "Scale") - def.sidetext = sidetext; - Option option = Option(def, lower_name + "_" + axis); - option.opt.full_width = true; - line.append_option(option); - } - - if (option_name == "Scale") - { - def.width = 45; - def.type = coStrings; - def.gui_type = "select_open"; - def.enum_labels.push_back(L("%")); - def.enum_labels.push_back(L("mm")); - def.default_value = new ConfigOptionStrings{ "mm" }; - - const Option option = Option(def, lower_name + "_unit"); - line.append_option(option); - } - - return line; -} - -void add_object_settings(wxWindow* parent, wxBoxSizer* sizer) -{ - auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, _(L("Object Settings"))); - optgroup->label_width = 100; - optgroup->set_grid_vgap(5); - - optgroup->m_on_change = [](t_config_option_key opt_key, boost::any value){ - if (opt_key == "scale_unit"){ - const wxString& selection = boost::any_cast<wxString>(value); - std::vector<std::string> axes{ "x", "y", "z" }; - for (auto axis : axes) { - std::string key = "scale_" + axis; - get_optgroup(ogFrequentlyObjectSettings)->set_side_text(key, selection); - } - - g_is_percent_scale = selection == _("%"); - update_scale_values(); - } - }; - - ConfigOptionDef def; - - // Objects(sub-objects) name - def.label = L("Name"); -// def.type = coString; - def.gui_type = "legend"; - def.tooltip = L("Object name"); - def.full_width = true; - def.default_value = new ConfigOptionString{ " " }; - optgroup->append_single_option_line(Option(def, "object_name")); - - - // Legend for object modification - auto line = Line{ "", "" }; - def.label = ""; - def.type = coString; - def.width = 55; - - std::vector<std::string> axes{ "x", "y", "z" }; - for (const auto axis : axes) { - const auto label = boost::algorithm::to_upper_copy(axis); - def.default_value = new ConfigOptionString{ " "+label }; - Option option = Option(def, axis + "_axis_legend"); - line.append_option(option); - } - optgroup->append_line(line); - - - // Settings table - optgroup->append_line(add_og_to_object_settings(L("Position"), L("mm"))); - optgroup->append_line(add_og_to_object_settings(L("Rotation"), "°")); - optgroup->append_line(add_og_to_object_settings(L("Scale"), "mm")); - - - def.label = L("Place on bed"); - def.type = coBool; - def.tooltip = L("Automatic placing of models on printing bed in Y axis"); - def.gui_type = ""; - def.sidetext = ""; - def.default_value = new ConfigOptionBool{ false }; - optgroup->append_single_option_line(Option(def, "place_on_bed")); - - m_option_sizer = new wxBoxSizer(wxVERTICAL); - optgroup->sizer->Add(m_option_sizer, 1, wxEXPAND | wxLEFT, 5); - - sizer->Add(optgroup->sizer, 0, wxEXPAND | wxLEFT | wxTOP, 20); - - optgroup->disable(); - - get_optgroups().push_back(optgroup); // ogFrequentlyObjectSettings -} - - -// add Collapsible Pane to sizer -wxCollapsiblePane* add_collapsible_pane(wxWindow* parent, wxBoxSizer* sizer_parent, const wxString& name, std::function<wxSizer *(wxWindow *)> content_function) -{ -#ifdef __WXMSW__ - auto *collpane = new PrusaCollapsiblePaneMSW(parent, wxID_ANY, name); -#else - auto *collpane = new PrusaCollapsiblePane/*wxCollapsiblePane*/(parent, wxID_ANY, name); -#endif // __WXMSW__ - // add the pane with a zero proportion value to the sizer which contains it - sizer_parent->Add(collpane, 0, wxGROW | wxALL, 0); - - wxWindow *win = collpane->GetPane(); - - wxSizer *sizer = content_function(win); - - wxSizer *sizer_pane = new wxBoxSizer(wxVERTICAL); - sizer_pane->Add(sizer, 1, wxGROW | wxEXPAND | wxBOTTOM, 2); - win->SetSizer(sizer_pane); - // sizer_pane->SetSizeHints(win); - return collpane; -} - -void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer) -{ - // *** Objects List *** - auto collpane = add_collapsible_pane(parent, sizer, "Objects List:", create_objects_list); - collpane->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, ([collpane](wxCommandEvent& e){ - // wxWindowUpdateLocker noUpdates(g_right_panel); - if (collpane->IsCollapsed()) { - m_sizer_object_buttons->Show(false); - m_sizer_part_buttons->Show(false); - m_sizer_object_movers->Show(false); - if (!m_objects_ctrl->HasSelection()) - m_collpane_settings->Show(false); - } - })); - - // *** Object/Part Settings *** - m_collpane_settings = add_collapsible_pane(parent, sizer, "Object Settings", content_settings); -} - -void show_collpane_settings(bool expert_mode) -{ - m_collpane_settings->Show(expert_mode && !m_objects_model->IsEmpty()); -} - -void add_object_to_list(const std::string &name, ModelObject* model_object) -{ - wxString item_name = name; - auto item = m_objects_model->Add(item_name, model_object->instances.size()); - m_objects_ctrl->Select(item); - - // Add error icon if detected auto-repaire - auto stats = model_object->volumes[0]->mesh.stl.stats; - int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + - stats.facets_added + stats.facets_reversed + stats.backwards_edges; - if (errors > 0) { - const PrusaDataViewBitmapText data(item_name, m_icon_manifold_warning); - wxVariant variant; - variant << data; - m_objects_model->SetValue(variant, item, 0); - } - - if (model_object->volumes.size() > 1) { - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(item, - model_object->volumes[id]->name, - m_icon_solidmesh, - model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value, - false); - m_objects_ctrl->Expand(item); - } - -#ifndef __WXOSX__ - object_ctrl_selection_changed(); -#endif //__WXMSW__ -} - -void delete_object_from_list() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0)) - return; -// m_objects_ctrl->Select(m_objects_model->Delete(item)); - m_objects_model->Delete(item); - - part_selection_changed(); - -// if (m_objects_model->IsEmpty()) -// m_collpane_settings->Show(false); -} - -void delete_all_objects_from_list() -{ - m_objects_model->DeleteAll(); - - part_selection_changed(); -// m_collpane_settings->Show(false); -} - -void set_object_count(int idx, int count) -{ - m_objects_model->SetValue(wxString::Format("%d", count), idx, 1); - m_objects_ctrl->Refresh(); -} - -void unselect_objects() -{ - if (!m_objects_ctrl->GetSelection()) - return; - - g_prevent_list_events = true; - m_objects_ctrl->UnselectAll(); - part_selection_changed(); - g_prevent_list_events = false; -} - -void select_current_object(int idx) -{ - g_prevent_list_events = true; - m_objects_ctrl->UnselectAll(); - if (idx>=0) - m_objects_ctrl->Select(m_objects_model->GetItemById(idx)); - part_selection_changed(); - g_prevent_list_events = false; -} - -void select_current_volume(int idx, int vol_idx) -{ - if (vol_idx < 0) { - select_current_object(idx); - return; - } - g_prevent_list_events = true; - m_objects_ctrl->UnselectAll(); - if (idx >= 0) - m_objects_ctrl->Select(m_objects_model->GetItemByVolumeId(idx, vol_idx)); - part_selection_changed(); - g_prevent_list_events = false; -} - -void remove() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { - if (m_event_remove_object > 0) { - wxCommandEvent event(m_event_remove_object); - get_main_frame()->ProcessWindowEvent(event); - } -// delete_object_from_list(); - } - else - on_btn_del(); -} - -void object_ctrl_selection_changed() -{ - if (g_prevent_list_events) return; - - part_selection_changed(); - - if (m_event_object_selection_changed > 0) { - wxCommandEvent event(m_event_object_selection_changed); - event.SetId(m_selected_object_id); // set $obj_idx - const wxDataViewItem item = m_objects_ctrl->GetSelection(); - if (!item || m_objects_model->GetParent(item) == wxDataViewItem(0)) - event.SetInt(-1); // set $vol_idx - else { - const int vol_idx = m_objects_model->GetVolumeIdByItem(item); - if (vol_idx == -2) // is settings item - event.SetInt(m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item))); // set $vol_idx - else - event.SetInt(vol_idx); - } - get_main_frame()->ProcessWindowEvent(event); - } - -#ifdef __WXOSX__ - update_extruder_in_config(g_selected_extruder); -#endif //__WXOSX__ -} - -void object_ctrl_context_menu() -{ - wxDataViewItem item; - wxDataViewColumn* col; -// printf("object_ctrl_context_menu\n"); - const wxPoint pt = get_mouse_position_in_control(); -// printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y); - m_objects_ctrl->HitTest(pt, item, col); - if (!item) -#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX - // after Yosemite OS X version, HitTest return undefined item - item = m_objects_ctrl->GetSelection(); - if (item) - show_context_menu(); - else - printf("undefined item\n"); - return; -#else - return; -#endif // __WXOSX__ -// printf("item exists\n"); - const wxString title = col->GetTitle(); -// printf("title = *%s*\n", title.data().AsChar()); - - if (title == " ") - show_context_menu(); -// #ys_FIXME -// else if (title == _("Name") && pt.x >15 && -// m_objects_model->GetIcon(item).GetRefData() == m_icon_manifold_warning.GetRefData()) -// { -// if (is_windows10()) -// fix_through_netfabb(); -// } -#ifndef __WXMSW__ - m_objects_ctrl->GetMainWindow()->SetToolTip(""); // hide tooltip -#endif //__WXMSW__ -} - -void object_ctrl_key_event(wxKeyEvent& event) -{ - if (event.GetKeyCode() == WXK_TAB) - m_objects_ctrl->Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward); - else if (event.GetKeyCode() == WXK_DELETE -#ifdef __WXOSX__ - || event.GetKeyCode() == WXK_BACK -#endif //__WXOSX__ - ){ - printf("WXK_BACK\n"); - remove(); - } - else - event.Skip(); -} - -void object_ctrl_item_value_change(wxDataViewEvent& event) -{ - if (event.GetColumn() == 2) - { - wxVariant variant; - m_objects_model->GetValue(variant, event.GetItem(), 2); -#ifdef __WXOSX__ - g_selected_extruder = variant.GetString(); -#else // --> for Linux - update_extruder_in_config(variant.GetString()); -#endif //__WXOSX__ - } -} - -void show_manipulation_og(const bool show) -{ - wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer(); - if (show == grid_sizer->IsShown(2)) - return; - for (size_t id = 2; id < 12; id++) - grid_sizer->Show(id, show); -} - -//update_optgroup -void update_settings_list() -{ -#ifdef __WXGTK__ - auto parent = get_optgroup(ogFrequentlyObjectSettings)->get_parent(); -#else - auto parent = get_optgroup(ogFrequentlyObjectSettings)->parent(); -#endif /* __WXGTK__ */ - -// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952. -// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, -// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. -#ifdef __linux__ - std::unique_ptr<wxWindowUpdateLocker> no_updates(new wxWindowUpdateLocker(parent)); -#else - wxWindowUpdateLocker noUpdates(parent); -#endif - - m_option_sizer->Clear(true); - - bool show_manipulations = true; - const auto item = m_objects_ctrl->GetSelection(); - if (m_config && m_objects_model->IsSettingsItem(item)) - { - auto extra_column = [](wxWindow* parent, const Line& line) - { - auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line - - auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); -#ifdef __WXMSW__ - btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); -#endif // __WXMSW__ - btn->Bind(wxEVT_BUTTON, [opt_key](wxEvent &event){ - (*m_config)->erase(opt_key); - wxTheApp->CallAfter([]() { update_settings_list(); }); - }); - return btn; - }; - - std::map<std::string, std::vector<std::string>> cat_options; - auto opt_keys = (*m_config)->keys(); - m_og_settings.resize(0); - std::vector<std::string> categories; - if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return; - { - auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : - get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size(); - - for (auto& opt_key : opt_keys) { - auto category = (*m_config)->def()->get(opt_key)->category; - if (category.empty() || - (category == "Extruders" && extruders_cnt == 1)) continue; - - std::vector< std::string > new_category; - - auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category); - cat_opt.push_back(opt_key); - if (cat_opt.size() == 1) - cat_options[category] = cat_opt; - } - - for (auto& cat : cat_options) { - if (cat.second.size() == 1 && cat.second[0] == "extruder") - continue; - - auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, cat.first, *m_config, false, ogDEFAULT, extra_column); - optgroup->label_width = 150; - optgroup->sidetext_width = 70; - - for (auto& opt : cat.second) - { - if (opt == "extruder") - continue; - Option option = optgroup->get_option(opt); - option.opt.width = 70; - optgroup->append_single_option_line(option); - } - optgroup->reload_config(); - m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); - m_og_settings.push_back(optgroup); - - categories.push_back(cat.first); - } - } - - if (m_og_settings.empty()) { - m_objects_ctrl->Select(m_objects_model->Delete(item)); - part_selection_changed(); - } - else { - if (!categories.empty()) - m_objects_model->UpdateSettingsDigest(item, categories); - show_manipulations = false; - } - } - - show_manipulation_og(show_manipulations); - show_info_sizer(show_manipulations && item && m_objects_model->GetParent(item) == wxDataViewItem(0)); - -#ifdef __linux__ - no_updates.reset(nullptr); -#endif - - parent->Layout(); - get_right_panel()->GetParent()->Layout(); -} - -void get_settings_choice(wxMenu *menu, int id, bool is_part) -{ - const auto category_name = menu->GetLabel(id); - - wxArrayString names; - wxArrayInt selections; - - settings_menu_hierarchy settings_menu; - get_options_menu(settings_menu, is_part); - std::vector< std::pair<std::string, std::string> > *settings_list = nullptr; - - auto opt_keys = (*m_config)->keys(); - - for (auto& cat : settings_menu) - { - if (_(cat.first) == category_name) { - int sel = 0; - for (auto& pair : cat.second) { - names.Add(_(pair.second)); - if (find(opt_keys.begin(), opt_keys.end(), pair.first) != opt_keys.end()) - selections.Add(sel); - sel++; - } - settings_list = &cat.second; - break; - } - } - - if (!settings_list) - return; - - if (wxGetMultipleChoices(selections, _(L("Select showing settings")), category_name, names) ==0 ) - return; - - std::vector <std::string> selected_options; - for (auto sel : selections) - selected_options.push_back((*settings_list)[sel].first); - - for (auto& setting:(*settings_list) ) - { - auto& opt_key = setting.first; - if (find(opt_keys.begin(), opt_keys.end(), opt_key) != opt_keys.end() && - find(selected_options.begin(), selected_options.end(), opt_key) == selected_options.end()) - (*m_config)->erase(opt_key); - - if(find(opt_keys.begin(), opt_keys.end(), opt_key) == opt_keys.end() && - find(selected_options.begin(), selected_options.end(), opt_key) != selected_options.end()) - (*m_config)->set_key_value(opt_key, m_default_config.get()->option(opt_key)->clone()); - } - - - // Add settings item for object - const auto item = m_objects_ctrl->GetSelection(); - if (item) { - const auto settings_item = m_objects_model->HasSettings(item); - m_objects_ctrl->Select(settings_item ? settings_item : - m_objects_model->AddSettingsChild(item)); -#ifndef __WXOSX__ - part_selection_changed(); -#endif //no __WXOSX__ - } - else - update_settings_list(); -} - -void menu_item_add_generic(wxMenuItem* &menu, int id) { - auto sub_menu = new wxMenu; - - std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") }; - for (auto& item : menu_items) - sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item))); - -#ifndef __WXMSW__ - sub_menu->Bind(wxEVT_MENU, [sub_menu](wxEvent &event) { - load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString()); - }); -#endif //no __WXMSW__ - - menu->SetSubMenu(sub_menu); -} - -wxMenuItem* menu_item_split(wxMenu* menu, int id) { - auto menu_item = new wxMenuItem(menu, id, _(L("Split to parts"))); - menu_item->SetBitmap(m_bmp_split); - return menu_item; -} - -wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part) { - auto menu_item = new wxMenuItem(menu, id, _(L("Add settings"))); - menu_item->SetBitmap(m_bmp_cog); - - auto sub_menu = create_add_settings_popupmenu(is_part); - menu_item->SetSubMenu(sub_menu); - return menu_item; -} - -wxMenu *create_add_part_popupmenu() -{ - wxMenu *menu = new wxMenu; - std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") }; - - wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+4+2); - - int i = 0; - for (auto& item : menu_items) { - auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item)); - menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh); - if (item == "Add generic") - menu_item_add_generic(menu_item, config_id_base + i); - menu->Append(menu_item); - i++; - } - - menu->AppendSeparator(); - auto menu_item = menu_item_split(menu, config_id_base + i + 4); - menu->Append(menu_item); - menu_item->Enable(is_splittable_object(false)); - - menu->AppendSeparator(); - // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + i + 5, false)); - - menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){ - switch (event.GetId() - config_id_base) { - case 0: - on_btn_load(); - break; - case 1: - on_btn_load(true); - break; - case 2: -// on_btn_load(true, true); - break; - case 3: - case 4: - case 5: - case 6: -#ifdef __WXMSW__ - load_lambda(menu->GetLabel(event.GetId()).ToStdString()); -#endif // __WXMSW__ - break; - case 7: //3: - on_btn_split(false); - break; - default: -#ifdef __WXMSW__ - get_settings_choice(menu, event.GetId(), false); -#endif // __WXMSW__ - break; - } - }); - - return menu; -} - -wxMenu *create_part_settings_popupmenu() -{ - wxMenu *menu = new wxMenu; - wxWindowID config_id_base = wxWindow::NewControlId(2); - - auto menu_item = menu_item_split(menu, config_id_base); - menu->Append(menu_item); - menu_item->Enable(is_splittable_object(true)); - - menu->AppendSeparator(); - // Append settings popupmenu - menu->Append(menu_item_settings(menu, config_id_base + 1, true)); - - menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){ - switch (event.GetId() - config_id_base) { - case 0: - on_btn_split(true); - break; - default:{ - get_settings_choice(menu, event.GetId(), true); - break; } - } - }); - - return menu; -} - -wxMenu *create_add_settings_popupmenu(bool is_part) -{ - wxMenu *menu = new wxMenu; - - auto categories = get_category_icon(); - - settings_menu_hierarchy settings_menu; - get_options_menu(settings_menu, is_part); - - for (auto cat : settings_menu) - { - auto menu_item = new wxMenuItem(menu, wxID_ANY, _(cat.first)); - menu_item->SetBitmap(categories.find(cat.first) == categories.end() ? - wxNullBitmap : categories.at(cat.first)); - menu->Append(menu_item); - } -#ifndef __WXMSW__ - menu->Bind(wxEVT_MENU, [menu,is_part](wxEvent &event) { - get_settings_choice(menu, event.GetId(), is_part); - }); -#endif //no __WXMSW__ - return menu; -} - -void show_context_menu() -{ - const auto item = m_objects_ctrl->GetSelection(); - if (item) - { - if (m_objects_model->IsSettingsItem(item)) - return; - const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ? - create_add_part_popupmenu() : - create_part_settings_popupmenu(); - get_tab_panel()->GetPage(0)->PopupMenu(menu); - } -} - -// ****** - -void load_part( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier) -{ - wxWindow* parent = get_tab_panel()->GetPage(0); - - wxArrayString input_files; - open_model(parent, input_files); - for (int i = 0; i < input_files.size(); ++i) { - std::string input_file = input_files.Item(i).ToStdString(); - - Model model; - try { - model = Model::read_from_file(input_file); - } - catch (std::exception &e) { - auto msg = _(L("Error! ")) + input_file + " : " + e.what() + "."; - show_error(parent, msg); - exit(1); - } - - for ( auto object : model.objects) { - for (auto volume : object->volumes) { - auto new_volume = model_object->add_volume(*volume); - new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); - boost::filesystem::path(input_file).filename().string(); - new_volume->name = boost::filesystem::path(input_file).filename().string(); - - part_names.Add(new_volume->name); - - // apply the same translation we applied to the object - new_volume->mesh.translate( model_object->origin_translation(0), - model_object->origin_translation(1), - model_object->origin_translation(2) ); - // set a default extruder value, since user can't add it manually - new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - - m_parts_changed = true; - } - } - } -} - -void load_lambda( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier) -{ - auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow()); - if (dlg->ShowModal() == wxID_CANCEL) { - return; - } - - std::string name = "lambda-"; - TriangleMesh mesh; - - auto params = dlg->ObjectParameters(); - switch (params.type) - { - case LambdaTypeBox:{ - mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); - name += "Box"; - break;} - case LambdaTypeCylinder:{ - mesh = make_cylinder(params.cyl_r, params.cyl_h); - name += "Cylinder"; - break;} - case LambdaTypeSphere:{ - mesh = make_sphere(params.sph_rho); - name += "Sphere"; - break;} - case LambdaTypeSlab:{ - const auto& size = model_object->bounding_box().size(); - mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); - // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z - mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); - name += "Slab"; - break; } - default: - break; - } - mesh.repair(); - - auto new_volume = model_object->add_volume(mesh); - new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); - - new_volume->name = name; - // set a default extruder value, since user can't add it manually - new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - - part_names.Add(name); - - m_parts_changed = true; -} - -void load_lambda(const std::string& type_name) -{ - if (m_selected_object_id < 0) return; - - auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow(), type_name); - if (dlg->ShowModal() == wxID_CANCEL) - return; - - const std::string name = "lambda-"+type_name; - TriangleMesh mesh; - - const auto params = dlg->ObjectParameters(); - if (type_name == _("Box")) - mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]); - else if (type_name == _("Cylinder")) - mesh = make_cylinder(params.cyl_r, params.cyl_h); - else if (type_name == _("Sphere")) - mesh = make_sphere(params.sph_rho); - else if (type_name == _("Slab")){ - const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size(); - mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h); - // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z - mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z); - } - mesh.repair(); - - auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh); - new_volume->set_type(ModelVolume::PARAMETER_MODIFIER); - - new_volume->name = name; - // set a default extruder value, since user can't add it manually - new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); - - m_parts_changed = true; - parts_changed(m_selected_object_id); - - m_objects_ctrl->Select(m_objects_model->AddChild(m_objects_ctrl->GetSelection(), - name, m_icon_modifiermesh)); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME - object_ctrl_selection_changed(); -#endif //no __WXOSX__ //__WXMSW__ -} - -void on_btn_load(bool is_modifier /*= false*/, bool is_lambda/* = false*/) -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - int obj_idx = -1; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) - obj_idx = m_objects_model->GetIdByItem(item); - else - return; - - if (obj_idx < 0) return; - wxArrayString part_names; - if (is_lambda) - load_lambda((*m_objects)[obj_idx], part_names, is_modifier); - else - load_part((*m_objects)[obj_idx], part_names, is_modifier); - - parts_changed(obj_idx); - - for (int i = 0; i < part_names.size(); ++i) - m_objects_ctrl->Select( m_objects_model->AddChild(item, part_names.Item(i), - is_modifier ? m_icon_modifiermesh : m_icon_solidmesh)); -#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME - object_ctrl_selection_changed(); -#endif //no __WXOSX__//__WXMSW__ -} - -void remove_settings_from_config() -{ - auto opt_keys = (*m_config)->keys(); - if (opt_keys.size() == 1 && opt_keys[0] == "extruder") - return; - int extruder = -1; - if ((*m_config)->has("extruder")) - extruder = (*m_config)->option<ConfigOptionInt>("extruder")->value; - - (*m_config)->clear(); - - if (extruder >=0 ) - (*m_config)->set_key_value("extruder", new ConfigOptionInt(extruder)); -} - -bool remove_subobject_from_object(const int volume_id) -{ - const auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - - // if user is deleting the last solid part, throw error - int solid_cnt = 0; - for (auto vol : (*m_objects)[m_selected_object_id]->volumes) - if (vol->is_model_part()) - ++solid_cnt; - if (volume->is_model_part() && solid_cnt == 1) { - Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object."))); - return false; - } - - (*m_objects)[m_selected_object_id]->delete_volume(volume_id); - m_parts_changed = true; - - parts_changed(m_selected_object_id); - return true; -} - -void on_btn_del() -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item) return; - - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id ==-1) - return; - - if (volume_id ==-2) - remove_settings_from_config(); - else if (!remove_subobject_from_object(volume_id)) - return; - - m_objects_ctrl->Select(m_objects_model->Delete(item)); - part_selection_changed(); -} - -bool get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume) -{ - if (!item || m_selected_object_id < 0) - return false; - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) { - if (split_part) return false; - volume = (*m_objects)[m_selected_object_id]->volumes[0]; - } - else - volume = (*m_objects)[m_selected_object_id]->volumes[volume_id]; - if (volume) - return true; - return false; -} - -bool is_splittable_object(const bool split_part) -{ - const wxDataViewItem item = m_objects_ctrl->GetSelection(); - if (!item) return false; - - wxDataViewItemArray children; - if (!split_part && m_objects_model->GetChildren(item, children) > 0) - return false; - - ModelVolume* volume; - if (!get_volume_by_item(split_part, item, volume) || !volume) - return false; - - TriangleMeshPtrs meshptrs = volume->mesh.split(); - if (meshptrs.size() <= 1) { - delete meshptrs.front(); - return false; - } - - return true; -} - -void on_btn_split(const bool split_part) -{ - const auto item = m_objects_ctrl->GetSelection(); - if (!item || m_selected_object_id<0) - return; - ModelVolume* volume; - if (!get_volume_by_item(split_part, item, volume)) return; - DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config; - const auto nozzle_dmrs_cnt = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size(); - if (volume->split(nozzle_dmrs_cnt) == 1) { - wxMessageBox(_(L("The selected object couldn't be split because it contains only one part."))); - return; - } - - auto model_object = (*m_objects)[m_selected_object_id]; - - if (split_part) { - auto parent = m_objects_model->GetParent(item); - m_objects_model->DeleteChildren(parent); - - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(parent, model_object->volumes[id]->name, - model_object->volumes[id]->is_modifier() ? m_icon_modifiermesh : m_icon_solidmesh, - model_object->volumes[id]->config.has("extruder") ? - model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, - false); - - m_objects_ctrl->Expand(parent); - } - else { - for (auto id = 0; id < model_object->volumes.size(); id++) - m_objects_model->AddChild(item, model_object->volumes[id]->name, - m_icon_solidmesh, - model_object->volumes[id]->config.has("extruder") ? - model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0, - false); - m_objects_ctrl->Expand(item); - } - - m_parts_changed = true; - parts_changed(m_selected_object_id); -} - -void on_btn_move_up(){ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) - return; - auto& volumes = (*m_objects)[m_selected_object_id]->volumes; - if (0 < volume_id && volume_id < volumes.size()) { - std::swap(volumes[volume_id - 1], volumes[volume_id]); - m_parts_changed = true; - m_objects_ctrl->Select(m_objects_model->MoveChildUp(item)); - part_selection_changed(); -// #ifdef __WXMSW__ -// object_ctrl_selection_changed(); -// #endif //__WXMSW__ - } -} - -void on_btn_move_down(){ - auto item = m_objects_ctrl->GetSelection(); - if (!item) - return; - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0) - return; - auto& volumes = (*m_objects)[m_selected_object_id]->volumes; - if (0 <= volume_id && volume_id+1 < volumes.size()) { - std::swap(volumes[volume_id + 1], volumes[volume_id]); - m_parts_changed = true; - m_objects_ctrl->Select(m_objects_model->MoveChildDown(item)); - part_selection_changed(); -// #ifdef __WXMSW__ -// object_ctrl_selection_changed(); -// #endif //__WXMSW__ - } -} - -void parts_changed(int obj_idx) -{ - if (m_event_object_settings_changed <= 0) return; - - wxCommandEvent e(m_event_object_settings_changed); - auto event_str = wxString::Format("%d %d %d", obj_idx, - is_parts_changed() ? 1 : 0, - is_part_settings_changed() ? 1 : 0); - e.SetString(event_str); - get_main_frame()->ProcessWindowEvent(e); -} - -void update_settings_value() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - if (m_selected_object_id < 0 || m_objects->size() <= m_selected_object_id) { - og->set_value("position_x", 0); - og->set_value("position_y", 0); - og->set_value("position_z", 0); - og->set_value("scale_x", 0); - og->set_value("scale_y", 0); - og->set_value("scale_z", 0); - og->set_value("rotation_x", 0); - og->set_value("rotation_y", 0); - og->set_value("rotation_z", 0); - og->disable(); - return; - } - g_is_percent_scale = boost::any_cast<wxString>(og->get_value("scale_unit")) == _("%"); - update_position_values(); - update_scale_values(); - update_rotation_values(); - og->enable(); -} - -void part_selection_changed() -{ - auto item = m_objects_ctrl->GetSelection(); - int obj_idx = -1; - auto og = get_optgroup(ogFrequentlyObjectSettings); - m_config = nullptr; - wxString object_name = wxEmptyString; - if (item) - { - const bool is_settings_item = m_objects_model->IsSettingsItem(item); - bool is_part = false; - wxString og_name = wxEmptyString; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { - obj_idx = m_objects_model->GetIdByItem(item); - og_name = _(L("Object manipulation")); - m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config); - } - else { - auto parent = m_objects_model->GetParent(item); - // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene - obj_idx = m_objects_model->GetIdByItem(parent); - if (is_settings_item) { - if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) { - og_name = _(L("Object Settings to modify")); - m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config); - } - else { - og_name = _(L("Part Settings to modify")); - is_part = true; - auto main_parent = m_objects_model->GetParent(parent); - obj_idx = m_objects_model->GetIdByItem(main_parent); - const auto volume_id = m_objects_model->GetVolumeIdByItem(parent); - m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config); - } - } - else { - og_name = _(L("Part manipulation")); - is_part = true; - const auto volume_id = m_objects_model->GetVolumeIdByItem(item); - m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config); - } - } - - og->set_name(" " + og_name + " "); - object_name = m_objects_model->GetName(item); - m_default_config = std::make_shared<DynamicPrintConfig>(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part))); - } - og->set_value("object_name", object_name); - - update_settings_list(); - - m_selected_object_id = obj_idx; - - update_settings_value(); - -/* wxWindowUpdateLocker noUpdates(get_right_panel()); - - m_move_options = Point3(0, 0, 0); - m_last_coords = Point3(0, 0, 0); - // reset move sliders - std::vector<std::string> opt_keys = {"x", "y", "z"}; - auto og = get_optgroup(ogObjectMovers); - for (auto opt_key: opt_keys) - og->set_value(opt_key, int(0)); - -// if (!item || m_selected_object_id < 0){ - if (m_selected_object_id < 0){ - m_sizer_object_buttons->Show(false); - m_sizer_part_buttons->Show(false); - m_sizer_object_movers->Show(false); - m_collpane_settings->Show(false); - return; - } - - m_collpane_settings->Show(true); - - auto volume_id = m_objects_model->GetVolumeIdByItem(item); - if (volume_id < 0){ - m_sizer_object_buttons->Show(true); - m_sizer_part_buttons->Show(false); - m_sizer_object_movers->Show(false); - m_collpane_settings->SetLabelText(_(L("Object Settings")) + ":"); - -// elsif($itemData->{type} eq 'object') { -// # select nothing in 3D preview -// -// # attach object config to settings panel -// $self->{optgroup_movers}->disable; -// $self->{staticbox}->SetLabel('Object Settings'); -// @opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new); -// $config = $self->{model_object}->config; -// } - - return; - } - - m_collpane_settings->SetLabelText(_(L("Part Settings")) + ":"); - - m_sizer_object_buttons->Show(false); - m_sizer_part_buttons->Show(true); - m_sizer_object_movers->Show(true); - - auto bb_size = m_objects[m_selected_object_id]->bounding_box().size(); - int scale = 10; //?? - - m_mover_x->SetMin(-bb_size.x * 4 * scale); - m_mover_x->SetMax(bb_size.x * 4 * scale); - - m_mover_y->SetMin(-bb_size.y * 4 * scale); - m_mover_y->SetMax(bb_size.y * 4 * scale); - - m_mover_z->SetMin(-bb_size.z * 4 * scale); - m_mover_z->SetMax(bb_size.z * 4 * scale); - - - -// my ($config, @opt_keys); - m_btn_move_up->Enable(volume_id > 0); - m_btn_move_down->Enable(volume_id + 1 < m_objects[m_selected_object_id]->volumes.size()); - - // attach volume config to settings panel - auto volume = m_objects[m_selected_object_id]->volumes[volume_id]; - - if (volume->modifier) - og->enable(); - else - og->disable(); - -// auto config = volume->config; - - // get default values -// @opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys}; -// } -/* - # get default values - my $default_config = Slic3r::Config::new_from_defaults_keys(\@opt_keys); - - # append default extruder - push @opt_keys, 'extruder'; - $default_config->set('extruder', 0); - $config->set_ifndef('extruder', 0); - $self->{settings_panel}->set_default_config($default_config); - $self->{settings_panel}->set_config($config); - $self->{settings_panel}->set_opt_keys(\@opt_keys); - $self->{settings_panel}->set_fixed_options([qw(extruder)]); - $self->{settings_panel}->enable; - } - */ -} - -void set_extruder_column_hidden(bool hide) -{ - m_objects_ctrl->GetColumn(2)->SetHidden(hide); -} - -void update_extruder_in_config(const wxString& selection) -{ - if (!m_config || selection.empty()) - return; - - int extruder = selection.size() > 1 ? 0 : atoi(selection.c_str()); - (*m_config)->set_key_value("extruder", new ConfigOptionInt(extruder)); - - if (m_event_update_scene > 0) { - wxCommandEvent e(m_event_update_scene); - get_main_frame()->ProcessWindowEvent(e); - } -} - -void update_scale_values() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); - auto size = (*m_objects)[m_selected_object_id]->instance_bounding_box(0).size(); - - if (g_is_percent_scale) { - auto scale = instance->scaling_factor * 100.0; - og->set_value("scale_x", int(scale)); - og->set_value("scale_y", int(scale)); - og->set_value("scale_z", int(scale)); - } - else { - og->set_value("scale_x", int(instance->scaling_factor * size(0) + 0.5)); - og->set_value("scale_y", int(instance->scaling_factor * size(1) + 0.5)); - og->set_value("scale_z", int(instance->scaling_factor * size(2) + 0.5)); - } -} - -void update_position_values() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); - -#if ENABLE_MODELINSTANCE_3D_OFFSET - og->set_value("position_x", int(instance->get_offset(X))); - og->set_value("position_y", int(instance->get_offset(Y))); - og->set_value("position_z", int(instance->get_offset(Z))); -#else - og->set_value("position_x", int(instance->offset(0))); - og->set_value("position_y", int(instance->offset(1))); - og->set_value("position_z", 0); -#endif // ENABLE_MODELINSTANCE_3D_OFFSET -} - -void update_position_values(const Vec3d& position) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - og->set_value("position_x", int(position(0))); - og->set_value("position_y", int(position(1))); - og->set_value("position_z", int(position(2))); -} - -void update_scale_values(double scaling_factor) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - // this is temporary - // to be able to update the values as size - // we need to store somewhere the original size - // or have it passed as parameter - if (!g_is_percent_scale) - og->set_value("scale_unit", _("%")); - - auto scale = scaling_factor * 100.0; - og->set_value("scale_x", int(scale)); - og->set_value("scale_y", int(scale)); - og->set_value("scale_z", int(scale)); -} - -void update_rotation_values() -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - auto instance = (*m_objects)[m_selected_object_id]->instances.front(); - og->set_value("rotation_x", 0); - og->set_value("rotation_y", 0); - og->set_value("rotation_z", int(Geometry::rad2deg(instance->rotation))); -} - -void update_rotation_value(double angle, Axis axis) -{ - auto og = get_optgroup(ogFrequentlyObjectSettings); - - std::string axis_str; - switch (axis) - { - case X: - { - axis_str = "rotation_x"; - break; - } - case Y: - { - axis_str = "rotation_y"; - break; - } - case Z: - { - axis_str = "rotation_z"; - break; - } - } - - og->set_value(axis_str, int(Geometry::rad2deg(angle))); -} - -void set_uniform_scaling(const bool uniform_scale) -{ - g_is_uniform_scale = uniform_scale; -} - -void on_begin_drag(wxDataViewEvent &event) -{ - wxDataViewItem item(event.GetItem()); - - // only allow drags for item, not containers - if (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->IsSettingsItem(item)) { - event.Veto(); - return; - } - - /* Under MSW or OSX, DnD moves an item to the place of another selected item - * But under GTK, DnD moves an item between another two items. - * And as a result - call EVT_CHANGE_SELECTION to unselect all items. - * To prevent such behavior use g_prevent_list_events - **/ - g_prevent_list_events = true;//it's needed for GTK - - wxTextDataObject *obj = new wxTextDataObject; - obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item))); - event.SetDataObject(obj); - event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move; -} - -void on_drop_possible(wxDataViewEvent &event) -{ - wxDataViewItem item(event.GetItem()); - - // only allow drags for item or background, not containers - if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || - event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) - event.Veto(); -} - -void on_drop(wxDataViewEvent &event) -{ - wxDataViewItem item(event.GetItem()); - - // only allow drops for item, not containers - if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) || - event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) { - event.Veto(); - return; - } - - wxTextDataObject obj; - obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer()); - - int from_volume_id = std::stoi(obj.GetText().ToStdString()); - int to_volume_id = m_objects_model->GetVolumeIdByItem(item); - -#ifdef __WXGTK__ - /* Under GTK, DnD moves an item between another two items. - * And event.GetItem() return item, which is under "insertion line" - * So, if we move item down we should to decrease the to_volume_id value - **/ - if (to_volume_id > from_volume_id) to_volume_id--; -#endif // __WXGTK__ - - m_objects_ctrl->Select(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, - m_objects_model->GetParent(item))); - - auto& volumes = (*m_objects)[m_selected_object_id]->volumes; - auto delta = to_volume_id < from_volume_id ? -1 : 1; - int cnt = 0; - for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id+=delta, cnt++) - std::swap(volumes[id], volumes[id +delta]); - - m_parts_changed = true; - parts_changed(m_selected_object_id); - - g_prevent_list_events = false; -} - -void update_objects_list_extruder_column(int extruders_count) -{ - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) - extruders_count = 1; - - // delete old 3rd column - m_objects_ctrl->DeleteColumn(m_objects_ctrl->GetColumn(2)); - // insert new created 3rd column - m_objects_ctrl->InsertColumn(2, object_ctrl_create_extruder_column(extruders_count)); - // set show/hide for this column - set_extruder_column_hidden(extruders_count <= 1); -} - -void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas) -{ - m_slider = new PrusaDoubleSlider(parent, wxID_ANY, 0, 0, 0, 100); - sizer->Add(m_slider, 0, wxEXPAND, 0); - - m_preview_canvas = canvas; - m_preview_canvas->Bind(wxEVT_KEY_DOWN, update_double_slider_from_canvas); - - m_slider->Bind(wxEVT_SCROLL_CHANGED, [parent](wxEvent& event) { - _3DScene::set_toolpaths_range(m_preview_canvas, m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6); - if (parent->IsShown()) - m_preview_canvas->Refresh(); - }); -} - -void fill_slider_values(std::vector<std::pair<int, double>> &values, - const std::vector<double> &layers_z) -{ - std::vector<double> layers_all_z = _3DScene::get_current_print_zs(m_preview_canvas, false); - if (layers_all_z.size() == layers_z.size()) - for (int i = 0; i < layers_z.size(); i++) - values.push_back(std::pair<int, double>(i+1, layers_z[i])); - else if (layers_all_z.size() > layers_z.size()) { - int cur_id = 0; - for (int i = 0; i < layers_z.size(); i++) - for (int j = cur_id; j < layers_all_z.size(); j++) - if (layers_z[i] - 1e-6 < layers_all_z[j] && layers_all_z[j] < layers_z[i] + 1e-6) { - values.push_back(std::pair<int, double>(j+1, layers_z[i])); - cur_id = j; - break; - } - } -} - -void set_double_slider_thumbs( const bool force_sliders_full_range, - const std::vector<double> &layers_z, - const double z_low, const double z_high) -{ - // Force slider full range only when slider is created. - // Support selected diapason on the all next steps - if (/*force_sliders_full_range*/z_high == 0.0) { - m_slider->SetLowerValue(0); - m_slider->SetHigherValue(layers_z.size() - 1); - return; - } - - for (int i = layers_z.size() - 1; i >= 0; i--) - if (z_low >= layers_z[i]) { - m_slider->SetLowerValue(i); - break; - } - for (int i = layers_z.size() - 1; i >= 0 ; i--) - if (z_high >= layers_z[i]) { - m_slider->SetHigherValue(i); - break; - } -} - -void update_double_slider(bool force_sliders_full_range) -{ - std::vector<std::pair<int, double>> values; - std::vector<double> layers_z = _3DScene::get_current_print_zs(m_preview_canvas, true); - fill_slider_values(values, layers_z); - - const double z_low = m_slider->GetLowerValueD(); - const double z_high = m_slider->GetHigherValueD(); - m_slider->SetMaxValue(layers_z.size() - 1); - m_slider->SetSliderValues(values); - - set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high); -} - -void reset_double_slider() -{ - m_slider->SetHigherValue(0); - m_slider->SetLowerValue(0); -} - -void update_double_slider_from_canvas(wxKeyEvent& event) -{ - if (event.HasModifiers()) { - event.Skip(); - return; - } - - const auto key = event.GetKeyCode(); - - if (key == 'U' || key == 'D') { - const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1; - m_slider->SetHigherValue(new_pos); - if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue()); - } - else if (key == 'S') - m_slider->ChangeOneLayerLock(); - else - event.Skip(); -} - -void show_manipulation_sizer(const bool is_simple_mode) -{ - auto item = m_objects_ctrl->GetSelection(); - if (!item || !is_simple_mode) - return; - - if (m_objects_model->IsSettingsItem(item)) { - m_objects_ctrl->Select(m_objects_model->GetParent(item)); - part_selection_changed(); - } -} - -} //namespace GUI -} //namespace Slic3r
\ No newline at end of file diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp deleted file mode 100644 index e66b4d1db..000000000 --- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef slic3r_GUI_ObjectParts_hpp_ -#define slic3r_GUI_ObjectParts_hpp_ - -class wxWindow; -class wxSizer; -class wxBoxSizer; -class wxString; -class wxArrayString; -class wxMenu; -class wxDataViewEvent; -class wxKeyEvent; -class wxGLCanvas; -class wxBitmap; - -namespace Slic3r { -class ModelObject; -class Model; - -namespace GUI { -//class wxGLCanvas; - -enum ogGroup{ - ogFrequentlyChangingParameters, - ogFrequentlyObjectSettings, - ogCurrentSettings -// ogObjectSettings, -// ogObjectMovers, -// ogPartSettings -}; - -enum LambdaTypeIDs{ - LambdaTypeBox, - LambdaTypeCylinder, - LambdaTypeSphere, - LambdaTypeSlab -}; - -struct OBJECT_PARAMETERS -{ - LambdaTypeIDs type = LambdaTypeBox; - double dim[3];// = { 1.0, 1.0, 1.0 }; - int cyl_r = 1; - int cyl_h = 1; - double sph_rho = 1.0; - double slab_h = 1.0; - double slab_z = 0.0; -}; - -typedef std::map<std::string, wxBitmap> t_category_icon; -inline t_category_icon& get_category_icon(); - -void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer); -void add_objects_list(wxWindow* parent, wxBoxSizer* sizer); -void add_object_settings(wxWindow* parent, wxBoxSizer* sizer); -void show_collpane_settings(bool expert_mode); - -wxMenu *create_add_settings_popupmenu(bool is_part); -wxMenu *create_add_part_popupmenu(); -wxMenu *create_part_settings_popupmenu(); - -// Add object to the list -//void add_object(const std::string &name); -void add_object_to_list(const std::string &name, ModelObject* model_object); -// Delete object from the list -void delete_object_from_list(); -// Delete all objects from the list -void delete_all_objects_from_list(); -// Set count of object on c++ side -void set_object_count(int idx, int count); -// Unselect all objects in the list on c++ side -void unselect_objects(); -// Select current object in the list on c++ side -void select_current_object(int idx); -// Select current volume in the list on c++ side -void select_current_volume(int idx, int vol_idx); -// Remove objects/sub-object from the list -void remove(); - -void object_ctrl_selection_changed(); -void object_ctrl_context_menu(); -void object_ctrl_key_event(wxKeyEvent& event); -void object_ctrl_item_value_change(wxDataViewEvent& event); -void show_context_menu(); -bool is_splittable_object(const bool split_part); - -void init_mesh_icons(); -void set_event_object_selection_changed(const int& event); -void set_event_object_settings_changed(const int& event); -void set_event_remove_object(const int& event); -void set_event_update_scene(const int& event); -void set_objects_from_model(Model &model); - -bool is_parts_changed(); -bool is_part_settings_changed(); - -void load_part( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier); - -void load_lambda( ModelObject* model_object, - wxArrayString& part_names, const bool is_modifier); -void load_lambda( const std::string& type_name); - -void on_btn_load(bool is_modifier = false, bool is_lambda = false); -void on_btn_del(); -void on_btn_split(const bool split_part); -void on_btn_move_up(); -void on_btn_move_down(); - -void parts_changed(int obj_idx); -void part_selection_changed(); - -void update_settings_value(); -// show/hide "Extruder" column for Objects List -void set_extruder_column_hidden(bool hide); -// update extruder in current config -void update_extruder_in_config(const wxString& selection); -// update position values displacements or "gizmos" -void update_position_values(); -void update_position_values(const Vec3d& position); -// update scale values after scale unit changing or "gizmos" -void update_scale_values(); -void update_scale_values(double scaling_factor); -// update rotation values object selection changing -void update_rotation_values(); -// update rotation value after "gizmos" -void update_rotation_value(double angle, Axis axis); -void set_uniform_scaling(const bool uniform_scale); - -void on_begin_drag(wxDataViewEvent &event); -void on_drop_possible(wxDataViewEvent &event); -void on_drop(wxDataViewEvent &event); - -// update extruder column for objects_ctrl according to extruders count -void update_objects_list_extruder_column(int extruders_count); - -// Create/Update/Reset double slider on 3dPreview -void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas); -void update_double_slider(bool force_sliders_full_range); -void reset_double_slider(); -// update DoubleSlider after keyDown in canvas -void update_double_slider_from_canvas(wxKeyEvent& event); - -void show_manipulation_sizer(const bool is_simple_mode); - -} //namespace GUI -} //namespace Slic3r -#endif //slic3r_GUI_ObjectParts_hpp_
\ No newline at end of file diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp deleted file mode 100644 index 7d741be7f..000000000 --- a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include "LambdaObjectDialog.hpp" - -#include <wx/window.h> -#include <wx/button.h> -#include "OptionsGroup.hpp" - -namespace Slic3r -{ -namespace GUI -{ -static wxString dots("…", wxConvUTF8); - -LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent, - const wxString type_name): - m_type_name(type_name) -{ - Create(parent, wxID_ANY, _(L("Lambda Object")), - parent->GetScreenPosition(), wxDefaultSize, - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - - // instead of double dim[3] = { 1.0, 1.0, 1.0 }; - object_parameters.dim[0] = 1.0; - object_parameters.dim[1] = 1.0; - object_parameters.dim[2] = 1.0; - - sizer = new wxBoxSizer(wxVERTICAL); - - // modificator options - if (m_type_name == wxEmptyString) { - m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition, - wxDefaultSize, wxCHB_TOP); - sizer->Add(m_modificator_options_book, 1, wxEXPAND | wxALL, 10); - } - else { - m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize); - sizer->Add(m_panel, 1, wxEXPAND | wxALL, 10); - } - - ConfigOptionDef def; - def.width = 70; - auto optgroup = init_modificator_options_page(_(L("Box"))); - if (optgroup){ - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ - int opt_id = opt_key == "l" ? 0 : - opt_key == "w" ? 1 : - opt_key == "h" ? 2 : -1; - if (opt_id < 0) return; - object_parameters.dim[opt_id] = boost::any_cast<double>(value); - }; - - def.type = coFloat; - def.default_value = new ConfigOptionFloat{ 1.0 }; - def.label = L("L"); - Option option(def, "l"); - optgroup->append_single_option_line(option); - - def.label = L("W"); - option = Option(def, "w"); - optgroup->append_single_option_line(option); - - def.label = L("H"); - option = Option(def, "h"); - optgroup->append_single_option_line(option); - } - - optgroup = init_modificator_options_page(_(L("Cylinder"))); - if (optgroup){ - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ - int val = boost::any_cast<int>(value); - if (opt_key == "cyl_r") - object_parameters.cyl_r = val; - else if (opt_key == "cyl_h") - object_parameters.cyl_h = val; - else return; - }; - - def.type = coInt; - def.default_value = new ConfigOptionInt{ 1 }; - def.label = L("Radius"); - auto option = Option(def, "cyl_r"); - optgroup->append_single_option_line(option); - - def.label = L("Height"); - option = Option(def, "cyl_h"); - optgroup->append_single_option_line(option); - } - - optgroup = init_modificator_options_page(_(L("Sphere"))); - if (optgroup){ - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ - if (opt_key == "sph_rho") - object_parameters.sph_rho = boost::any_cast<double>(value); - else return; - }; - - def.type = coFloat; - def.default_value = new ConfigOptionFloat{ 1.0 }; - def.label = L("Rho"); - auto option = Option(def, "sph_rho"); - optgroup->append_single_option_line(option); - } - - optgroup = init_modificator_options_page(_(L("Slab"))); - if (optgroup){ - optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ - double val = boost::any_cast<double>(value); - if (opt_key == "slab_z") - object_parameters.slab_z = val; - else if (opt_key == "slab_h") - object_parameters.slab_h = val; - else return; - }; - - def.type = coFloat; - def.default_value = new ConfigOptionFloat{ 1.0 }; - def.label = L("H"); - auto option = Option(def, "slab_h"); - optgroup->append_single_option_line(option); - - def.label = L("Initial Z"); - option = Option(def, "slab_z"); - optgroup->append_single_option_line(option); - } - - Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e) - { - auto page_idx = m_modificator_options_book->GetSelection(); - if (page_idx < 0) return; - switch (page_idx) - { - case 0: - object_parameters.type = LambdaTypeBox; - break; - case 1: - object_parameters.type = LambdaTypeCylinder; - break; - case 2: - object_parameters.type = LambdaTypeSphere; - break; - case 3: - object_parameters.type = LambdaTypeSlab; - break; - default: - break; - } - })); - - const auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL); - - wxButton* btn_OK = static_cast<wxButton*>(FindWindowById(wxID_OK, this)); - btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - // validate user input - if (!CanClose())return; - EndModal(wxID_OK); - Destroy(); - }); - - wxButton* btn_CANCEL = static_cast<wxButton*>(FindWindowById(wxID_CANCEL, this)); - btn_CANCEL->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { - // validate user input - if (!CanClose())return; - EndModal(wxID_CANCEL); - Destroy(); - }); - - sizer->Add(button_sizer, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); - - SetSizer(sizer); - sizer->Fit(this); - sizer->SetSizeHints(this); -} - -// Called from the constructor. -// Create a panel for a rectangular / circular / custom bed shape. -ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title){ - if (!m_type_name.IsEmpty() && m_type_name != title) - return nullptr; - - auto panel = m_type_name.IsEmpty() ? new wxPanel(m_modificator_options_book) : m_panel; - - ConfigOptionsGroupShp optgroup; - optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Add")) + " " +title + " " +dots); - optgroup->label_width = 100; - - m_optgroups.push_back(optgroup); - - if (m_type_name.IsEmpty()) { - panel->SetSizerAndFit(optgroup->sizer); - m_modificator_options_book->AddPage(panel, title); - } - else - panel->SetSizer(optgroup->sizer); - - return optgroup; -} - - -} //namespace GUI -} //namespace Slic3r diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp deleted file mode 100644 index 8f3e8cd80..000000000 --- a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef slic3r_LambdaObjectDialog_hpp_ -#define slic3r_LambdaObjectDialog_hpp_ - -#include "GUI.hpp" - -#include <wx/dialog.h> -#include <wx/sizer.h> -#include <wx/choicebk.h> - -class wxPanel; - -namespace Slic3r -{ -namespace GUI -{ -using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>; -class LambdaObjectDialog : public wxDialog -{ - wxChoicebook* m_modificator_options_book = nullptr; - std::vector <ConfigOptionsGroupShp> m_optgroups; - wxString m_type_name; - wxPanel* m_panel = nullptr; -public: - LambdaObjectDialog(wxWindow* parent, - const wxString type_name = wxEmptyString); - ~LambdaObjectDialog(){} - - bool CanClose() { return true; } // ??? - OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; } - - ConfigOptionsGroupShp init_modificator_options_page(const wxString& title); - - // Note whether the window was already closed, so a pending update is not executed. - bool m_already_closed = false; - OBJECT_PARAMETERS object_parameters; - wxBoxSizer* sizer = nullptr; -}; -} //namespace GUI -} //namespace Slic3r -#endif //slic3r_LambdaObjectDialog_hpp_ diff --git a/xs/src/slic3r/GUI/MsgDialog.cpp b/xs/src/slic3r/GUI/MsgDialog.cpp deleted file mode 100644 index 58679ed9e..000000000 --- a/xs/src/slic3r/GUI/MsgDialog.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "MsgDialog.hpp" - -#include <wx/settings.h> -#include <wx/sizer.h> -#include <wx/stattext.h> -#include <wx/button.h> -#include <wx/statbmp.h> -#include <wx/scrolwin.h> - -#include "libslic3r/libslic3r.h" -#include "libslic3r/Utils.hpp" -#include "GUI.hpp" -#include "ConfigWizard.hpp" - -namespace Slic3r { -namespace GUI { - - -MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id) : - MsgDialog(parent, title, headline, wxBitmap(from_u8(Slic3r::var("Slic3r_192px.png")), wxBITMAP_TYPE_PNG), button_id) -{} - -MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id) : - wxDialog(parent, wxID_ANY, title), - boldfont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)), - content_sizer(new wxBoxSizer(wxVERTICAL)), - btn_sizer(new wxBoxSizer(wxHORIZONTAL)) -{ - boldfont.SetWeight(wxFONTWEIGHT_BOLD); - - auto *topsizer = new wxBoxSizer(wxHORIZONTAL); - auto *rightsizer = new wxBoxSizer(wxVERTICAL); - - auto *headtext = new wxStaticText(this, wxID_ANY, headline); - headtext->SetFont(boldfont); - headtext->Wrap(CONTENT_WIDTH); - rightsizer->Add(headtext); - rightsizer->AddSpacer(VERT_SPACING); - - rightsizer->Add(content_sizer, 1, wxEXPAND); - - if (button_id != wxID_NONE) { - auto *button = new wxButton(this, button_id); - button->SetFocus(); - btn_sizer->Add(button); - } - - rightsizer->Add(btn_sizer, 0, wxALIGN_CENTRE_HORIZONTAL); - - auto *logo = new wxStaticBitmap(this, wxID_ANY, std::move(bitmap)); - - topsizer->Add(logo, 0, wxALL, BORDER); - topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER); - - SetSizerAndFit(topsizer); -} - -MsgDialog::~MsgDialog() {} - - -// ErrorDialog - -ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : - MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG)) -{ - auto *panel = new wxScrolledWindow(this); - auto *p_sizer = new wxBoxSizer(wxVERTICAL); - panel->SetSizer(p_sizer); - - auto *text = new wxStaticText(panel, wxID_ANY, msg); - text->Wrap(CONTENT_WIDTH); - p_sizer->Add(text, 1, wxEXPAND); - - panel->SetMinSize(wxSize(CONTENT_WIDTH, 0)); - panel->SetScrollRate(0, 5); - - content_sizer->Add(panel, 1, wxEXPAND); - - SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT)); - Fit(); -} - -ErrorDialog::~ErrorDialog() {} - - - -} -} diff --git a/xs/src/slic3r/GUI/MsgDialog.hpp b/xs/src/slic3r/GUI/MsgDialog.hpp deleted file mode 100644 index ca349eb5c..000000000 --- a/xs/src/slic3r/GUI/MsgDialog.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef slic3r_MsgDialog_hpp_ -#define slic3r_MsgDialog_hpp_ - -#include <string> -#include <unordered_map> - -#include <wx/dialog.h> -#include <wx/font.h> -#include <wx/bitmap.h> - -#include "slic3r/Utils/Semver.hpp" - -class wxBoxSizer; -class wxCheckBox; - -namespace Slic3r { - -namespace GUI { - - -// A message / query dialog with a bitmap on the left and any content on the right -// with buttons underneath. -struct MsgDialog : wxDialog -{ - MsgDialog(MsgDialog &&) = delete; - MsgDialog(const MsgDialog &) = delete; - MsgDialog &operator=(MsgDialog &&) = delete; - MsgDialog &operator=(const MsgDialog &) = delete; - virtual ~MsgDialog(); - - // TODO: refactor with CreateStdDialogButtonSizer usage - -protected: - enum { - CONTENT_WIDTH = 500, - CONTENT_MAX_HEIGHT = 600, - BORDER = 30, - VERT_SPACING = 15, - HORIZ_SPACING = 5, - }; - - // button_id is an id of a button that can be added by default, use wxID_NONE to disable - MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxWindowID button_id = wxID_OK); - MsgDialog(wxWindow *parent, const wxString &title, const wxString &headline, wxBitmap bitmap, wxWindowID button_id = wxID_OK); - - wxFont boldfont; - wxBoxSizer *content_sizer; - wxBoxSizer *btn_sizer; -}; - - -// Generic error dialog, used for displaying exceptions -struct ErrorDialog : MsgDialog -{ - ErrorDialog(wxWindow *parent, const wxString &msg); - ErrorDialog(ErrorDialog &&) = delete; - ErrorDialog(const ErrorDialog &) = delete; - ErrorDialog &operator=(ErrorDialog &&) = delete; - ErrorDialog &operator=(const ErrorDialog &) = delete; - virtual ~ErrorDialog(); -}; - - -} -} - -#endif diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp deleted file mode 100644 index ea22b2cb5..000000000 --- a/xs/src/slic3r/GUI/OptionsGroup.cpp +++ /dev/null @@ -1,545 +0,0 @@ -#include "OptionsGroup.hpp" -#include "ConfigExceptions.hpp" - -#include <utility> -#include <wx/numformatter.h> -#include <boost/algorithm/string/split.hpp> -#include <boost/algorithm/string/classification.hpp> -#include "Utils.hpp" - -namespace Slic3r { namespace GUI { - -const t_field& OptionsGroup::build_field(const Option& opt, wxStaticText* label/* = nullptr*/) { - return build_field(opt.opt_id, opt.opt, label); -} -const t_field& OptionsGroup::build_field(const t_config_option_key& id, wxStaticText* label/* = nullptr*/) { - const ConfigOptionDef& opt = m_options.at(id).opt; - return build_field(id, opt, label); -} - -const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label/* = nullptr*/) { - // Check the gui_type field first, fall through - // is the normal type. - if (opt.gui_type.compare("select") == 0) { - } else if (opt.gui_type.compare("select_open") == 0) { - m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); - } else if (opt.gui_type.compare("color") == 0) { - m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(parent(), opt, id))); - } else if (opt.gui_type.compare("f_enum_open") == 0 || - opt.gui_type.compare("i_enum_open") == 0 || - opt.gui_type.compare("i_enum_closed") == 0) { - m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); - } else if (opt.gui_type.compare("slider") == 0) { - m_fields.emplace(id, STDMOVE(SliderCtrl::Create<SliderCtrl>(parent(), opt, id))); - } else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl - } else if (opt.gui_type.compare("legend") == 0) { // StaticText - m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id))); - } else { - switch (opt.type) { - case coFloatOrPercent: - case coFloat: - case coFloats: - case coPercent: - case coPercents: - case coString: - case coStrings: - m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(parent(), opt, id))); - break; - case coBool: - case coBools: - m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(parent(), opt, id))); - break; - case coInt: - case coInts: - m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(parent(), opt, id))); - break; - case coEnum: - m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id))); - break; - case coPoints: - m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(parent(), opt, id))); - break; - case coNone: break; - default: - throw /*//!ConfigGUITypeError("")*/std::logic_error("This control doesn't exist till now"); break; - } - } - // Grab a reference to fields for convenience - const t_field& field = m_fields[id]; - field->m_on_change = [this](std::string opt_id, boost::any value){ - //! This function will be called from Field. - //! Call OptionGroup._on_change(...) - if (!m_disabled) - this->on_change_OG(opt_id, value); - }; - field->m_on_kill_focus = [this](){ - //! This function will be called from Field. - if (!m_disabled) - this->on_kill_focus(); - }; - field->m_parent = parent(); - - //! Label to change background color, when option is modified - field->m_Label = label; - field->m_back_to_initial_value = [this](std::string opt_id){ - if (!m_disabled) - this->back_to_initial_value(opt_id); - }; - field->m_back_to_sys_value = [this](std::string opt_id){ - if (!this->m_disabled) - this->back_to_sys_value(opt_id); - }; - - // assign function objects for callbacks, etc. - return field; -} - -void OptionsGroup::add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field) -{ - if (!m_show_modified_btns) { - field->m_Undo_btn->Hide(); - field->m_Undo_to_sys_btn->Hide(); - return; - } - - sizer->Add(field->m_Undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); - sizer->Add(field->m_Undo_btn, 0, wxALIGN_CENTER_VERTICAL); -} - -void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/* = nullptr*/) { -//! if (line.sizer != nullptr || (line.widget != nullptr && line.full_width > 0)){ - if ( (line.sizer != nullptr || line.widget != nullptr) && line.full_width){ - if (line.sizer != nullptr) { - sizer->Add(line.sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 15); - return; - } - if (line.widget != nullptr) { - sizer->Add(line.widget(m_parent), 0, wxEXPAND | wxALL, wxOSX ? 0 : 15); - return; - } - } - - auto option_set = line.get_options(); - for (auto opt : option_set) - m_options.emplace(opt.opt_id, opt); - - // if we have a single option with no label, no sidetext just add it directly to sizer - if (option_set.size() == 1 && label_width == 0 && option_set.front().opt.full_width && - option_set.front().opt.sidetext.size() == 0 && option_set.front().side_widget == nullptr && - line.get_extra_widgets().size() == 0) { - wxSizer* tmp_sizer; -#ifdef __WXGTK__ - tmp_sizer = new wxBoxSizer(wxVERTICAL); - m_panel->SetSizer(tmp_sizer); - m_panel->Layout(); -#else - tmp_sizer = sizer; -#endif /* __WXGTK__ */ - - const auto& option = option_set.front(); - const auto& field = build_field(option); - - auto btn_sizer = new wxBoxSizer(wxHORIZONTAL); - add_undo_buttuns_to_sizer(btn_sizer, field); - tmp_sizer->Add(btn_sizer, 0, wxEXPAND | wxALL, 0); - if (is_window_field(field)) - tmp_sizer->Add(field->getWindow(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); - if (is_sizer_field(field)) - tmp_sizer->Add(field->getSizer(), 0, wxEXPAND | wxALL, wxOSX ? 0 : 5); - return; - } - - auto grid_sizer = m_grid_sizer; -#ifdef __WXGTK__ - m_panel->SetSizer(m_grid_sizer); - m_panel->Layout(); -#endif /* __WXGTK__ */ - - // if we have an extra column, build it - if (extra_column) { - if (extra_column) { - grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3); - } - else { - // if the callback provides no sizer for the extra cell, put a spacer - grid_sizer->AddSpacer(1); - } - } - - - // Build a label if we have it - wxStaticText* label=nullptr; - if (label_width != 0) { - long label_style = staticbox ? 0 : wxALIGN_RIGHT; -#ifdef __WXGTK__ - // workaround for correct text align of the StaticBox on Linux - // flags wxALIGN_RIGHT and wxALIGN_CENTRE don't work when Ellipsize flags are _not_ given. - // Text is properly aligned only when Ellipsize is checked. - label_style |= staticbox ? 0 : wxST_ELLIPSIZE_END; -#endif /* __WXGTK__ */ - label = new wxStaticText(parent(), wxID_ANY, line.label + (line.label.IsEmpty() ? "" : ":"), - wxDefaultPosition, wxSize(label_width, -1), label_style); - label->SetFont(label_font); - label->Wrap(label_width); // avoid a Linux/GTK bug - if (!line.near_label_widget) - grid_sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | - (m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5); - else { - // If we're here, we have some widget near the label - // so we need a horizontal sizer to arrange these things - auto sizer = new wxBoxSizer(wxHORIZONTAL); - grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); - sizer->Add(line.near_label_widget(parent()), 0, wxRIGHT, 7); - sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | - (m_flag == ogSIDE_OPTIONS_VERTICAL ? wxTOP : wxALIGN_CENTER_VERTICAL), 5); - } - if (line.label_tooltip.compare("") != 0) - label->SetToolTip(line.label_tooltip); - } - - // If there's a widget, build it and add the result to the sizer. - if (line.widget != nullptr) { - auto wgt = line.widget(parent()); - // If widget doesn't have label, don't use border - grid_sizer->Add(wgt, 0, wxEXPAND | wxBOTTOM | wxTOP, (wxOSX || line.label.IsEmpty()) ? 0 : 5); - if (colored_Label != nullptr) *colored_Label = label; - return; - } - - // If we're here, we have more than one option or a single option with sidetext - // so we need a horizontal sizer to arrange these things - auto sizer = new wxBoxSizer(m_flag == ogSIDE_OPTIONS_VERTICAL ? wxVERTICAL : wxHORIZONTAL); - grid_sizer->Add(sizer, 0, wxEXPAND | (staticbox ? wxALL : wxBOTTOM | wxTOP | wxLEFT), staticbox ? 0 : 1); - // If we have a single option with no sidetext just add it directly to the grid sizer - if (option_set.size() == 1 && option_set.front().opt.sidetext.size() == 0 && - option_set.front().side_widget == nullptr && line.get_extra_widgets().size() == 0) { - const auto& option = option_set.front(); - const auto& field = build_field(option, label); - - add_undo_buttuns_to_sizer(sizer, field); - if (is_window_field(field)) - sizer->Add(field->getWindow(), option.opt.full_width ? 1 : 0, (option.opt.full_width ? wxEXPAND : 0) | - wxBOTTOM | wxTOP | wxALIGN_CENTER_VERTICAL, (wxOSX||!staticbox) ? 0 : 2); - if (is_sizer_field(field)) - sizer->Add(field->getSizer(), 1, (option.opt.full_width ? wxEXPAND : 0) | wxALIGN_CENTER_VERTICAL, 0); - return; - } - - for (auto opt : option_set) { - ConfigOptionDef option = opt.opt; - wxSizer* sizer_tmp; - if (m_flag == ogSIDE_OPTIONS_VERTICAL){ - auto sz = new wxFlexGridSizer(1, 3, 2, 2); - sz->RemoveGrowableCol(2); - sizer_tmp = sz; - } - else - sizer_tmp = sizer; - // add label if any - if (option.label != "") { - wxString str_label = _(option.label); -//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1 -// wxString str_label = (option.label == "Top" || option.label == "Bottom") ? -// wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()): -// L_str(option.label); - label = new wxStaticText(parent(), wxID_ANY, str_label + ":", wxDefaultPosition, wxDefaultSize); - label->SetFont(label_font); - sizer_tmp->Add(label, 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL, 0); - } - - // add field - const Option& opt_ref = opt; - auto& field = build_field(opt_ref, label); - add_undo_buttuns_to_sizer(sizer_tmp, field); - is_sizer_field(field) ? - sizer_tmp->Add(field->getSizer(), 0, wxALIGN_CENTER_VERTICAL, 0) : - sizer_tmp->Add(field->getWindow(), 0, wxALIGN_CENTER_VERTICAL, 0); - - // add sidetext if any - if (option.sidetext != "") { - auto sidetext = new wxStaticText( parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, - wxSize(sidetext_width, -1)/*wxDefaultSize*/, wxALIGN_LEFT); - sidetext->SetFont(sidetext_font); - sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, m_flag == ogSIDE_OPTIONS_VERTICAL ? 0 : 4); - field->set_side_text_ptr(sidetext); - } - - // add side widget if any - if (opt.side_widget != nullptr) { - sizer_tmp->Add(opt.side_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 1); //! requires verification - } - - if (opt.opt_id != option_set.back().opt_id && m_flag != ogSIDE_OPTIONS_VERTICAL) //! istead of (opt != option_set.back()) - { - sizer_tmp->AddSpacer(6); - } - - if (m_flag == ogSIDE_OPTIONS_VERTICAL) - sizer->Add(sizer_tmp, 0, wxALIGN_RIGHT|wxALL, 0); - } - // add extra sizers if any - for (auto extra_widget : line.get_extra_widgets()) { - sizer->Add(extra_widget(parent())/*!.target<wxWindow>()*/, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4); //! requires verification - } -} - -Line OptionsGroup::create_single_option_line(const Option& option) const { - Line retval{ _(option.opt.label), _(option.opt.tooltip) }; - Option tmp(option); - tmp.opt.label = std::string(""); - retval.append_option(tmp); - return retval; -} - -void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) { - if (m_on_change != nullptr) - m_on_change(opt_id, value); -} - -Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index /*= -1*/) -{ - if (!m_config->has(opt_key)) { - std::cerr << "No " << opt_key << " in ConfigOptionsGroup config.\n"; - } - - std::string opt_id = opt_index == -1 ? opt_key : opt_key + "#" + std::to_string(opt_index); - std::pair<std::string, int> pair(opt_key, opt_index); - m_opt_map.emplace(opt_id, pair); - - return Option(*m_config->def()->get(opt_key), opt_id); -} - -void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) -{ - if (!m_opt_map.empty()) - { - auto it = m_opt_map.find(opt_id); - if (it == m_opt_map.end()) - { - OptionsGroup::on_change_OG(opt_id, value); - return; - } - - auto itOption = it->second; - std::string opt_key = itOption.first; - int opt_index = itOption.second; - - auto option = m_options.at(opt_id).opt; - - // get value -//! auto field_value = get_value(opt_id); - if (option.gui_flags.compare("serialized")==0) { - if (opt_index != -1){ - // die "Can't set serialized option indexed value" ; - } - change_opt_value(*m_config, opt_key, value); - } - else { - if (opt_index == -1) { - // change_opt_value(*m_config, opt_key, field_value); - //!? why field_value?? in this case changed value will be lose! No? - change_opt_value(*m_config, opt_key, value); - } - else { - change_opt_value(*m_config, opt_key, value, opt_index); -// auto value = m_config->get($opt_key); -// $value->[$opt_index] = $field_value; -// $self->config->set($opt_key, $value); - } - } - } - - OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this -} - -void ConfigOptionsGroup::back_to_initial_value(const std::string& opt_key) -{ - if (m_get_initial_config == nullptr) - return; - back_to_config_value(m_get_initial_config(), opt_key); -} - -void ConfigOptionsGroup::back_to_sys_value(const std::string& opt_key) -{ - if (m_get_sys_config == nullptr) - return; - if (!have_sys_config()) - return; - back_to_config_value(m_get_sys_config(), opt_key); -} - -void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key) -{ - boost::any value; - if (opt_key == "extruders_count"){ - auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter")); - value = int(nozzle_diameter->values.size()); - } - else if (m_opt_map.find(opt_key) != m_opt_map.end()) - { - auto opt_id = m_opt_map.find(opt_key)->first; - std::string opt_short_key = m_opt_map.at(opt_id).first; - int opt_index = m_opt_map.at(opt_id).second; - value = get_config_value(config, opt_short_key, opt_index); - } - else{ - value = get_config_value(config, opt_key); - change_opt_value(*m_config, opt_key, value); - return; - } - - set_value(opt_key, value); - on_change_OG(opt_key, get_value(opt_key)); -} - -void ConfigOptionsGroup::reload_config(){ - for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { - auto opt_id = it->first; - std::string opt_key = m_opt_map.at(opt_id).first; - int opt_index = m_opt_map.at(opt_id).second; - auto option = m_options.at(opt_id).opt; - set_value(opt_id, config_value(opt_key, opt_index, option.gui_flags.compare("serialized") == 0 )); - } - -} - -boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize){ - - if (deserialize) { - // Want to edit a vector value(currently only multi - strings) in a single edit box. - // Aggregate the strings the old way. - // Currently used for the post_process config value only. - if (opt_index != -1) - throw std::out_of_range("Can't deserialize option indexed value"); -// return join(';', m_config->get(opt_key)}); - return get_config_value(*m_config, opt_key); - } - else { -// return opt_index == -1 ? m_config->get(opt_key) : m_config->get_at(opt_key, opt_index); - return get_config_value(*m_config, opt_key, opt_index); - } -} - -boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index /*= -1*/) -{ - size_t idx = opt_index == -1 ? 0 : opt_index; - - boost::any ret; - wxString text_value = wxString(""); - const ConfigOptionDef* opt = config.def()->get(opt_key); - switch (opt->type){ - case coFloatOrPercent:{ - const auto &value = *config.option<ConfigOptionFloatOrPercent>(opt_key); - if (value.percent) - { - text_value = wxString::Format(_T("%i"), int(value.value)); - text_value += "%"; - } - else - text_value = double_to_string(value.value); - ret = text_value; - break; - } - case coPercent:{ - double val = config.option<ConfigOptionPercent>(opt_key)->value; - text_value = wxString::Format(_T("%i"), int(val)); - ret = text_value;// += "%"; - } - break; - case coPercents: - case coFloats: - case coFloat:{ - double val = opt->type == coFloats ? - config.opt_float(opt_key, idx) : - opt->type == coFloat ? config.opt_float(opt_key) : - config.option<ConfigOptionPercents>(opt_key)->get_at(idx); - ret = double_to_string(val); - } - break; - case coString: - ret = static_cast<wxString>(config.opt_string(opt_key)); - break; - case coStrings: - if (opt_key.compare("compatible_printers") == 0){ - ret = config.option<ConfigOptionStrings>(opt_key)->values; - break; - } - if (config.option<ConfigOptionStrings>(opt_key)->values.empty()) - ret = text_value; - else if (opt->gui_flags.compare("serialized") == 0){ - std::vector<std::string> values = config.option<ConfigOptionStrings>(opt_key)->values; - if (!values.empty() && values[0].compare("") != 0) - for (auto el : values) - text_value += el + ";"; - ret = text_value; - } - else - ret = static_cast<wxString>(config.opt_string(opt_key, static_cast<unsigned int>(idx))); - break; - case coBool: - ret = config.opt_bool(opt_key); - break; - case coBools: - ret = config.opt_bool(opt_key, idx); - break; - case coInt: - ret = config.opt_int(opt_key); - break; - case coInts: - ret = config.opt_int(opt_key, idx); - break; - case coEnum:{ - if (opt_key.compare("external_fill_pattern") == 0 || - opt_key.compare("fill_pattern") == 0 ){ - ret = static_cast<int>(config.option<ConfigOptionEnum<InfillPattern>>(opt_key)->value); - } - else if (opt_key.compare("gcode_flavor") == 0 ){ - ret = static_cast<int>(config.option<ConfigOptionEnum<GCodeFlavor>>(opt_key)->value); - } - else if (opt_key.compare("support_material_pattern") == 0){ - ret = static_cast<int>(config.option<ConfigOptionEnum<SupportMaterialPattern>>(opt_key)->value); - } - else if (opt_key.compare("seam_position") == 0){ - ret = static_cast<int>(config.option<ConfigOptionEnum<SeamPosition>>(opt_key)->value); - } - else if (opt_key.compare("host_type") == 0){ - ret = static_cast<int>(config.option<ConfigOptionEnum<PrintHostType>>(opt_key)->value); - } - } - break; - case coPoints: - if (opt_key.compare("bed_shape") == 0) - ret = config.option<ConfigOptionPoints>(opt_key)->values; - else - ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx); - break; - case coNone: - default: - break; - } - return ret; -} - -Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index){ - Field* field = get_field(opt_key); - if (field != nullptr) - return field; - std::string opt_id = ""; - for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { - if (opt_key == m_opt_map.at(it->first).first && opt_index == m_opt_map.at(it->first).second){ - opt_id = it->first; - break; - } - } - return opt_id.empty() ? nullptr : get_field(opt_id); -} - -void ogStaticText::SetText(const wxString& value, bool wrap/* = true*/) -{ - SetLabel(value); - if (wrap) Wrap(400); - GetParent()->Layout(); -} - -} // GUI -} // Slic3r diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp deleted file mode 100644 index 4941e5453..000000000 --- a/xs/src/slic3r/GUI/OptionsGroup.hpp +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef slic3r_OptionsGroup_hpp_ -#define slic3r_OptionsGroup_hpp_ - -#include <wx/wx.h> -#include <wx/stattext.h> -#include <wx/settings.h> -//#include <wx/window.h> - -#include <map> -#include <functional> - -#include "libslic3r/Config.hpp" -#include "libslic3r/PrintConfig.hpp" -#include "libslic3r/libslic3r.h" - -#include "Field.hpp" - -// Translate the ifdef -#ifdef __WXOSX__ - #define wxOSX true -#else - #define wxOSX false -#endif - -#define BORDER(a, b) ((wxOSX ? a : b)) - -namespace Slic3r { namespace GUI { - -enum ogDrawFlag{ - ogDEFAULT, - ogSIDE_OPTIONS_VERTICAL -}; - -/// Widget type describes a function object that returns a wxWindow (our widget) and accepts a wxWidget (parent window). -using widget_t = std::function<wxSizer*(wxWindow*)>;//!std::function<wxWindow*(wxWindow*)>; - -//auto default_label_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT); //GetSystemColour -//auto modified_label_clr = *new wxColour(254, 189, 101); - -/// Wraps a ConfigOptionDef and adds function object for creating a side_widget. -struct Option { - ConfigOptionDef opt { ConfigOptionDef() }; - t_config_option_key opt_id;//! {""}; - widget_t side_widget {nullptr}; - bool readonly {false}; - - Option(const ConfigOptionDef& _opt, t_config_option_key id) : - opt(_opt), opt_id(id) {} -}; -using t_option = std::unique_ptr<Option>; //! - -/// Represents option lines -class Line { -public: - wxString label {wxString("")}; - wxString label_tooltip {wxString("")}; - size_t full_width {0}; - wxSizer* sizer {nullptr}; - widget_t widget {nullptr}; - std::function<wxWindow*(wxWindow*)> near_label_widget{ nullptr }; - - void append_option(const Option& option) { - m_options.push_back(option); - } - void append_widget(const widget_t widget) { - m_extra_widgets.push_back(widget); - } - Line(wxString label, wxString tooltip) : - label(label), label_tooltip(tooltip) {} - - const std::vector<widget_t>& get_extra_widgets() const {return m_extra_widgets;} - const std::vector<Option>& get_options() const { return m_options; } - -private: - std::vector<Option> m_options;//! {std::vector<Option>()}; - std::vector<widget_t> m_extra_widgets;//! {std::vector<widget_t>()}; -}; - -using column_t = std::function<wxWindow*(wxWindow* parent, const Line&)>;//std::function<wxSizer*(const Line&)>; - -using t_optionfield_map = std::map<t_config_option_key, t_field>; -using t_opt_map = std::map< std::string, std::pair<std::string, int> >; - -class OptionsGroup { - wxStaticBox* stb; -public: - const bool staticbox {true}; - const wxString title {wxString("")}; - size_t label_width {200}; - wxSizer* sizer {nullptr}; - column_t extra_column {nullptr}; - t_change m_on_change {nullptr}; - std::function<DynamicPrintConfig()> m_get_initial_config{ nullptr }; - std::function<DynamicPrintConfig()> m_get_sys_config{ nullptr }; - std::function<bool()> have_sys_config{ nullptr }; - - wxFont sidetext_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; - wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; - int sidetext_width{ -1 }; - - /// Returns a copy of the pointer of the parent wxWindow. - /// Accessor function is because users are not allowed to change the parent - /// but defining it as const means a lot of const_casts to deal with wx functions. - inline wxWindow* parent() const { -#ifdef __WXGTK__ - return m_panel; -#else - return m_parent; -#endif /* __WXGTK__ */ - } -#ifdef __WXGTK__ - wxWindow* get_parent() const { - return m_parent; - } -#endif /* __WXGTK__ */ - - void append_line(const Line& line, wxStaticText** colored_Label = nullptr); - Line create_single_option_line(const Option& option) const; - void append_single_option_line(const Option& option) { append_line(create_single_option_line(option)); } - - // return a non-owning pointer reference - inline Field* get_field(const t_config_option_key& id) const{ - if (m_fields.find(id) == m_fields.end()) return nullptr; - return m_fields.at(id).get(); - } - bool set_value(const t_config_option_key& id, const boost::any& value, bool change_event = false) { - if (m_fields.find(id) == m_fields.end()) return false; - m_fields.at(id)->set_value(value, change_event); - return true; - } - boost::any get_value(const t_config_option_key& id) { - boost::any out; - if (m_fields.find(id) == m_fields.end()) ; - else - out = m_fields.at(id)->get_value(); - return out; - } - - bool set_side_text(const t_config_option_key& opt_key, const wxString& side_text) { - if (m_fields.find(opt_key) == m_fields.end()) return false; - auto st = m_fields.at(opt_key)->m_side_text; - if (!st) return false; - st->SetLabel(side_text); - return true; - } - - void set_name(const wxString& new_name) { - stb->SetLabel(new_name); - } - - inline void enable() { for (auto& field : m_fields) field.second->enable(); } - inline void disable() { for (auto& field : m_fields) field.second->disable(); } - void set_flag(ogDrawFlag flag) { m_flag = flag; } - void set_grid_vgap(int gap) { m_grid_sizer->SetVGap(gap); } - - void set_show_modified_btns_val(bool show) { - m_show_modified_btns = show; - } - - OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, - ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : - m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), - staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){ - if (staticbox) { - stb = new wxStaticBox(_parent, wxID_ANY, title); - stb->SetFont(bold_font()); - } - sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL)); - auto num_columns = 1U; - if (label_width != 0) num_columns++; - if (extra_column != nullptr) num_columns++; - m_grid_sizer = new wxFlexGridSizer(0, num_columns, 1,0); - static_cast<wxFlexGridSizer*>(m_grid_sizer)->SetFlexibleDirection(wxBOTH/*wxHORIZONTAL*/); - static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(label_width != 0); -#ifdef __WXGTK__ - m_panel = new wxPanel( _parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - sizer->Fit(m_panel); - sizer->Add(m_panel, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5); -#else - sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX||!staticbox ? 0: 5); -#endif /* __WXGTK__ */ - } - - wxGridSizer* get_grid_sizer(){ return m_grid_sizer; } - -protected: - std::map<t_config_option_key, Option> m_options; - wxWindow* m_parent {nullptr}; - - /// Field list, contains unique_ptrs of the derived type. - /// using types that need to know what it is beyond the public interface - /// need to cast based on the related ConfigOptionDef. - t_optionfield_map m_fields; - bool m_disabled {false}; - wxGridSizer* m_grid_sizer {nullptr}; - // "true" if option is created in preset tabs - bool m_show_modified_btns{ false }; - - ogDrawFlag m_flag{ ogDEFAULT }; - - // This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox - // Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel - // inside it before you insert the other controls. -#ifdef __WXGTK__ - wxPanel* m_panel {nullptr}; -#endif /* __WXGTK__ */ - - /// Generate a wxSizer or wxWindow from a configuration option - /// Precondition: opt resolves to a known ConfigOption - /// Postcondition: fields contains a wx gui object. - const t_field& build_field(const t_config_option_key& id, const ConfigOptionDef& opt, wxStaticText* label = nullptr); - const t_field& build_field(const t_config_option_key& id, wxStaticText* label = nullptr); - const t_field& build_field(const Option& opt, wxStaticText* label = nullptr); - void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field); - - virtual void on_kill_focus (){}; - virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value); - virtual void back_to_initial_value(const std::string& opt_key){} - virtual void back_to_sys_value(const std::string& opt_key){} -}; - -class ConfigOptionsGroup: public OptionsGroup { -public: - ConfigOptionsGroup( wxWindow* parent, const wxString& title, DynamicPrintConfig* _config = nullptr, - bool is_tab_opt = false, ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) : - OptionsGroup(parent, title, is_tab_opt, flag, extra_clmn), m_config(_config) {} - - /// reference to libslic3r config, non-owning pointer (?). - DynamicPrintConfig* m_config {nullptr}; - bool m_full_labels {0}; - t_opt_map m_opt_map; - - Option get_option(const std::string& opt_key, int opt_index = -1); - Line create_single_option_line(const std::string& title, int idx = -1) /*const*/{ - Option option = get_option(title, idx); - return OptionsGroup::create_single_option_line(option); - } - void append_single_option_line(const Option& option) { - OptionsGroup::append_single_option_line(option); - } - void append_single_option_line(const std::string title, int idx = -1) - { - Option option = get_option(title, idx); - append_single_option_line(option); - } - - void on_change_OG(const t_config_option_key& opt_id, const boost::any& value) override; - void back_to_initial_value(const std::string& opt_key) override; - void back_to_sys_value(const std::string& opt_key) override; - void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key); - void on_kill_focus() override{ reload_config();} - void reload_config(); - boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize); - // return option value from config - boost::any get_config_value(const DynamicPrintConfig& config, const std::string& opt_key, int opt_index = -1); - Field* get_fieldc(const t_config_option_key& opt_key, int opt_index); -}; - -// Static text shown among the options. -class ogStaticText :public wxStaticText{ -public: - ogStaticText() {} - ogStaticText(wxWindow* parent, const char *text) : wxStaticText(parent, wxID_ANY, text, wxDefaultPosition, wxDefaultSize){} - ~ogStaticText(){} - - void SetText(const wxString& value, bool wrap = true); -}; - -}} - -#endif /* slic3r_OptionsGroup_hpp_ */ diff --git a/xs/src/slic3r/GUI/Preferences.cpp b/xs/src/slic3r/GUI/Preferences.cpp deleted file mode 100644 index 89a8ead92..000000000 --- a/xs/src/slic3r/GUI/Preferences.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "Preferences.hpp" -#include "AppConfig.hpp" -#include "OptionsGroup.hpp" - -namespace Slic3r { -namespace GUI { - -PreferencesDialog::PreferencesDialog(wxWindow* parent, int event_preferences) : - wxDialog(parent, wxID_ANY, _(L("Preferences")), wxDefaultPosition, wxDefaultSize), - m_event_preferences(event_preferences) { - build(); - } - -void PreferencesDialog::build() -{ - auto app_config = get_app_config(); - m_optgroup = std::make_shared<ConfigOptionsGroup>(this, _(L("General"))); - m_optgroup->label_width = 400; - m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){ - m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0"; - }; - - // TODO -// $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( -// opt_id = > 'version_check', -// type = > 'bool', -// label = > 'Check for updates', -// tooltip = > 'If this is enabled, Slic3r will check for updates daily and display a reminder if a newer version is available.', -// default = > $app_config->get("version_check") // 1, -// readonly = > !wxTheApp->have_version_check, -// )); - - ConfigOptionDef def; - def.label = L("Remember output directory"); - def.type = coBool; - def.tooltip = L("If this is enabled, Slic3r will prompt the last output directory " - "instead of the one containing the input files."); - def.default_value = new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path")[0] == '1' : true }; // 1; - Option option(def, "remember_output_path"); - m_optgroup->append_single_option_line(option); - - def.label = L("Auto-center parts"); - def.type = coBool; - def.tooltip = L("If this is enabled, Slic3r will auto-center objects " - "around the print bed center."); - def.default_value = new ConfigOptionBool{ app_config->get("autocenter")[0] == '1' }; // 1; - option = Option (def,"autocenter"); - m_optgroup->append_single_option_line(option); - - def.label = L("Background processing"); - def.type = coBool; - def.tooltip = L("If this is enabled, Slic3r will pre-process objects as soon " - "as they\'re loaded in order to save time when exporting G-code."); - def.default_value = new ConfigOptionBool{ app_config->get("background_processing")[0] == '1' }; // 1; - option = Option (def,"background_processing"); - m_optgroup->append_single_option_line(option); - - // Please keep in sync with ConfigWizard - def.label = L("Check for application updates"); - def.type = coBool; - def.tooltip = L("If enabled, Slic3r checks for new versions of Slic3r PE 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.default_value = new ConfigOptionBool(app_config->get("version_check") == "1"); - option = Option (def, "version_check"); - m_optgroup->append_single_option_line(option); - - // Please keep in sync with ConfigWizard - def.label = L("Update built-in Presets automatically"); - def.type = coBool; - 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.default_value = new ConfigOptionBool(app_config->get("preset_update") == "1"); - option = Option (def, "preset_update"); - m_optgroup->append_single_option_line(option); - - def.label = L("Suppress \" - default - \" presets"); - def.type = coBool; - def.tooltip = L("Suppress \" - default - \" presets in the Print / Filament / Printer " - "selections once there are any other valid presets available."); - def.default_value = new ConfigOptionBool{ app_config->get("no_defaults")[0] == '1' }; // 1; - option = Option (def,"no_defaults"); - m_optgroup->append_single_option_line(option); - - def.label = L("Show incompatible print and filament presets"); - def.type = coBool; - def.tooltip = L("When checked, the print and filament presets are shown in the preset editor " - "even if they are marked as incompatible with the active printer"); - def.default_value = new ConfigOptionBool{ app_config->get("show_incompatible_presets")[0] == '1' }; // 1; - option = Option (def,"show_incompatible_presets"); - m_optgroup->append_single_option_line(option); - - def.label = L("Use legacy OpenGL 1.1 rendering"); - def.type = coBool; - def.tooltip = L("If you have rendering issues caused by a buggy OpenGL 2.0 driver, " - "you may try to check this checkbox. This will disable the layer height " - "editing and anti aliasing, so it is likely better to upgrade your graphics driver."); - def.default_value = new ConfigOptionBool{ app_config->get("use_legacy_opengl")[0] == '1' }; // 1; - option = Option (def,"use_legacy_opengl"); - m_optgroup->append_single_option_line(option); - - auto sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - - auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL); - wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this)); - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); - sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); - - SetSizer(sizer); - sizer->SetSizeHints(this); -} - -void PreferencesDialog::accept() -{ - if (m_values.find("no_defaults") != m_values.end()|| - m_values.find("use_legacy_opengl")!= m_values.end()) { - warning_catcher(this, _(L("You need to restart Slic3r to make the changes effective."))); - } - - auto app_config = get_app_config(); - for (std::map<std::string, std::string>::iterator it = m_values.begin(); it != m_values.end(); ++it) { - app_config->set(it->first, it->second); - } - - EndModal(wxID_OK); - Close(); // needed on Linux - - // Nothify the UI to update itself from the ini file. - if (m_event_preferences > 0) { - wxCommandEvent event(m_event_preferences); - get_app()->ProcessEvent(event); - } -} - -} // GUI -} // Slic3r
\ No newline at end of file diff --git a/xs/src/slic3r/GUI/Preferences.hpp b/xs/src/slic3r/GUI/Preferences.hpp deleted file mode 100644 index d01d78b70..000000000 --- a/xs/src/slic3r/GUI/Preferences.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef slic3r_Preferences_hpp_ -#define slic3r_Preferences_hpp_ - -#include "GUI.hpp" - -#include <wx/dialog.h> -#include <map> - -namespace Slic3r { -namespace GUI { - -class ConfigOptionsGroup; - -class PreferencesDialog : public wxDialog -{ - std::map<std::string, std::string> m_values; - std::shared_ptr<ConfigOptionsGroup> m_optgroup; - int m_event_preferences; -public: - PreferencesDialog(wxWindow* parent, int event_preferences); - ~PreferencesDialog(){ } - - void build(); - void accept(); -}; - -} // GUI -} // Slic3r - - -#endif /* slic3r_Preferences_hpp_ */ diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp deleted file mode 100644 index 9911caa5b..000000000 --- a/xs/src/slic3r/GUI/Preset.cpp +++ /dev/null @@ -1,1019 +0,0 @@ -//#undef NDEBUG -#include <cassert> - -#include "Preset.hpp" -#include "AppConfig.hpp" -#include "BitmapCache.hpp" - -#include <fstream> -#include <stdexcept> -#include <boost/format.hpp> -#include <boost/filesystem.hpp> -#include <boost/filesystem/fstream.hpp> -#include <boost/algorithm/string/predicate.hpp> - -#include <boost/nowide/cenv.hpp> -#include <boost/nowide/cstdio.hpp> -#include <boost/nowide/fstream.hpp> -#include <boost/property_tree/ini_parser.hpp> -#include <boost/property_tree/ptree.hpp> -#include <boost/locale.hpp> -#include <boost/log/trivial.hpp> - -#include <wx/image.h> -#include <wx/choice.h> -#include <wx/bmpcbox.h> -#include <wx/wupdlock.h> - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/Utils.hpp" -#include "../../libslic3r/PlaceholderParser.hpp" - -using boost::property_tree::ptree; - -namespace Slic3r { - -ConfigFileType guess_config_file_type(const ptree &tree) -{ - size_t app_config = 0; - size_t bundle = 0; - size_t config = 0; - for (const ptree::value_type &v : tree) { - if (v.second.empty()) { - if (v.first == "background_processing" || - v.first == "last_output_path" || - v.first == "no_controller" || - v.first == "no_defaults") - ++ app_config; - else if (v.first == "nozzle_diameter" || - v.first == "filament_diameter") - ++ config; - } else if (boost::algorithm::starts_with(v.first, "print:") || - boost::algorithm::starts_with(v.first, "filament:") || - boost::algorithm::starts_with(v.first, "printer:") || - v.first == "settings") - ++ bundle; - else if (v.first == "presets") { - ++ app_config; - ++ bundle; - } else if (v.first == "recent") { - for (auto &kvp : v.second) - if (kvp.first == "config_directory" || kvp.first == "skein_directory") - ++ app_config; - } - } - return (app_config > bundle && app_config > config) ? CONFIG_FILE_TYPE_APP_CONFIG : - (bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG; -} - - -VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool load_all) -{ - ptree tree; - boost::filesystem::ifstream ifs(path); - boost::property_tree::read_ini(ifs, tree); - return VendorProfile::from_ini(tree, path, load_all); -} - -VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all) -{ - static const std::string printer_model_key = "printer_model:"; - const std::string id = path.stem().string(); - - if (! boost::filesystem::exists(path)) { - throw std::runtime_error((boost::format("Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`.") % id % path).str()); - } - - VendorProfile res(id); - - auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator - { - auto res = tree.find(key); - if (res == tree.not_found()) { - throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str()); - } - return res; - }; - - const auto &vendor_section = get_or_throw(tree, "vendor")->second; - res.name = get_or_throw(vendor_section, "name")->second.data(); - - auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data(); - auto config_version = Semver::parse(config_version_str); - if (! config_version) { - throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str()); - } else { - res.config_version = std::move(*config_version); - } - - auto config_update_url = vendor_section.find("config_update_url"); - if (config_update_url != vendor_section.not_found()) { - res.config_update_url = config_update_url->second.data(); - } - - if (! load_all) { - return res; - } - - for (auto §ion : tree) { - if (boost::starts_with(section.first, printer_model_key)) { - VendorProfile::PrinterModel model; - model.id = section.first.substr(printer_model_key.size()); - model.name = section.second.get<std::string>("name", model.id); - auto technology_field = section.second.get<std::string>("technology", "FFF"); - if (! ConfigOptionEnum<PrinterTechnology>::from_string(technology_field, model.technology)) { - BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field; - model.technology = ptFFF; - } - section.second.get<std::string>("variants", ""); - const auto variants_field = section.second.get<std::string>("variants", ""); - std::vector<std::string> variants; - if (Slic3r::unescape_strings_cstyle(variants_field, variants)) { - for (const std::string &variant_name : variants) { - if (model.variant(variant_name) == nullptr) - model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name)); - } - } else { - BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field; - } - if (! model.id.empty() && ! model.variants.empty()) - res.models.push_back(std::move(model)); - } - } - - return res; -} - - -// Suffix to be added to a modified preset name in the combo box. -static std::string g_suffix_modified = " (modified)"; -const std::string& Preset::suffix_modified() -{ - return g_suffix_modified; -} - -void Preset::update_suffix_modified() -{ - g_suffix_modified = (" (" + _(L("modified")) + ")").ToUTF8().data(); -} -// Remove an optional "(modified)" suffix from a name. -// This converts a UI name to a unique preset identifier. -std::string Preset::remove_suffix_modified(const std::string &name) -{ - return boost::algorithm::ends_with(name, g_suffix_modified) ? - name.substr(0, name.size() - g_suffix_modified.size()) : - name; -} - -void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extruders) -{ - const auto &defaults = FullPrintConfig::defaults(); - for (const std::string &key : Preset::nozzle_options()) { - auto *opt = config.option(key, false); - assert(opt != nullptr); - assert(opt->is_vector()); - if (opt != nullptr && opt->is_vector() && key != "default_filament_profile") - static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key)); - } -} - -// Update new extruder fields at the printer profile. -void Preset::normalize(DynamicPrintConfig &config) -{ - auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter")); - if (nozzle_diameter != nullptr) - // Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values. - set_num_extruders(config, (unsigned int)nozzle_diameter->values.size()); - if (config.option("filament_diameter") != nullptr) { - // This config contains single or multiple filament presets. - // Ensure that the filament preset vector options contain the correct number of values. - size_t n = (nozzle_diameter == nullptr) ? 1 : nozzle_diameter->values.size(); - const auto &defaults = FullPrintConfig::defaults(); - for (const std::string &key : Preset::filament_options()) { - if (key == "compatible_printers") - continue; - auto *opt = config.option(key, false); - /*assert(opt != nullptr); - assert(opt->is_vector());*/ - if (opt != nullptr && opt->is_vector()) - static_cast<ConfigOptionVectorBase*>(opt)->resize(n, defaults.option(key)); - } - // The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately. - for (const std::string &key : { "filament_settings_id" }) { - auto *opt = config.option(key, false); - assert(opt != nullptr); - assert(opt->type() == coStrings); - if (opt != nullptr && opt->type() == coStrings) - static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string()); - } - } -} - -DynamicPrintConfig& Preset::load(const std::vector<std::string> &keys, const StaticPrintConfig &defaults) -{ - // Set the configuration from the defaults. - this->config.apply_only(defaults, keys.empty() ? defaults.keys() : keys); - if (! this->is_default) { - // Load the preset file, apply preset values on top of defaults. - try { - this->config.load_from_ini(this->file); - Preset::normalize(this->config); - } catch (const std::ifstream::failure &err) { - throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + this->file + "\n\tReason: " + err.what()); - } catch (const std::runtime_error &err) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + this->file + "\n\tReason: " + err.what()); - } - } - this->loaded = true; - return this->config; -} - -void Preset::save() -{ - this->config.save(this->file); -} - -// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty. -std::string Preset::label() const -{ - return this->name + (this->is_dirty ? g_suffix_modified : ""); -} - -bool Preset::is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const -{ - auto &condition = this->compatible_printers_condition(); - auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(this->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); - } 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()); - 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) != - compatible_printers->values.end(); -} - -bool Preset::is_compatible_with_printer(const Preset &active_printer) const -{ - DynamicPrintConfig config; - config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name)); - const ConfigOption *opt = active_printer.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_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) -{ - return this->is_compatible = is_compatible_with_printer(active_printer, extra_config); -} - -void Preset::set_visible_from_appconfig(const AppConfig &app_config) -{ - if (vendor == nullptr) { return; } - const std::string &model = config.opt_string("printer_model"); - const std::string &variant = config.opt_string("printer_variant"); - if (model.empty() || variant.empty()) { return; } - is_visible = app_config.get_variant(vendor->id, model, variant); -} - -const std::vector<std::string>& Preset::print_options() -{ - static std::vector<std::string> s_opts { - "layer_height", "first_layer_height", "perimeters", "spiral_vase", "top_solid_layers", "bottom_solid_layers", - "extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "external_fill_pattern", - "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle", - "solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first", "max_print_speed", - "max_volumetric_speed", "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed", - "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", - "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", - "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", - "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", - "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", - "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", - "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", - "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", - "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", - "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", - "top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects", - "elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y", - "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "single_extruder_multi_material_priming", - "compatible_printers", "compatible_printers_condition","inherits" - }; - return s_opts; -} - -const std::vector<std::string>& Preset::filament_options() -{ - static std::vector<std::string> s_opts { - "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", - "extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time", - "filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves", - "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower", - "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", - "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", - "start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits" - }; - return s_opts; -} - -const std::vector<std::string>& Preset::printer_options() -{ - static std::vector<std::string> s_opts; - if (s_opts.empty()) { - s_opts = { - "printer_technology", - "bed_shape", "z_offset", "gcode_flavor", "use_relative_e_distances", "serial_port", "serial_speed", - "use_firmware_retraction", "use_volumetric_e", "variable_layer_height", - "host_type", "print_host", "printhost_apikey", "printhost_cafile", - "single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode", - "between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction", - "cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits", - "remaining_times", "silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting", - "machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e", - "machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e", - "machine_min_extruding_rate", "machine_min_travel_rate", - "machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e" - }; - s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end()); - } - return s_opts; -} - -// The following nozzle options of a printer profile will be adjusted to match the size -// of the nozzle_diameter vector. -const std::vector<std::string>& Preset::nozzle_options() -{ - // ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings - static std::vector<std::string> s_opts { - "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset", - "retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed", - "retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe", - "retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour", - "default_filament_profile" - }; - return s_opts; -} - -const std::vector<std::string>& Preset::sla_printer_options() -{ - static std::vector<std::string> s_opts; - if (s_opts.empty()) { - s_opts = { - "printer_technology", - "bed_shape", "max_print_height", - "display_width", "display_height", "display_pixels_x", "display_pixels_y", - "printer_correction", - "printer_notes", - "inherits" - }; - } - return s_opts; -} - -const std::vector<std::string>& Preset::sla_material_options() -{ - static std::vector<std::string> s_opts; - if (s_opts.empty()) { - s_opts = { - "layer_height", "initial_layer_height", - "exposure_time", "initial_exposure_time", - "material_correction_printing", "material_correction_curing", - "material_notes", - "compatible_printers", - "compatible_printers_condition", "inherits" - }; - } - return s_opts; -} - -PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : - m_type(type), - m_edited_preset(type, "", false), - m_idx_selected(0), - m_bitmap_main_frame(new wxBitmap), - m_bitmap_cache(new GUI::BitmapCache) -{ - // Insert just the default preset. - this->add_default_preset(keys, defaults, default_name); - m_edited_preset.config.apply(m_presets.front().config); -} - -PresetCollection::~PresetCollection() -{ - delete m_bitmap_main_frame; - m_bitmap_main_frame = nullptr; - delete m_bitmap_cache; - m_bitmap_cache = nullptr; -} - -void PresetCollection::reset(bool delete_files) -{ - if (m_presets.size() > m_num_default_presets) { - if (delete_files) { - // Erase the preset files. - for (Preset &preset : m_presets) - if (! preset.is_default && ! preset.is_external && ! preset.is_system) - boost::nowide::remove(preset.file.c_str()); - } - // Don't use m_presets.resize() here as it requires a default constructor for Preset. - m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end()); - this->select_preset(0); - } -} - -void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name) -{ - // Insert just the default preset. - m_presets.emplace_back(Preset(this->type(), preset_name, true)); - m_presets.back().load(keys, defaults); - ++ m_num_default_presets; -} - -// Load all presets found in dir_path. -// Throws an exception on error. -void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir) -{ - boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred(); - m_dir_path = dir.string(); - t_config_option_keys keys = this->default_preset().config.keys(); - std::string errors_cummulative; - for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) { - std::string name = dir_entry.path().filename().string(); - // Remove the .ini suffix. - name.erase(name.size() - 4); - if (this->find_preset(name, false)) { - // This happens when there's is a preset (most likely legacy one) with the same name as a system preset - // that's already been loaded from a bundle. - BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name; - continue; - } - try { - Preset preset(m_type, name, false); - preset.file = dir_entry.path().string(); - //FIXME One should initialize with SLAFullPrintConfig for the SLA profiles! - preset.load(keys, static_cast<const HostConfig&>(FullPrintConfig::defaults())); - m_presets.emplace_back(preset); - } catch (const std::runtime_error &err) { - errors_cummulative += err.what(); - errors_cummulative += "\n"; - } - } - std::sort(m_presets.begin() + m_num_default_presets, m_presets.end()); - this->select_preset(first_visible_idx()); - if (! errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); -} - -// Load a preset from an already parsed config file, insert it into the sorted sequence of presets -// and select it, losing previous modifications. -Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select) -{ - DynamicPrintConfig cfg(this->default_preset().config); - cfg.apply_only(config, cfg.keys(), true); - return this->load_preset(path, name, std::move(cfg), select); -} - -static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const DynamicPrintConfig &cfg2) -{ - t_config_option_keys diff = cfg1.diff(cfg2); - // Following keys are used by the UI, not by the slicing core, therefore they are not important - // when comparing profiles for equality. Ignore them. - for (const char *key : { "compatible_printers", "compatible_printers_condition", "inherits", - "print_settings_id", "filament_settings_id", "printer_settings_id", - "printer_model", "printer_variant", "default_print_profile", "default_filament_profile" }) - diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end()); - // Preset with the same name as stored inside the config exists. - return diff.empty(); -} - -// Load a preset from an already parsed config file, insert it into the sorted sequence of presets -// and select it, losing previous modifications. -// In case -Preset& PresetCollection::load_external_preset( - // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) - const std::string &path, - // Name of the profile, derived from the source file name. - const std::string &name, - // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. - const std::string &original_name, - // Config to initialize the preset from. - const DynamicPrintConfig &config, - // Select the preset after loading? - bool select) -{ - // Load the preset over a default preset, so that the missing fields are filled in from the default preset. - DynamicPrintConfig cfg(this->default_preset().config); - cfg.apply_only(config, cfg.keys(), true); - // Is there a preset already loaded with the name stored inside the config? - std::deque<Preset>::iterator it = this->find_preset_internal(original_name); - if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) { - // The preset exists and it matches the values stored inside config. - if (select) - this->select_preset(it - m_presets.begin()); - return *it; - } - // Update the "inherits" field. - std::string &inherits = Preset::inherits(cfg); - if (it != m_presets.end() && inherits.empty()) { - // There is a profile with the same name already loaded. Should we update the "inherits" field? - if (it->vendor == nullptr) - inherits = it->inherits(); - else - inherits = it->name; - } - // The external preset does not match an internal preset, load the external preset. - std::string new_name; - for (size_t idx = 0;; ++ idx) { - std::string suffix; - if (original_name.empty()) { - if (idx > 0) - suffix = " (" + std::to_string(idx) + ")"; - } else { - if (idx == 0) - suffix = " (" + original_name + ")"; - else - suffix = " (" + original_name + "-" + std::to_string(idx) + ")"; - } - new_name = name + suffix; - it = this->find_preset_internal(new_name); - if (it == m_presets.end() || it->name != new_name) - // Unique profile name. Insert a new profile. - break; - if (profile_print_params_same(it->config, cfg)) { - // The preset exists and it matches the values stored inside config. - if (select) - this->select_preset(it - m_presets.begin()); - return *it; - } - // Form another profile name. - } - // Insert a new profile. - Preset &preset = this->load_preset(path, new_name, std::move(cfg), select); - preset.is_external = true; - if (&this->get_selected_preset() == &preset) - this->get_edited_preset().is_external = true; - - return preset; -} - -Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select) -{ - auto it = this->find_preset_internal(name); - if (it == m_presets.end() || it->name != name) { - // The preset was not found. Create a new preset. - it = m_presets.emplace(it, Preset(m_type, name, false)); - } - Preset &preset = *it; - preset.file = path; - preset.config = std::move(config); - preset.loaded = true; - preset.is_dirty = false; - if (select) - this->select_preset_by_name(name, true); - return preset; -} - -void PresetCollection::save_current_preset(const std::string &new_name) -{ - // 1) Find the preset with a new_name or create a new one, - // initialize it with the edited config. - auto it = this->find_preset_internal(new_name); - if (it != m_presets.end() && it->name == new_name) { - // Preset with the same name found. - Preset &preset = *it; - if (preset.is_default || preset.is_external || preset.is_system) - // Cannot overwrite the default preset. - return; - // Overwriting an existing preset. - preset.config = std::move(m_edited_preset.config); - } else { - // Creating a new preset. - Preset &preset = *m_presets.insert(it, m_edited_preset); - std::string &inherits = preset.inherits(); - std::string old_name = preset.name; - preset.name = new_name; - preset.file = this->path_from_name(new_name); - preset.vendor = nullptr; - if (preset.is_system) { - // Inheriting from a system preset. - inherits = /* preset.vendor->name + "/" + */ old_name; - } else if (inherits.empty()) { - // Inheriting from a user preset. Link the new preset to the old preset. - // inherits = old_name; - } else { - // Inherited from a user preset. Just maintain the "inherited" flag, - // meaning it will inherit from either the system preset, or the inherited user preset. - } - preset.is_default = false; - preset.is_system = false; - preset.is_external = false; - } - // 2) Activate the saved preset. - this->select_preset_by_name(new_name, true); - // 2) Store the active preset to disk. - this->get_selected_preset().save(); -} - -void PresetCollection::delete_current_preset() -{ - const Preset &selected = this->get_selected_preset(); - if (selected.is_default) - return; - if (! selected.is_external && ! selected.is_system) { - // Erase the preset file. - boost::nowide::remove(selected.file.c_str()); - } - // Remove the preset from the list. - m_presets.erase(m_presets.begin() + m_idx_selected); - // Find the next visible preset. - size_t new_selected_idx = m_idx_selected; - if (new_selected_idx < m_presets.size()) - for (; new_selected_idx < m_presets.size() && ! m_presets[new_selected_idx].is_visible; ++ new_selected_idx) ; - if (new_selected_idx == m_presets.size()) - for (--new_selected_idx; new_selected_idx > 0 && !m_presets[new_selected_idx].is_visible; --new_selected_idx); - this->select_preset(new_selected_idx); -} - -bool PresetCollection::load_bitmap_default(const std::string &file_name) -{ - return m_bitmap_main_frame->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG); -} - -const Preset* PresetCollection::get_selected_preset_parent() const -{ - const std::string &inherits = this->get_edited_preset().inherits(); - if (inherits.empty()) - return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; - const Preset* preset = this->find_preset(inherits, false); - return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset; -} - -const Preset* PresetCollection::get_preset_parent(const Preset& child) const -{ - const std::string &inherits = child.inherits(); - if (inherits.empty()) -// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; - return nullptr; - const Preset* preset = this->find_preset(inherits, false); - return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset; -} - -const std::string& PresetCollection::get_suffix_modified() { - return g_suffix_modified; -} - -// Return a preset by its name. If the preset is active, a temporary copy is returned. -// If a preset is not found by its name, null is returned. -Preset* PresetCollection::find_preset(const std::string &name, bool first_visible_if_not_found) -{ - Preset key(m_type, name, false); - auto it = this->find_preset_internal(name); - // Ensure that a temporary copy is returned if the preset found is currently selected. - return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin()) : - first_visible_if_not_found ? &this->first_visible() : nullptr; -} - -// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. -size_t PresetCollection::first_visible_idx() const -{ - size_t idx = m_default_suppressed ? m_num_default_presets : 0; - for (; idx < this->m_presets.size(); ++ idx) - if (m_presets[idx].is_visible) - break; - if (idx == m_presets.size()) - idx = 0; - return idx; -} - -void PresetCollection::set_default_suppressed(bool default_suppressed) -{ - if (m_default_suppressed != default_suppressed) { - m_default_suppressed = default_suppressed; - m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > m_num_default_presets && m_idx_selected > 0); - } -} - -size_t PresetCollection::update_compatible_with_printer_internal(const Preset &active_printer, 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"); - 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_with_printer(active_printer, &config) && - selected && unselect_if_incompatible) - m_idx_selected = (size_t)-1; - if (selected) - preset_selected.is_compatible = preset_edited.is_compatible; - } - return m_idx_selected; -} - -// Save the preset under a new name. If the name is different from the old one, -// a new preset is stored into the list of presets. -// All presets are marked as not modified and the new preset is activated. -//void PresetCollection::save_current_preset(const std::string &new_name); - -// Delete the current preset, activate the first visible preset. -//void PresetCollection::delete_current_preset(); - -// Update the wxChoice UI component from this list of presets. -// Hide the -void PresetCollection::update_platter_ui(wxBitmapComboBox *ui) -{ - if (ui == nullptr) - return; - // Otherwise fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = 0; - - const Preset &selected_preset = this->get_selected_preset(); - // Show wide icons if the currently selected preset is not compatible with the current printer, - // and draw a red flag in front of the selected preset. - bool wide_icons = !selected_preset.is_compatible && m_bitmap_incompatible != nullptr; - - std::map<wxString, wxBitmap*> nonsys_presets; - wxString selected = ""; - if (!this->m_presets.front().is_visible) - ui->Append("------- " +_(L("System presets")) + " -------", wxNullBitmap); - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { - const Preset &preset = this->m_presets[i]; - if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected)) - continue; - std::string bitmap_key = ""; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector<wxBitmap> bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmap_cache->mkclear(16, 16) : *m_bitmap_incompatible); - // Paint the color bars. - bmps.emplace_back(m_bitmap_cache->mkclear(4, 16)); - bmps.emplace_back(*m_bitmap_main_frame); - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmap_cache->mkclear(6, 16)); - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - - if (preset.is_default || preset.is_system){ - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); - if (i == m_idx_selected) - selected_preset_item = ui->GetCount() - 1; - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) - selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - } - if (i + 1 == m_num_default_presets) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); - } - if (!nonsys_presets.empty()) - { - ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap); - for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected) - selected_preset_item = ui->GetCount() - 1; - } - } - - ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); - ui->Thaw(); -} - -size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible) -{ - if (ui == nullptr) - return 0; - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = 0; - - std::map<wxString, wxBitmap*> nonsys_presets; - wxString selected = ""; - if (!this->m_presets.front().is_visible) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); - for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) { - const Preset &preset = this->m_presets[i]; - if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected)) - continue; - std::string bitmap_key = "tab"; - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - wxBitmap *bmp = m_bitmap_cache->find(bitmap_key); - if (bmp == nullptr) { - // Create the bitmap with color bars. - std::vector<wxBitmap> bmps; - const wxBitmap* tmp_bmp = preset.is_compatible ? m_bitmap_compatible : m_bitmap_incompatible; - bmps.emplace_back((tmp_bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *tmp_bmp); - // Paint a lock at the system presets. - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(16, 16)); - bmp = m_bitmap_cache->insert(bitmap_key, bmps); - } - - if (preset.is_default || preset.is_system){ - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), - (bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp); - if (i == m_idx_selected) - selected_preset_item = ui->GetCount() - 1; - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()), bmp/*preset.is_compatible*/); - if (i == m_idx_selected) - selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()); - } - if (i + 1 == m_num_default_presets) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); - } - if (!nonsys_presets.empty()) - { - ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap); - for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected) - selected_preset_item = ui->GetCount() - 1; - } - } - ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); - ui->Thaw(); - return selected_preset_item; -} - -// Update a dirty floag of the current preset, update the labels of the UI component accordingly. -// Return true if the dirty flag changed. -bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui) -{ - wxWindowUpdateLocker noUpdates(ui); - // 1) Update the dirty flag of the current preset. - bool was_dirty = this->get_selected_preset().is_dirty; - bool is_dirty = current_is_dirty(); - this->get_selected_preset().is_dirty = is_dirty; - this->get_edited_preset().is_dirty = is_dirty; - // 2) Update the labels. - for (unsigned int ui_id = 0; ui_id < ui->GetCount(); ++ ui_id) { - std::string old_label = ui->GetString(ui_id).utf8_str().data(); - std::string preset_name = Preset::remove_suffix_modified(old_label); - const Preset *preset = this->find_preset(preset_name, false); - assert(preset != nullptr); - if (preset != nullptr) { - std::string new_label = preset->is_dirty ? preset->name + g_suffix_modified : preset->name; - if (old_label != new_label) - ui->SetString(ui_id, wxString::FromUTF8(new_label.c_str())); - } - } -#ifdef __APPLE__ - // wxWidgets on OSX do not upload the text of the combo box line automatically. - // Force it to update by re-selecting. - ui->SetSelection(ui->GetSelection()); -#endif /* __APPLE __ */ - return was_dirty != is_dirty; -} - -std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/) -{ - std::vector<std::string> changed; - if (edited != nullptr && reference != nullptr) { - changed = deep_compare ? - reference->config.deep_diff(edited->config) : - reference->config.diff(edited->config); - // The "compatible_printers" option key is handled differently from the others: - // It is not mandatory. If the key is missing, it means it is compatible with any printer. - // If the key exists and it is empty, it means it is compatible with no printer. - std::initializer_list<const char*> optional_keys { "compatible_printers" }; - for (auto &opt_key : optional_keys) { - if (reference->config.has(opt_key) != edited->config.has(opt_key)) - changed.emplace_back(opt_key); - } - } - return changed; -} - -// Select a new preset. This resets all the edits done to the currently selected preset. -// If the preset with index idx does not exist, a first visible preset is selected. -Preset& PresetCollection::select_preset(size_t idx) -{ - for (Preset &preset : m_presets) - preset.is_dirty = false; - if (idx >= m_presets.size()) - idx = first_visible_idx(); - m_idx_selected = idx; - m_edited_preset = m_presets[idx]; - m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected == 0; - return m_presets[idx]; -} - -bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, bool force) -{ - std::string name = Preset::remove_suffix_modified(name_w_suffix); - // 1) Try to find the preset by its name. - auto it = this->find_preset_internal(name); - size_t idx = 0; - if (it != m_presets.end() && it->name == name && it->is_visible) - // Preset found by its name and it is visible. - idx = it - m_presets.begin(); - else { - // Find the first visible preset. - for (size_t i = m_default_suppressed ? m_num_default_presets : 0; i < m_presets.size(); ++ i) - if (m_presets[i].is_visible) { - idx = i; - break; - } - // If the first visible preset was not found, return the 0th element, which is the default preset. - } - - // 2) Select the new preset. - if (m_idx_selected != idx || force) { - this->select_preset(idx); - return true; - } - - return false; -} - -bool PresetCollection::select_preset_by_name_strict(const std::string &name) -{ - // 1) Try to find the preset by its name. - auto it = this->find_preset_internal(name); - size_t idx = (size_t)-1; - if (it != m_presets.end() && it->name == name && it->is_visible) - // Preset found by its name. - idx = it - m_presets.begin(); - // 2) Select the new preset. - if (idx != (size_t)-1) { - this->select_preset(idx); - return true; - } - m_idx_selected = idx; - return false; -} - -// Merge one vendor's presets with the other vendor's presets, report duplicates. -std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&other, const std::set<VendorProfile> &new_vendors) -{ - std::vector<std::string> duplicates; - for (Preset &preset : other.m_presets) { - if (preset.is_default || preset.is_external) - continue; - Preset key(m_type, preset.name); - auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key); - if (it == m_presets.end() || it->name != preset.name) { - if (preset.vendor != nullptr) { - // Re-assign a pointer to the vendor structure in the new PresetBundle. - auto it = new_vendors.find(*preset.vendor); - assert(it != new_vendors.end()); - preset.vendor = &(*it); - } - this->m_presets.emplace(it, std::move(preset)); - } else - duplicates.emplace_back(std::move(preset.name)); - } - return duplicates; -} - -std::string PresetCollection::name() const -{ - switch (this->type()) { - case Preset::TYPE_PRINT: return "print"; - case Preset::TYPE_FILAMENT: return "filament"; - case Preset::TYPE_PRINTER: return "printer"; - default: return "invalid"; - } -} - -// Generate a file path from a profile name. Add the ".ini" suffix if it is missing. -std::string PresetCollection::path_from_name(const std::string &new_name) const -{ - std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini"); - return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string(); -} - -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/Preset.hpp b/xs/src/slic3r/GUI/Preset.hpp deleted file mode 100644 index 821d7dc54..000000000 --- a/xs/src/slic3r/GUI/Preset.hpp +++ /dev/null @@ -1,444 +0,0 @@ -#ifndef slic3r_Preset_hpp_ -#define slic3r_Preset_hpp_ - -#include <deque> - -#include <boost/filesystem/path.hpp> -#include <boost/property_tree/ptree_fwd.hpp> - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/PrintConfig.hpp" -#include "slic3r/Utils/Semver.hpp" - -class wxBitmap; -class wxChoice; -class wxBitmapComboBox; -class wxItemContainer; - -namespace Slic3r { - -class AppConfig; -class PresetBundle; - -namespace GUI { - class BitmapCache; -} - -enum ConfigFileType -{ - CONFIG_FILE_TYPE_UNKNOWN, - CONFIG_FILE_TYPE_APP_CONFIG, - CONFIG_FILE_TYPE_CONFIG, - CONFIG_FILE_TYPE_CONFIG_BUNDLE, -}; - -extern ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree); - -class VendorProfile -{ -public: - std::string name; - std::string id; - Semver config_version; - std::string config_update_url; - - struct PrinterVariant { - PrinterVariant() {} - PrinterVariant(const std::string &name) : name(name) {} - std::string name; - }; - - struct PrinterModel { - PrinterModel() {} - std::string id; - std::string name; - PrinterTechnology technology; - std::vector<PrinterVariant> variants; - PrinterVariant* variant(const std::string &name) { - for (auto &v : this->variants) - if (v.name == name) - return &v; - return nullptr; - } - const PrinterVariant* variant(const std::string &name) const { return const_cast<PrinterModel*>(this)->variant(name); } - }; - std::vector<PrinterModel> models; - - VendorProfile() {} - VendorProfile(std::string id) : id(std::move(id)) {} - - static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true); - static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true); - - size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; } - - bool operator< (const VendorProfile &rhs) const { return this->id < rhs.id; } - bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; } -}; - -class Preset -{ -public: - enum Type - { - TYPE_INVALID, - TYPE_PRINT, - TYPE_FILAMENT, - TYPE_SLA_MATERIAL, - TYPE_PRINTER, - }; - - Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {} - - Type type = TYPE_INVALID; - - // The preset represents a "default" set of properties, - // pulled from the default values of the PrintConfig (see PrintConfigDef for their definitions). - bool is_default; - // External preset points to a configuration, which has been loaded but not imported - // into the Slic3r default configuration location. - bool is_external = false; - // System preset is read-only. - bool is_system = false; - // Preset is visible, if it is associated with a printer model / variant that is enabled in the AppConfig - // or if it has no printer model / variant association. - // Also the "default" preset is only visible, if it is the only preset in the list. - bool is_visible = true; - // Has this preset been modified? - bool is_dirty = false; - // Is this preset compatible with the currently active printer? - bool is_compatible = true; - - // Name of the preset, usually derived form the file name. - std::string name; - // File name of the preset. This could be a Print / Filament / Printer preset, - // or a Configuration file bundling the Print + Filament + Printer presets (in that case is_external and possibly is_system will be true), - // or it could be a G-code (again, is_external will be true). - std::string file; - // If this is a system profile, then there should be a vendor data available to display at the UI. - const VendorProfile *vendor = nullptr; - - // Has this profile been loaded? - bool loaded = false; - - // Configuration data, loaded from a file, or set from the defaults. - DynamicPrintConfig config; - - // Load this profile for the following keys only. - DynamicPrintConfig& load(const std::vector<std::string> &keys, const StaticPrintConfig &defaults); - - void save(); - - // Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty. - std::string label() const; - - // Set the is_dirty flag if the provided config is different from the active one. - void set_dirty(const DynamicPrintConfig &config) { this->is_dirty = ! this->config.diff(config).empty(); } - void set_dirty(bool dirty = true) { this->is_dirty = dirty; } - void reset_dirty() { this->is_dirty = false; } - - 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); } - const std::string& inherits() const { return Preset::inherits(const_cast<Preset*>(this)->config); } - - // Returns the "compatible_printers_condition". - static std::string& compatible_printers_condition(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("compatible_printers_condition", true)->value; } - std::string& compatible_printers_condition() { return Preset::compatible_printers_condition(this->config); } - const std::string& compatible_printers_condition() const { return Preset::compatible_printers_condition(const_cast<Preset*>(this)->config); } - - static PrinterTechnology& printer_technology(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value; } - PrinterTechnology& printer_technology() { return Preset::printer_technology(this->config); } - const PrinterTechnology& printer_technology() const { return Preset::printer_technology(const_cast<Preset*>(this)->config); } - - // Mark this preset as compatible if it is compatible with active_printer. - bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config); - - // Set is_visible according to application config - void set_visible_from_appconfig(const AppConfig &app_config); - - // Resize the extruder specific fields, initialize them with the content of the 1st extruder. - void set_num_extruders(unsigned int n) { set_num_extruders(this->config, n); } - - // Sort lexicographically by a preset name. The preset name shall be unique across a single PresetCollection. - bool operator<(const Preset &other) const { return this->name < other.name; } - - static const std::vector<std::string>& print_options(); - static const std::vector<std::string>& filament_options(); - // Printer options contain the nozzle options. - static const std::vector<std::string>& printer_options(); - // Nozzle options of the printer options. - static const std::vector<std::string>& nozzle_options(); - - static const std::vector<std::string>& sla_printer_options(); - static const std::vector<std::string>& sla_material_options(); - - static void update_suffix_modified(); - -protected: - friend class PresetCollection; - friend class PresetBundle; - static void normalize(DynamicPrintConfig &config); - // Resize the extruder specific vectors () - static void set_num_extruders(DynamicPrintConfig &config, unsigned int n); - static const std::string& suffix_modified(); - static std::string remove_suffix_modified(const std::string &name); -}; - -// Collections of presets of the same type (one of the Print, Filament or Printer type). -class PresetCollection -{ -public: - // Initialize the PresetCollection with the "- default -" preset. - PresetCollection(Preset::Type type, const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name = "- default -"); - ~PresetCollection(); - - typedef std::deque<Preset>::iterator Iterator; - typedef std::deque<Preset>::const_iterator ConstIterator; - Iterator begin() { return m_presets.begin() + m_num_default_presets; } - ConstIterator begin() const { return m_presets.begin() + m_num_default_presets; } - Iterator end() { return m_presets.end(); } - ConstIterator end() const { return m_presets.end(); } - - void reset(bool delete_files); - - Preset::Type type() const { return m_type; } - std::string name() const; - const std::deque<Preset>& operator()() const { return m_presets; } - - // Add default preset at the start of the collection, increment the m_default_preset counter. - void add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name); - - // Load ini files of the particular type from the provided directory path. - void load_presets(const std::string &dir_path, const std::string &subdir); - - // Load a preset from an already parsed config file, insert it into the sorted sequence of presets - // and select it, losing previous modifications. - Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true); - Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true); - - Preset& load_external_preset( - // Path to the profile source file (a G-code, an AMF or 3MF file, a config file) - const std::string &path, - // Name of the profile, derived from the source file name. - const std::string &name, - // Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored. - const std::string &original_name, - // Config to initialize the preset from. - const DynamicPrintConfig &config, - // Select the preset after loading? - bool select = true); - - // Save the preset under a new name. If the name is different from the old one, - // a new preset is stored into the list of presets. - // All presets are marked as not modified and the new preset is activated. - void save_current_preset(const std::string &new_name); - - // Delete the current preset, activate the first visible preset. - void delete_current_preset(); - - // Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame. - bool load_bitmap_default(const std::string &file_name); - - // Compatible & incompatible marks, to be placed at the wxBitmapComboBox items. - void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; } - void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; } - void set_bitmap_lock (const wxBitmap *bmp) { m_bitmap_lock = bmp; } - void set_bitmap_lock_open (const wxBitmap *bmp) { m_bitmap_lock_open = bmp; } - - // Enable / disable the "- default -" preset. - void set_default_suppressed(bool default_suppressed); - bool is_default_suppressed() const { return m_default_suppressed; } - - // Select a preset. If an invalid index is provided, the first visible preset is selected. - Preset& select_preset(size_t idx); - // Return the selected preset, without the user modifications applied. - Preset& get_selected_preset() { return m_presets[m_idx_selected]; } - const Preset& get_selected_preset() const { return m_presets[m_idx_selected]; } - int get_selected_idx() const { return m_idx_selected; } - // Returns the name of the selected preset, or an empty string if no preset is selected. - std::string get_selected_preset_name() const { return (m_idx_selected == -1) ? std::string() : this->get_selected_preset().name; } - // For the current edited preset, return the parent preset if there is one. - // If there is no parent preset, nullptr is returned. - // 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 - 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; } - - // used to update preset_choice from Tab - const std::deque<Preset>& get_presets() { return m_presets; } - int get_idx_selected() { return m_idx_selected; } - static const std::string& get_suffix_modified(); - - // Return a preset possibly with modifications. - Preset& default_preset() { return m_presets.front(); } - const Preset& default_preset() const { return m_presets.front(); } - // Return a preset by an index. If the preset is active, a temporary copy is returned. - Preset& preset(size_t idx) { return (int(idx) == m_idx_selected) ? m_edited_preset : m_presets[idx]; } - const Preset& preset(size_t idx) const { return const_cast<PresetCollection*>(this)->preset(idx); } - void discard_current_changes() { m_presets[m_idx_selected].reset_dirty(); m_edited_preset = m_presets[m_idx_selected]; } - - // Return a preset by its name. If the preset is active, a temporary copy is returned. - // If a preset is not found by its name, null is returned. - Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false); - const Preset* find_preset(const std::string &name, bool first_visible_if_not_found = false) const - { return const_cast<PresetCollection*>(this)->find_preset(name, first_visible_if_not_found); } - - size_t first_visible_idx() const; - // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. - // If one of the prefered_alternates is compatible, select it. - template<typename PreferedCondition> - size_t first_compatible_idx(PreferedCondition prefered_condition) const - { - size_t i = m_default_suppressed ? m_num_default_presets : 0; - size_t n = this->m_presets.size(); - size_t i_compatible = n; - for (; i < n; ++ i) - if (m_presets[i].is_compatible) { - if (prefered_condition(m_presets[i].name)) - return i; - if (i_compatible == n) - // Store the first compatible profile into i_compatible. - i_compatible = i; - } - return (i_compatible == n) ? 0 : i_compatible; - } - // Return index of the first compatible preset. Certainly at least the '- default -' preset shall be compatible. - size_t first_compatible_idx() const { return this->first_compatible_idx([](const std::string&){return true;}); } - - // Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible. - // Return the first visible preset. Certainly at least the '- default -' preset shall be visible. - Preset& first_visible() { return this->preset(this->first_visible_idx()); } - const Preset& first_visible() const { return this->preset(this->first_visible_idx()); } - Preset& first_compatible() { return this->preset(this->first_compatible_idx()); } - template<typename PreferedCondition> - Preset& first_compatible(PreferedCondition prefered_condition) { return this->preset(this->first_compatible_idx(prefered_condition)); } - const Preset& first_compatible() const { return this->preset(this->first_compatible_idx()); } - - // Return number of presets including the "- default -" preset. - size_t size() const { return m_presets.size(); } - bool has_defaults_only() const { return m_presets.size() <= m_num_default_presets; } - - // For Print / Filament presets, disable those, which are not compatible with the printer. - template<typename PreferedCondition> - void update_compatible_with_printer(const Preset &active_printer, bool select_other_if_incompatible, PreferedCondition prefered_condition) - { - if (this->update_compatible_with_printer_internal(active_printer, 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_with_printer(const Preset &active_printer, bool select_other_if_incompatible) - { this->update_compatible_with_printer(active_printer, 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;}); } - - // Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ. - bool current_is_dirty() const { return ! this->current_dirty_options().empty(); } - // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector<std::string> current_dirty_options(const bool deep_compare = false) const - { return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); } - // Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ. - std::vector<std::string> current_different_from_parent_options(const bool deep_compare = false) const - { return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); } - - // Update the choice UI from the list of presets. - // If show_incompatible, all presets are shown, otherwise only the compatible presets are shown. - // If an incompatible preset is selected, it is shown as well. - size_t update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible); - // 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(wxBitmapComboBox *ui); - - // Update a dirty floag of the current preset, update the labels of the UI component accordingly. - // Return true if the dirty flag changed. - bool update_dirty_ui(wxBitmapComboBox *ui); - - // Select a profile by its name. Return true if the selection changed. - // Without force, the selection is only updated if the index changes. - // With force, the changes are reverted if the new index is the same as the old index. - bool select_preset_by_name(const std::string &name, bool force); - - // Generate a file path from a profile name. Add the ".ini" suffix if it is missing. - std::string path_from_name(const std::string &new_name) const; - -protected: - // Select a preset, if it exists. If it does not exist, select an invalid (-1) index. - // This is a temporary state, which shall be fixed immediately by the following step. - bool select_preset_by_name_strict(const std::string &name); - - // Merge one vendor's presets with the other vendor's presets, report duplicates. - std::vector<std::string> merge_presets(PresetCollection &&other, const std::set<VendorProfile> &new_vendors); - -private: - PresetCollection(); - PresetCollection(const PresetCollection &other); - PresetCollection& operator=(const PresetCollection &other); - - // Find a preset position in the sorted list of presets. - // The "-- default -- " preset is always the first, so it needs - // to be handled differently. - // If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name. - std::deque<Preset>::iterator find_preset_internal(const std::string &name) - { - Preset key(m_type, name); - auto it = std::lower_bound(m_presets.begin() + m_num_default_presets, m_presets.end(), key); - if (it == m_presets.end() || it->name != name) { - // Preset has not been not found in the sorted list of non-default presets. Try the defaults. - for (size_t i = 0; i < m_num_default_presets; ++ i) - if (m_presets[i].name == name) { - it = m_presets.begin() + i; - break; - } - } - return it; - } - std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const - { return const_cast<PresetCollection*>(this)->find_preset_internal(name); } - - size_t update_compatible_with_printer_internal(const Preset &active_printer, bool unselect_if_incompatible); - - static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false); - - // Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER. - Preset::Type m_type; - // List of presets, starting with the "- default -" preset. - // 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; - // 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. - int m_idx_selected; - // 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. - // These bitmaps are not owned by PresetCollection, but by a PresetBundle. - const wxBitmap *m_bitmap_compatible = nullptr; - const wxBitmap *m_bitmap_incompatible = nullptr; - const wxBitmap *m_bitmap_lock = nullptr; - const wxBitmap *m_bitmap_lock_open = nullptr; - // Marks placed at the wxBitmapComboBox of a MainFrame. - // These bitmaps are owned by PresetCollection. - wxBitmap *m_bitmap_main_frame; - // Path to the directory to store the config files into. - std::string m_dir_path; - - // Caching color bitmaps for the filament combo box. - GUI::BitmapCache *m_bitmap_cache = nullptr; - - // to access select_preset_by_name_strict() - friend class PresetBundle; -}; - -} // namespace Slic3r - -#endif /* slic3r_Preset_hpp_ */ diff --git a/xs/src/slic3r/GUI/PresetBundle.cpp b/xs/src/slic3r/GUI/PresetBundle.cpp deleted file mode 100644 index cd3924dd0..000000000 --- a/xs/src/slic3r/GUI/PresetBundle.cpp +++ /dev/null @@ -1,1401 +0,0 @@ -//#undef NDEBUG -#include <cassert> - -#include "PresetBundle.hpp" -#include "BitmapCache.hpp" - -#include <algorithm> -#include <fstream> -#include <boost/filesystem.hpp> -#include <boost/algorithm/clamp.hpp> -#include <boost/algorithm/string/predicate.hpp> - -#include <boost/nowide/cenv.hpp> -#include <boost/nowide/cstdio.hpp> -#include <boost/nowide/fstream.hpp> -#include <boost/property_tree/ini_parser.hpp> -#include <boost/property_tree/ptree.hpp> -#include <boost/locale.hpp> -#include <boost/log/trivial.hpp> - -#include <wx/dcmemory.h> -#include <wx/image.h> -#include <wx/choice.h> -#include <wx/bmpcbox.h> -#include <wx/wupdlock.h> - -#include "../../libslic3r/libslic3r.h" -#include "../../libslic3r/PlaceholderParser.hpp" -#include "../../libslic3r/Utils.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. -// #define SLIC3R_PROFILE_USE_PRESETS_SUBDIR - -namespace Slic3r { - -static std::vector<std::string> s_project_options { - "wiping_volumes_extruders", - "wiping_volumes_matrix" -}; - -PresetBundle::PresetBundle() : - prints(Preset::TYPE_PRINT, Preset::print_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())), - filaments(Preset::TYPE_FILAMENT, Preset::filament_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults())), - sla_materials(Preset::TYPE_SLA_MATERIAL, Preset::sla_material_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults())), - printers(Preset::TYPE_PRINTER, Preset::printer_options(), static_cast<const HostConfig&>(FullPrintConfig::defaults()), "- default FFF -"), - m_bitmapCompatible(new wxBitmap), - m_bitmapIncompatible(new wxBitmap), - m_bitmapLock(new wxBitmap), - m_bitmapLockOpen(new wxBitmap), - m_bitmapCache(new GUI::BitmapCache) -{ - if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) - wxImage::AddHandler(new wxPNGHandler); - - // The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes, - // therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being - // initialized based on PrintConfigDef(), but to empty values (zeros, empty vectors, empty strings). - // - // "compatible_printers", "compatible_printers_condition", "inherits", - // "print_settings_id", "filament_settings_id", "printer_settings_id", - // "printer_vendor", "printer_model", "printer_variant", "default_print_profile", "default_filament_profile" - - // Create the ID config keys, as they are not part of the Static print config classes. - this->prints.default_preset().config.optptr("print_settings_id", true); - this->prints.default_preset().compatible_printers_condition(); - this->prints.default_preset().inherits(); - - this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values = { "" }; - this->filaments.default_preset().compatible_printers_condition(); - this->filaments.default_preset().inherits(); - - this->sla_materials.default_preset().config.optptr("sla_material_settings_id", true); - this->sla_materials.default_preset().compatible_printers_condition(); - this->sla_materials.default_preset().inherits(); - - this->printers.add_default_preset(Preset::sla_printer_options(), static_cast<const SLAMaterialConfig&>(SLAFullPrintConfig::defaults()), "- default SLA -"); - this->printers.preset(1).printer_technology() = ptSLA; - for (size_t i = 0; i < 2; ++ i) { - Preset &preset = this->printers.preset(i); - preset.config.optptr("printer_settings_id", true); - preset.config.optptr("printer_vendor", true); - preset.config.optptr("printer_model", true); - preset.config.optptr("printer_variant", true); - preset.config.optptr("default_print_profile", true); - preset.config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" }; - preset.inherits(); - } - - // Load the default preset bitmaps. - this->prints .load_bitmap_default("cog.png"); - this->filaments .load_bitmap_default("spool.png"); - this->sla_materials.load_bitmap_default("package_green.png"); - this->printers .load_bitmap_default("printer_empty.png"); - this->load_compatible_bitmaps(); - - // Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above. - this->prints .select_preset(0); - this->filaments .select_preset(0); - this->sla_materials.select_preset(0); - this->printers .select_preset(0); - - this->project_config.apply_only(FullPrintConfig::defaults(), s_project_options); -} - -PresetBundle::~PresetBundle() -{ - assert(m_bitmapCompatible != nullptr); - assert(m_bitmapIncompatible != nullptr); - assert(m_bitmapLock != nullptr); - assert(m_bitmapLockOpen != nullptr); - delete m_bitmapCompatible; - m_bitmapCompatible = nullptr; - delete m_bitmapIncompatible; - m_bitmapIncompatible = nullptr; - delete m_bitmapLock; - m_bitmapLock = nullptr; - delete m_bitmapLockOpen; - m_bitmapLockOpen = nullptr; - delete m_bitmapCache; - m_bitmapCache = nullptr; -} - -void PresetBundle::reset(bool delete_files) -{ - // Clear the existing presets, delete their respective files. - this->vendors.clear(); - this->prints .reset(delete_files); - this->filaments .reset(delete_files); - this->sla_materials.reset(delete_files); - this->printers .reset(delete_files); - this->filament_presets.clear(); - this->filament_presets.emplace_back(this->filaments.get_selected_preset_name()); - this->obsolete_presets.prints.clear(); - this->obsolete_presets.filaments.clear(); - this->obsolete_presets.sla_materials.clear(); - this->obsolete_presets.printers.clear(); -} - -void PresetBundle::setup_directories() -{ - boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir()); - std::initializer_list<boost::filesystem::path> paths = { - data_dir, - data_dir / "vendor", - data_dir / "cache", -#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR - // Store the print/filament/printer presets into a "presets" directory. - data_dir / "presets", - data_dir / "presets" / "print", - data_dir / "presets" / "filament", - data_dir / "presets" / "sla_material", - data_dir / "presets" / "printer" -#else - // Store the print/filament/printer presets at the same location as the upstream Slic3r. - data_dir / "print", - data_dir / "filament", - data_dir / "sla_material", - data_dir / "printer" -#endif - }; - for (const boost::filesystem::path &path : paths) { - boost::filesystem::path subdir = path; - subdir.make_preferred(); - if (! boost::filesystem::is_directory(subdir) && - ! boost::filesystem::create_directory(subdir)) - throw std::runtime_error(std::string("Slic3r was unable to create its data directory at ") + subdir.string()); - } -} - -void PresetBundle::load_presets(const AppConfig &config) -{ - // First load the vendor specific system presets. - std::string errors_cummulative = this->load_system_presets(); - - const std::string dir_user_presets = data_dir() -#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR - // Store the print/filament/printer presets into a "presets" directory. - + "/presets" -#else - // Store the print/filament/printer presets at the same location as the upstream Slic3r. -#endif - ; - try { - this->prints.load_presets(dir_user_presets, "print"); - } catch (const std::runtime_error &err) { - errors_cummulative += err.what(); - } - try { - this->filaments.load_presets(dir_user_presets, "filament"); - } catch (const std::runtime_error &err) { - errors_cummulative += err.what(); - } - try { - this->sla_materials.load_presets(dir_user_presets, "sla_material"); - } catch (const std::runtime_error &err) { - errors_cummulative += err.what(); - } - try { - this->printers.load_presets(dir_user_presets, "printer"); - } catch (const std::runtime_error &err) { - errors_cummulative += err.what(); - } - this->update_multi_material_filament_presets(); - this->update_compatible_with_printer(false); - if (! errors_cummulative.empty()) - throw std::runtime_error(errors_cummulative); - - this->load_selections(config); -} - -// Load system presets into this PresetBundle. -// For each vendor, there will be a single PresetBundle loaded. -std::string PresetBundle::load_system_presets() -{ - // Here the vendor specific read only Config Bundles are stored. - boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / "vendor").make_preferred(); - std::string errors_cummulative; - bool first = true; - for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) { - std::string name = dir_entry.path().filename().string(); - // Remove the .ini suffix. - name.erase(name.size() - 4); - try { - // Load the config bundle, flatten it. - if (first) { - // Reset this PresetBundle and load the first vendor config. - this->load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM); - first = false; - } else { - // Load the other vendor configs, merge them with this PresetBundle. - // Report duplicate profiles. - PresetBundle other; - other.load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM); - std::vector<std::string> duplicates = this->merge_presets(std::move(other)); - if (! duplicates.empty()) { - errors_cummulative += "Vendor configuration file " + name + " contains the following presets with names used by other vendors: "; - for (size_t i = 0; i < duplicates.size(); ++ i) { - if (i > 0) - errors_cummulative += ", "; - errors_cummulative += duplicates[i]; - } - } - } - } catch (const std::runtime_error &err) { - errors_cummulative += err.what(); - errors_cummulative += "\n"; - } - } - if (first) { - // No config bundle loaded, reset. - this->reset(false); - } - return errors_cummulative; -} - -// Merge one vendor's presets with the other vendor's presets, report duplicates. -std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other) -{ - this->vendors.insert(other.vendors.begin(), other.vendors.end()); - std::vector<std::string> duplicate_prints = this->prints .merge_presets(std::move(other.prints), this->vendors); - std::vector<std::string> duplicate_filaments = this->filaments .merge_presets(std::move(other.filaments), this->vendors); - std::vector<std::string> duplicate_sla_materials = this->sla_materials.merge_presets(std::move(other.sla_materials), this->vendors); - std::vector<std::string> duplicate_printers = this->printers .merge_presets(std::move(other.printers), this->vendors); - append(this->obsolete_presets.prints, std::move(other.obsolete_presets.prints)); - append(this->obsolete_presets.filaments, std::move(other.obsolete_presets.filaments)); - append(this->obsolete_presets.sla_materials, std::move(other.obsolete_presets.sla_materials)); - append(this->obsolete_presets.printers, std::move(other.obsolete_presets.printers)); - append(duplicate_prints, std::move(duplicate_filaments)); - append(duplicate_prints, std::move(duplicate_sla_materials)); - append(duplicate_prints, std::move(duplicate_printers)); - return duplicate_prints; -} - -static inline std::string remove_ini_suffix(const std::string &name) -{ - std::string out = name; - if (boost::iends_with(out, ".ini")) - out.erase(out.end() - 4, out.end()); - return out; -} - -// Set the "enabled" flag for printer vendors, printer models and printer variants -// based on the user configuration. -// If the "vendor" section is missing, enable all models and variants of the particular vendor. -void PresetBundle::load_installed_printers(const AppConfig &config) -{ - for (auto &preset : printers) { - preset.set_visible_from_appconfig(config); - } -} - -// Load selections (current print, current filaments, current printer) from config.ini -// This is done on application start up or after updates are applied. -void PresetBundle::load_selections(const AppConfig &config) -{ - // Update visibility of presets based on application vendor / model / variant configuration. - this->load_installed_printers(config); - - // Parse the initial print / filament / printer profile names. - std::string initial_print_profile_name = remove_ini_suffix(config.get("presets", "print")); - std::string initial_filament_profile_name = remove_ini_suffix(config.get("presets", "filament")); - std::string initial_sla_material_profile_name = remove_ini_suffix(config.get("presets", "sla_material")); - std::string initial_printer_profile_name = remove_ini_suffix(config.get("presets", "printer")); - - // Activate print / filament / printer profiles from the config. - // If the printer profile enumerated by the config are not visible, select an alternate preset. - // Do not select alternate profiles for the print / filament profiles as those presets - // will be selected by the following call of this->update_compatible_with_printer(true). - prints.select_preset_by_name_strict(initial_print_profile_name); - filaments.select_preset_by_name_strict(initial_filament_profile_name); - sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name); - printers.select_preset_by_name(initial_printer_profile_name, true); - - if (printers.get_selected_preset().printer_technology() == ptFFF){ - // Load the names of the other filament profiles selected for a multi-material printer. - auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(printers.get_selected_preset().config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - this->filament_presets = { initial_filament_profile_name }; - for (unsigned int i = 1; i < (unsigned int)num_extruders; ++i) { - char name[64]; - sprintf(name, "filament_%d", i); - if (!config.has("presets", name)) - break; - this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name))); - } - // Do not define the missing filaments, so that the update_compatible_with_printer() will use the preferred filaments. - this->filament_presets.resize(num_extruders, ""); - } - - // Update visibility of presets based on their compatibility with the active printer. - // Always try to select a compatible print and filament preset to the current printer preset, - // as the application may have been closed with an active "external" preset, which does not - // exist. - this->update_compatible_with_printer(true); - this->update_multi_material_filament_presets(); -} - -// Export selections (current print, current filaments, current printer) into config.ini -void PresetBundle::export_selections(AppConfig &config) -{ - assert(filament_presets.size() >= 1); - assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front()); - config.clear_section("presets"); - config.set("presets", "print", prints.get_selected_preset_name()); - config.set("presets", "filament", filament_presets.front()); - for (int i = 1; i < filament_presets.size(); ++i) { - char name[64]; - sprintf(name, "filament_%d", i); - config.set("presets", name, filament_presets[i]); - } - config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); - config.set("presets", "printer", printers.get_selected_preset_name()); -} - -void PresetBundle::export_selections(PlaceholderParser &pp) -{ - assert(filament_presets.size() >= 1); - assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front()); - switch (printers.get_edited_preset().printer_technology()) { - case ptFFF: - pp.set("print_preset", prints.get_selected_preset().name); - pp.set("filament_preset", filament_presets); - break; - case ptSLA: - pp.set("sla_material_preset", sla_materials.get_selected_preset().name); - break; - } - pp.set("printer_preset", printers.get_selected_preset().name); -} - -bool PresetBundle::load_compatible_bitmaps() -{ - const std::string path_bitmap_compatible = "flag-green-icon.png"; - const std::string path_bitmap_incompatible = "flag-red-icon.png"; - const std::string path_bitmap_lock = "sys_lock.png";//"lock.png"; - const std::string path_bitmap_lock_open = "sys_unlock.png";//"lock_open.png"; - bool loaded_compatible = m_bitmapCompatible ->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_compatible).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_incompatible = m_bitmapIncompatible->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_incompatible).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_lock = m_bitmapLock->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_lock).c_str()), wxBITMAP_TYPE_PNG); - bool loaded_lock_open = m_bitmapLockOpen->LoadFile( - wxString::FromUTF8(Slic3r::var(path_bitmap_lock_open).c_str()), wxBITMAP_TYPE_PNG); - if (loaded_compatible) { - prints .set_bitmap_compatible(m_bitmapCompatible); - filaments .set_bitmap_compatible(m_bitmapCompatible); - sla_materials.set_bitmap_compatible(m_bitmapCompatible); -// printers .set_bitmap_compatible(m_bitmapCompatible); - } - if (loaded_incompatible) { - prints .set_bitmap_incompatible(m_bitmapIncompatible); - filaments .set_bitmap_incompatible(m_bitmapIncompatible); - sla_materials.set_bitmap_incompatible(m_bitmapIncompatible); -// printers .set_bitmap_incompatible(m_bitmapIncompatible); - } - if (loaded_lock) { - prints .set_bitmap_lock(m_bitmapLock); - filaments .set_bitmap_lock(m_bitmapLock); - sla_materials.set_bitmap_lock(m_bitmapLock); - printers .set_bitmap_lock(m_bitmapLock); - } - if (loaded_lock_open) { - prints .set_bitmap_lock_open(m_bitmapLock); - filaments .set_bitmap_lock_open(m_bitmapLock); - sla_materials.set_bitmap_lock_open(m_bitmapLock); - printers .set_bitmap_lock_open(m_bitmapLock); - } - return loaded_compatible && loaded_incompatible && loaded_lock && loaded_lock_open; -} - -DynamicPrintConfig PresetBundle::full_config() const -{ - return (this->printers.get_edited_preset().printer_technology() == ptFFF) ? - this->full_fff_config() : - this->full_sla_config(); -} - -DynamicPrintConfig PresetBundle::full_fff_config() const -{ - DynamicPrintConfig out; - out.apply(FullPrintConfig::defaults()); - out.apply(this->prints.get_edited_preset().config); - // Add the default filament preset to have the "filament_preset_id" defined. - out.apply(this->filaments.default_preset().config); - out.apply(this->printers.get_edited_preset().config); - out.apply(this->project_config); - - auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - // Collect the "compatible_printers_condition" and "inherits" values over all presets (print, filaments, printers) into a single vector. - std::vector<std::string> compatible_printers_condition; - std::vector<std::string> inherits; - compatible_printers_condition.emplace_back(this->prints.get_edited_preset().compatible_printers_condition()); - inherits .emplace_back(this->prints.get_edited_preset().inherits()); - - if (num_extruders <= 1) { - out.apply(this->filaments.get_edited_preset().config); - compatible_printers_condition.emplace_back(this->filaments.get_edited_preset().compatible_printers_condition()); - inherits .emplace_back(this->filaments.get_edited_preset().inherits()); - } else { - // Retrieve filament presets and build a single config object for them. - // First collect the filament configurations based on the user selection of this->filament_presets. - // Here this->filaments.find_preset() and this->filaments.first_visible() return the edited copy of the preset if active. - std::vector<const DynamicPrintConfig*> filament_configs; - for (const std::string &filament_preset_name : this->filament_presets) - filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config); - 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))); - inherits .emplace_back(Preset::inherits(*const_cast<DynamicPrintConfig*>(cfg))); - } - // Option values to set a ConfigOptionVector from. - std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr); - // loop through options and apply them to the resulting config. - for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { - if (key == "compatible_printers") - continue; - // Get a destination option. - ConfigOption *opt_dst = out.option(key, false); - if (opt_dst->is_scalar()) { - // Get an option, do not create if it does not exist. - const ConfigOption *opt_src = filament_configs.front()->option(key); - if (opt_src != nullptr) - opt_dst->set(opt_src); - } else { - // Setting a vector value from all filament_configs. - for (size_t i = 0; i < filament_opts.size(); ++ i) - filament_opts[i] = filament_configs[i]->option(key); - static_cast<ConfigOptionVectorBase*>(opt_dst)->set(filament_opts); - } - } - } - - // Don't store the "compatible_printers_condition" for the printer profile, there is none. - inherits.emplace_back(this->printers.get_edited_preset().inherits()); - - // These two value types clash between the print and filament profiles. They should be renamed. - out.erase("compatible_printers"); - out.erase("compatible_printers_condition"); - out.erase("inherits"); - - static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" }; - for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) { - std::string key = std::string(keys[i]) + "_extruder"; - auto *opt = dynamic_cast<ConfigOptionInt*>(out.option(key, false)); - assert(opt != nullptr); - opt->value = boost::algorithm::clamp<int>(opt->value, 0, int(num_extruders)); - } - - out.option<ConfigOptionString >("print_settings_id", true)->value = this->prints.get_selected_preset().name; - out.option<ConfigOptionStrings>("filament_settings_id", true)->values = this->filament_presets; - out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name; - - // Serialize the collected "compatible_printers_condition" and "inherits" fields. - // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored. - // The vector will not be stored if all fields are empty strings. - auto add_if_some_non_empty = [&out](std::vector<std::string> &&values, const std::string &key) { - bool nonempty = false; - for (const std::string &v : values) - if (! v.empty()) { - nonempty = true; - break; - } - if (nonempty) - out.set_key_value(key, new ConfigOptionStrings(std::move(values))); - }; - add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative"); - add_if_some_non_empty(std::move(inherits), "inherits_cummulative"); - return out; -} - -DynamicPrintConfig PresetBundle::full_sla_config() const -{ - DynamicPrintConfig out; - out.apply(SLAFullPrintConfig::defaults()); - out.apply(this->sla_materials.get_edited_preset().config); - out.apply(this->printers.get_edited_preset().config); - // There are no project configuration values as of now, the project_config is reserved for FFF printers. -// out.apply(this->project_config); - - // Collect the "compatible_printers_condition" and "inherits" values over all presets (sla_materials, printers) into a single vector. - std::vector<std::string> compatible_printers_condition; - std::vector<std::string> inherits; - compatible_printers_condition.emplace_back(this->/*prints*/sla_materials.get_edited_preset().compatible_printers_condition()); - inherits .emplace_back(this->/*prints*/sla_materials.get_edited_preset().inherits()); - inherits .emplace_back(this->printers.get_edited_preset().inherits()); - - // These two value types clash between the print and filament profiles. They should be renamed. - out.erase("compatible_printers"); - out.erase("compatible_printers_condition"); - out.erase("inherits"); - - out.option<ConfigOptionString >("sla_material_settings_id", true)->value = this->sla_materials.get_selected_preset().name; - out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name; - - // Serialize the collected "compatible_printers_condition" and "inherits" fields. - // There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored. - // The vector will not be stored if all fields are empty strings. - auto add_if_some_non_empty = [&out](std::vector<std::string> &&values, const std::string &key) { - bool nonempty = false; - for (const std::string &v : values) - if (! v.empty()) { - nonempty = true; - break; - } - if (nonempty) - out.set_key_value(key, new ConfigOptionStrings(std::move(values))); - }; - add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative"); - add_if_some_non_empty(std::move(inherits), "inherits_cummulative"); - return out; -} - -// Load an external config file containing the print, filament and printer presets. -// Instead of a config file, a G-code may be loaded containing the full set of parameters. -// In the future the configuration will likely be read from an AMF file as well. -// If the file is loaded successfully, its print / filament / printer profiles will be activated. -void PresetBundle::load_config_file(const std::string &path) -{ - if (boost::iends_with(path, ".gcode") || boost::iends_with(path, ".g")) { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config.load_from_gcode_file(path); - Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - return; - } - - // 1) Try to load the config file into a boost property tree. - boost::property_tree::ptree tree; - try { - boost::nowide::ifstream ifs(path); - boost::property_tree::read_ini(ifs, tree); - } catch (const std::ifstream::failure &err) { - throw std::runtime_error(std::string("The config file cannot be loaded: ") + path + "\n\tReason: " + err.what()); - } catch (const std::runtime_error &err) { - throw std::runtime_error(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what()); - } - - // 2) Continue based on the type of the configuration file. - ConfigFileType config_file_type = guess_config_file_type(tree); - switch (config_file_type) { - case CONFIG_FILE_TYPE_UNKNOWN: - throw std::runtime_error(std::string("Unknown configuration file type: ") + path); - case CONFIG_FILE_TYPE_APP_CONFIG: - throw std::runtime_error(std::string("Invalid configuration file: ") + path + ". This is an application config file."); - case CONFIG_FILE_TYPE_CONFIG: - { - // Initialize a config from full defaults. - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config.load(tree); - Preset::normalize(config); - load_config_file_config(path, true, std::move(config)); - break; - } - case CONFIG_FILE_TYPE_CONFIG_BUNDLE: - load_config_file_config_bundle(path, tree); - break; - } -} - -void PresetBundle::load_config_string(const char* str, const char* source_filename) -{ - if (str != nullptr) - { - DynamicPrintConfig config; - config.apply(FullPrintConfig::defaults()); - config.load_from_gcode_string(str); - Preset::normalize(config); - load_config_file_config((source_filename == nullptr) ? "" : source_filename, true, std::move(config)); - } -} - -// Load a config file from a boost property_tree. This is a private method called from load_config_file. -void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config) -{ - PrinterTechnology printer_technology = Preset::printer_technology(config); - - // The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway, - // but some of the alpha versions of Slic3r did. - { - ConfigOption *opt_compatible = config.optptr("compatible_printers"); - if (opt_compatible != nullptr) { - assert(opt_compatible->type() == coStrings); - if (opt_compatible->type() == coStrings) - static_cast<ConfigOptionStrings*>(opt_compatible)->values.clear(); - } - } - - size_t num_extruders = (printer_technology == ptFFF) ? - std::min(config.option<ConfigOptionFloats>("nozzle_diameter" )->values.size(), - config.option<ConfigOptionFloats>("filament_diameter")->values.size()) : - 0; - // Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which - // accumulate values over all presets (print, filaments, printers). - // These values will be distributed into their particular presets when loading. - std::vector<std::string> compatible_printers_condition_values = std::move(config.option<ConfigOptionStrings>("compatible_printers_condition_cummulative", true)->values); - std::vector<std::string> inherits_values = std::move(config.option<ConfigOptionStrings>("inherits_cummulative", true)->values); - std::string &compatible_printers_condition = Preset::compatible_printers_condition(config); - std::string &inherits = Preset::inherits(config); - compatible_printers_condition_values.resize(num_extruders + 2, std::string()); - inherits_values.resize(num_extruders + 2, std::string()); - // The "default_filament_profile" will be later extracted into the printer profile. - if (printer_technology == ptFFF) - config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string()); - - // 1) Create a name from the file name. - // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. - std::string name = is_external ? boost::filesystem::path(name_or_path).filename().string() : name_or_path; - - // 2) If the loading succeeded, split and load the config into print / filament / printer settings. - // First load the print and printer presets. - for (size_t i_group = 0; i_group < 2; ++ i_group) { - PresetCollection &presets = (i_group == 0) ? ((printer_technology == ptFFF) ? this->prints : this->sla_materials) : this->printers; - // Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles. - size_t idx = (i_group == 0) ? 0 : num_extruders + 1; - inherits = inherits_values[idx]; - compatible_printers_condition = compatible_printers_condition_values[idx]; - if (is_external) - presets.load_external_preset(name_or_path, name, - config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") : "printer_settings_id", true), - config); - else - presets.load_preset(presets.path_from_name(name), name, config).save(); - } - - if (Preset::printer_technology(config) == ptFFF) { - // 3) Now load the filaments. If there are multiple filament presets, split them and load them. - auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true); - old_filament_profile_names->values.resize(num_extruders, std::string()); - - if (num_extruders <= 1) { - // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. - inherits = inherits_values[1]; - compatible_printers_condition = compatible_printers_condition_values[1]; - if (is_external) - this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); - else - this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save(); - this->filament_presets.clear(); - this->filament_presets.emplace_back(name); - } else { - // Split the filament presets, load each of them separately. - std::vector<DynamicPrintConfig> configs(num_extruders, this->filaments.default_preset().config); - // loop through options and scatter them into configs. - for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) { - const ConfigOption *other_opt = config.option(key); - if (other_opt == nullptr) - continue; - if (other_opt->is_scalar()) { - for (size_t i = 0; i < configs.size(); ++ i) - configs[i].option(key, false)->set(other_opt); - } else if (key != "compatible_printers") { - for (size_t i = 0; i < configs.size(); ++ i) - static_cast<ConfigOptionVectorBase*>(configs[i].option(key, false))->set_at(other_opt, 0, i); - } - } - // Load the configs into this->filaments and make them active. - this->filament_presets.clear(); - for (size_t i = 0; i < configs.size(); ++ i) { - DynamicPrintConfig &cfg = configs[i]; - // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. - cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1]; - cfg.opt_string("inherits", true) = inherits_values[i + 1]; - // Load all filament presets, but only select the first one in the preset dialog. - Preset *loaded = nullptr; - if (is_external) - loaded = &this->filaments.load_external_preset(name_or_path, name, - (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "", - std::move(cfg), i == 0); - else { - // Used by the config wizard when creating a custom setup. - // Therefore this block should only be called for a single extruder. - char suffix[64]; - if (i == 0) - suffix[0] = 0; - else - sprintf(suffix, "%d", i); - std::string new_name = name + suffix; - loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name), - new_name, std::move(cfg), i == 0); - loaded->save(); - } - this->filament_presets.emplace_back(loaded->name); - } - } - - // 4) Load the project config values (the per extruder wipe matrix etc). - this->project_config.apply_only(config, s_project_options); - } - - this->update_compatible_with_printer(false); -} - -// Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file. -void PresetBundle::load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree) -{ - // 1) Load the config bundle into a temp data. - PresetBundle tmp_bundle; - // Load the config bundle, don't save the loaded presets to user profile directory. - tmp_bundle.load_configbundle(path, 0); - std::string bundle_name = std::string(" - ") + boost::filesystem::path(path).filename().string(); - - // 2) Extract active configs from the config bundle, copy them and activate them in this bundle. - auto load_one = [this, &path, &bundle_name](PresetCollection &collection_dst, PresetCollection &collection_src, const std::string &preset_name_src, bool activate) -> std::string { - Preset *preset_src = collection_src.find_preset(preset_name_src, false); - Preset *preset_dst = collection_dst.find_preset(preset_name_src, false); - assert(preset_src != nullptr); - std::string preset_name_dst; - if (preset_dst != nullptr && preset_dst->is_default) { - // No need to copy a default preset, it always exists in collection_dst. - if (activate) - collection_dst.select_preset(0); - return preset_name_src; - } else if (preset_dst != nullptr && preset_src->config == preset_dst->config) { - // Don't save as the config exists in the current bundle and its content is the same. - return preset_name_src; - } else { - // Generate a new unique name. - preset_name_dst = preset_name_src + bundle_name; - Preset *preset_dup = nullptr; - for (size_t i = 1; (preset_dup = collection_dst.find_preset(preset_name_dst, false)) != nullptr; ++ i) { - if (preset_src->config == preset_dup->config) - // The preset has been already copied into collection_dst. - return preset_name_dst; - // Try to generate another name. - char buf[64]; - sprintf(buf, " (%d)", i); - preset_name_dst = preset_name_src + buf + bundle_name; - } - } - assert(! preset_name_dst.empty()); - // Save preset_src->config into collection_dst under preset_name_dst. - // The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway, - // but some of the alpha versions of Slic3r did. - ConfigOption *opt_compatible = preset_src->config.optptr("compatible_printers"); - if (opt_compatible != nullptr) { - assert(opt_compatible->type() == coStrings); - if (opt_compatible->type() == coStrings) - static_cast<ConfigOptionStrings*>(opt_compatible)->values.clear(); - } - collection_dst.load_preset(path, preset_name_dst, std::move(preset_src->config), activate).is_external = true; - return preset_name_dst; - }; - load_one(this->prints, tmp_bundle.prints, tmp_bundle.prints .get_selected_preset().name, true); - load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filaments .get_selected_preset().name, true); - load_one(this->sla_materials, tmp_bundle.sla_materials, tmp_bundle.sla_materials.get_selected_preset().name, true); - load_one(this->printers, tmp_bundle.printers, tmp_bundle.printers .get_selected_preset().name, true); - this->update_multi_material_filament_presets(); - for (size_t i = 1; i < std::min(tmp_bundle.filament_presets.size(), this->filament_presets.size()); ++ i) - this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false); - - this->update_compatible_with_printer(false); -} - -// Process the Config Bundle loaded as a Boost property tree. -// For each print, filament and printer preset (group defined by group_name), apply the inherited presets. -// The presets starting with '*' are considered non-terminal and they are -// removed through the flattening process by this function. -// This function will never fail, but it will produce error messages through boost::log. -static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, const std::string &group_name) -{ - namespace pt = boost::property_tree; - - typedef std::pair<pt::ptree::key_type, pt::ptree> ptree_child_type; - - // 1) For the group given by group_name, initialize the presets. - struct Prst { - Prst(const std::string &name, pt::ptree *node) : name(name), node(node) {} - // Name of this preset. If the name starts with '*', it is an intermediate preset, - // which will not make it into the result. - const std::string name; - // Link to the source boost property tree node, owned by tree. - pt::ptree *node; - // Link to the presets, from which this preset inherits. - std::vector<Prst*> inherits; - // Link to the presets, for which this preset is a direct parent. - std::vector<Prst*> parent_of; - // When running the Kahn's Topological sorting algorithm, this counter is decreased from inherits.size() to zero. - // A cycle is indicated, if the number does not drop to zero after the Kahn's algorithm finishes. - size_t num_incoming_edges_left = 0; - // Sorting by the name, to be used when inserted into std::set. - bool operator==(const Prst &rhs) const { return this->name == rhs.name; } - bool operator< (const Prst &rhs) const { return this->name < rhs.name; } - }; - // Find the presets, store them into a std::map, addressed by their names. - std::set<Prst> presets; - std::string group_name_preset = group_name + ":"; - for (auto §ion : tree) - if (boost::starts_with(section.first, group_name_preset) && section.first.size() > group_name_preset.size()) - presets.emplace(section.first.substr(group_name_preset.size()), §ion.second); - // Fill in the "inherits" and "parent_of" members, report invalid inheritance fields. - for (const Prst &prst : presets) { - // Parse the list of comma separated values, possibly enclosed in quotes. - std::vector<std::string> inherits_names; - if (Slic3r::unescape_strings_cstyle(prst.node->get<std::string>("inherits", ""), inherits_names)) { - // Resolve the inheritance by name. - std::vector<Prst*> &inherits_nodes = const_cast<Prst&>(prst).inherits; - for (const std::string &node_name : inherits_names) { - auto it = presets.find(Prst(node_name, nullptr)); - if (it == presets.end()) - BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " inherits an unknown preset \"" << node_name << "\""; - else { - inherits_nodes.emplace_back(const_cast<Prst*>(&(*it))); - inherits_nodes.back()->parent_of.emplace_back(const_cast<Prst*>(&prst)); - } - } - } else { - BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " has an invalid \"inherits\" field"; - } - // Remove the "inherits" key, it has no meaning outside the config bundle. - const_cast<pt::ptree*>(prst.node)->erase("inherits"); - } - - // 2) Create a linear ordering for the directed acyclic graph of preset inheritance. - // https://en.wikipedia.org/wiki/Topological_sorting - // Kahn's algorithm. - std::vector<Prst*> sorted; - { - // Initialize S with the set of all nodes with no incoming edge. - std::deque<Prst*> S; - for (const Prst &prst : presets) - if (prst.inherits.empty()) - S.emplace_back(const_cast<Prst*>(&prst)); - else - const_cast<Prst*>(&prst)->num_incoming_edges_left = prst.inherits.size(); - while (! S.empty()) { - Prst *n = S.front(); - S.pop_front(); - sorted.emplace_back(n); - for (Prst *m : n->parent_of) { - assert(m->num_incoming_edges_left > 0); - if (-- m->num_incoming_edges_left == 0) { - // We have visited all parents of m. - S.emplace_back(m); - } - } - } - if (sorted.size() < presets.size()) { - for (const Prst &prst : presets) - if (prst.num_incoming_edges_left) - BOOST_LOG_TRIVIAL(error) << "flatten_configbundle_hierarchy: The preset " << prst.name << " has cyclic dependencies"; - } - } - - // Apply the dependencies in their topological ordering. - for (Prst *prst : sorted) { - // Merge the preset nodes in their order of application. - // Iterate in a reverse order, so the last change will be placed first in merged. - for (auto it_inherits = prst->inherits.rbegin(); it_inherits != prst->inherits.rend(); ++ it_inherits) - for (auto it = (*it_inherits)->node->begin(); it != (*it_inherits)->node->end(); ++ it) - if (prst->node->find(it->first) == prst->node->not_found()) - prst->node->add_child(it->first, it->second); - } - - // Remove the "internal" presets from the ptree. These presets are marked with '*'. - group_name_preset += '*'; - for (auto it_section = tree.begin(); it_section != tree.end(); ) { - if (boost::starts_with(it_section->first, group_name_preset) && it_section->first.size() > group_name_preset.size()) - // Remove the "internal" preset from the ptree. - it_section = tree.erase(it_section); - else - // Keep the preset. - ++ it_section; - } -} - -static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree) -{ - flatten_configbundle_hierarchy(tree, "print"); - flatten_configbundle_hierarchy(tree, "filament"); - flatten_configbundle_hierarchy(tree, "sla_material"); - flatten_configbundle_hierarchy(tree, "printer"); -} - -// Load a config bundle file, into presets and store the loaded presets into separate files -// of the local configuration directory. -size_t PresetBundle::load_configbundle(const std::string &path, unsigned int flags) -{ - if (flags & (LOAD_CFGBNDLE_RESET_USER_PROFILE | LOAD_CFGBNDLE_SYSTEM)) - // Reset this bundle, delete user profile files if LOAD_CFGBNDLE_SAVE. - this->reset(flags & LOAD_CFGBNDLE_SAVE); - - // 1) Read the complete config file into a boost::property_tree. - namespace pt = boost::property_tree; - pt::ptree tree; - boost::nowide::ifstream ifs(path); - pt::read_ini(ifs, tree); - - const VendorProfile *vendor_profile = nullptr; - if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) { - auto vp = VendorProfile::from_ini(tree, path); - if (vp.num_variants() == 0) - return 0; - vendor_profile = &(*this->vendors.insert(vp).first); - } - - if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) { - return 0; - } - - // 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed. - flatten_configbundle_hierarchy(tree); - - // 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files. - // Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure. - std::vector<std::string> loaded_prints; - std::vector<std::string> loaded_filaments; - std::vector<std::string> loaded_sla_materials; - std::vector<std::string> loaded_printers; - std::string active_print; - std::vector<std::string> active_filaments; - std::string active_sla_material; - std::string active_printer; - size_t presets_loaded = 0; - for (const auto §ion : tree) { - PresetCollection *presets = nullptr; - std::vector<std::string> *loaded = nullptr; - std::string preset_name; - if (boost::starts_with(section.first, "print:")) { - presets = &this->prints; - loaded = &loaded_prints; - preset_name = section.first.substr(6); - } else if (boost::starts_with(section.first, "filament:")) { - presets = &this->filaments; - loaded = &loaded_filaments; - preset_name = section.first.substr(9); - } else if (boost::starts_with(section.first, "sla_material:")) { - presets = &this->sla_materials; - loaded = &loaded_sla_materials; - preset_name = section.first.substr(9); - } else if (boost::starts_with(section.first, "printer:")) { - presets = &this->printers; - loaded = &loaded_printers; - preset_name = section.first.substr(8); - } else if (section.first == "presets") { - // Load the names of the active presets. - for (auto &kvp : section.second) { - if (kvp.first == "print") { - active_print = kvp.second.data(); - } else if (boost::starts_with(kvp.first, "filament")) { - int idx = 0; - if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) { - if (int(active_filaments.size()) <= idx) - active_filaments.resize(idx + 1, std::string()); - active_filaments[idx] = kvp.second.data(); - } - } else if (kvp.first == "sla_material") { - active_sla_material = kvp.second.data(); - } else if (kvp.first == "printer") { - active_printer = kvp.second.data(); - } - } - } else if (section.first == "obsolete_presets") { - // Parse the names of obsolete presets. These presets will be deleted from user's - // profile directory on installation of this vendor preset. - for (auto &kvp : section.second) { - std::vector<std::string> *dst = nullptr; - if (kvp.first == "print") - dst = &this->obsolete_presets.prints; - else if (kvp.first == "filament") - dst = &this->obsolete_presets.filaments; - else if (kvp.first == "sla_material") - dst = &this->obsolete_presets.sla_materials; - else if (kvp.first == "printer") - dst = &this->obsolete_presets.printers; - if (dst) - unescape_strings_cstyle(kvp.second.data(), *dst); - } - } else if (section.first == "settings") { - // Load the settings. - for (auto &kvp : section.second) { - if (kvp.first == "autocenter") { - } - } - } else - // Ignore an unknown section. - continue; - if (presets != nullptr) { - // Load the print, filament or printer preset. - const DynamicPrintConfig &default_config = presets->default_preset().config; - DynamicPrintConfig config(default_config); - for (auto &kvp : section.second) - config.set_deserialize(kvp.first, kvp.second.data()); - Preset::normalize(config); - // Report configuration fields, which are misplaced into a wrong group. - std::string incorrect_keys; - size_t n_incorrect_keys = 0; - for (const std::string &key : config.keys()) - if (! default_config.has(key)) { - if (incorrect_keys.empty()) - incorrect_keys = key; - else { - incorrect_keys += ", "; - incorrect_keys += key; - } - config.erase(key); - ++ n_incorrect_keys; - } - if (! incorrect_keys.empty()) - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed"; - if ((flags & LOAD_CFGBNDLE_SYSTEM) && presets == &printers) { - // Filter out printer presets, which are not mentioned in the vendor profile. - // These presets are considered not installed. - auto printer_model = config.opt_string("printer_model"); - if (printer_model.empty()) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" defines no printer model, it will be ignored."; - continue; - } - auto printer_variant = config.opt_string("printer_variant"); - if (printer_variant.empty()) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" defines no printer variant, it will be ignored."; - continue; - } - auto it_model = std::find_if(vendor_profile->models.cbegin(), vendor_profile->models.cend(), - [&](const VendorProfile::PrinterModel &m) { return m.id == printer_model; } - ); - if (it_model == vendor_profile->models.end()) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" defines invalid printer model \"" << printer_model << "\", it will be ignored."; - continue; - } - auto it_variant = it_model->variant(printer_variant); - if (it_variant == nullptr) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" defines invalid printer variant \"" << printer_variant << "\", it will be ignored."; - continue; - } - const Preset *preset_existing = presets->find_preset(section.first, false); - if (preset_existing != nullptr) { - BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" << - section.first << "\" has already been loaded from another Confing Bundle."; - continue; - } - } - // Decide a full path to this .ini file. - auto file_name = boost::algorithm::iends_with(preset_name, ".ini") ? preset_name : preset_name + ".ini"; - auto file_path = (boost::filesystem::path(data_dir()) -#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR - // Store the print/filament/printer presets into a "presets" directory. - / "presets" -#else - // Store the print/filament/printer presets at the same location as the upstream Slic3r. -#endif - / presets->name() / file_name).make_preferred(); - // Load the preset into the list of presets, save it to disk. - Preset &loaded = presets->load_preset(file_path.string(), preset_name, std::move(config), false); - if (flags & LOAD_CFGBNDLE_SAVE) - loaded.save(); - if (flags & LOAD_CFGBNDLE_SYSTEM) { - loaded.is_system = true; - loaded.vendor = vendor_profile; - } - ++ presets_loaded; - } - } - - // 3) Activate the presets. - if ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) { - if (! active_print.empty()) - prints.select_preset_by_name(active_print, true); - if (! active_sla_material.empty()) - sla_materials.select_preset_by_name(active_sla_material, true); - if (! active_printer.empty()) - printers.select_preset_by_name(active_printer, true); - // Activate the first filament preset. - if (! active_filaments.empty() && ! active_filaments.front().empty()) - filaments.select_preset_by_name(active_filaments.front(), true); - this->update_multi_material_filament_presets(); - for (size_t i = 0; i < std::min(this->filament_presets.size(), active_filaments.size()); ++ i) - this->filament_presets[i] = filaments.find_preset(active_filaments[i], true)->name; - this->update_compatible_with_printer(false); - } - - return presets_loaded; -} - -void PresetBundle::update_multi_material_filament_presets() -{ - if (printers.get_edited_preset().printer_technology() != ptFFF) - return; - - // Verify and select the filament presets. - auto *nozzle_diameter = static_cast<const ConfigOptionFloats*>(printers.get_edited_preset().config.option("nozzle_diameter")); - size_t num_extruders = nozzle_diameter->values.size(); - // Verify validity of the current filament presets. - for (size_t i = 0; i < std::min(this->filament_presets.size(), num_extruders); ++ i) - this->filament_presets[i] = this->filaments.find_preset(this->filament_presets[i], true)->name; - // Append the rest of filament presets. - this->filament_presets.resize(num_extruders, this->filament_presets.empty() ? this->filaments.first_visible().name : this->filament_presets.back()); - - // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator): - std::vector<double> old_matrix = this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values; - size_t old_number_of_extruders = int(sqrt(old_matrix.size())+EPSILON); - if (num_extruders != old_number_of_extruders) { - // First verify if purging volumes presets for each extruder matches number of extruders - std::vector<double>& extruders = this->project_config.option<ConfigOptionFloats>("wiping_volumes_extruders")->values; - while (extruders.size() < 2*num_extruders) { - extruders.push_back(extruders.size()>1 ? extruders[0] : 50.); // copy the values from the first extruder - extruders.push_back(extruders.size()>1 ? extruders[1] : 50.); - } - while (extruders.size() > 2*num_extruders) { - extruders.pop_back(); - extruders.pop_back(); - } - - std::vector<double> new_matrix; - for (unsigned int i=0;i<num_extruders;++i) - for (unsigned int j=0;j<num_extruders;++j) { - // append the value for this pair from the old matrix (if it's there): - if (i<old_number_of_extruders && j<old_number_of_extruders) - new_matrix.push_back(old_matrix[i*old_number_of_extruders + j]); - else - new_matrix.push_back( i==j ? 0. : extruders[2*i]+extruders[2*j+1]); // so it matches new extruder volumes - } - this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values = new_matrix; - } -} - -void PresetBundle::update_compatible_with_printer(bool select_other_if_incompatible) -{ - const Preset &printer_preset = this->printers.get_edited_preset(); - - switch (printers.get_edited_preset().printer_technology()) { - case ptFFF: - { - 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_with_printer(printer_preset, select_other_if_incompatible) : - this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible, - [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; }); - prefered_filament_profiles.empty() ? - this->filaments.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : - this->filaments.update_compatible_with_printer(printer_preset, 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) { - // Verify validity of the current filament presets. - this->filament_presets.front() = this->filaments.get_edited_preset().name; - for (size_t idx = 1; idx < this->filament_presets.size(); ++ idx) { - std::string &filament_name = this->filament_presets[idx]; - Preset *preset = this->filaments.find_preset(filament_name, false); - if (preset == nullptr || ! preset->is_compatible) { - // Pick a compatible profile. If there are prefered_filament_profiles, use them. - if (prefered_filament_profiles.empty()) - filament_name = this->filaments.first_compatible().name; - else { - const std::string &preferred = (idx < prefered_filament_profiles.size()) ? - prefered_filament_profiles[idx] : prefered_filament_profiles.front(); - filament_name = this->filaments.first_compatible( - [&preferred](const std::string& profile_name){ return profile_name == preferred; }).name; - } - } - } - } - } - case ptSLA: - { - 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->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : - this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible, - [&prefered_print_profile](const std::string& profile_name){ return profile_name == prefered_print_profile; }); - } - } -} - -void PresetBundle::export_configbundle(const std::string &path) //, const DynamicPrintConfig &settings -{ - boost::nowide::ofstream c; - c.open(path, std::ios::out | std::ios::trunc); - - // Put a comment at the first line including the time stamp and Slic3r version. - c << "# " << Slic3r::header_slic3r_generated() << std::endl; - - // Export the print, filament and printer profiles. - for (size_t i_group = 0; i_group < 3; ++ i_group) { - const PresetCollection &presets = (i_group == 0) ? this->prints : (i_group == 1) ? this->filaments : this->printers; - for (const Preset &preset : presets()) { - if (preset.is_default || preset.is_external) - // Only export the common presets, not external files or the default preset. - continue; - c << std::endl << "[" << presets.name() << ":" << preset.name << "]" << std::endl; - for (const std::string &opt_key : preset.config.keys()) - c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; - } - } - - // Export the names of the active presets. - c << std::endl << "[presets]" << std::endl; - c << "print = " << this->prints.get_selected_preset().name << std::endl; - c << "sla_material = " << this->sla_materials.get_selected_preset().name << std::endl; - c << "printer = " << this->printers.get_selected_preset().name << std::endl; - for (size_t i = 0; i < this->filament_presets.size(); ++ i) { - char suffix[64]; - if (i > 0) - sprintf(suffix, "_%d", i); - else - suffix[0] = 0; - c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl; - } - -#if 0 - // Export the following setting values from the provided setting repository. - static const char *settings_keys[] = { "autocenter" }; - c << "[settings]" << std::endl; - for (size_t i = 0; i < sizeof(settings_keys) / sizeof(settings_keys[0]); ++ i) - c << settings_keys[i] << " = " << settings.serialize(settings_keys[i]) << std::endl; -#endif - - c.close(); -} - -// Set the filament preset name. As the name could come from the UI selection box, -// an optional "(modified)" suffix will be removed from the filament name. -void PresetBundle::set_filament_preset(size_t idx, const std::string &name) -{ - if (name.find_first_of("-------") == 0) - return; - - if (idx >= filament_presets.size()) - filament_presets.resize(idx + 1, filaments.default_preset().name); - filament_presets[idx] = Preset::remove_suffix_modified(name); -} - -static inline int hex_digit_to_int(const char c) -{ - return - (c >= '0' && c <= '9') ? int(c - '0') : - (c >= 'A' && c <= 'F') ? int(c - 'A') + 10 : - (c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1; -} - -bool PresetBundle::parse_color(const std::string &scolor, unsigned char *rgb_out) -{ - rgb_out[0] = rgb_out[1] = rgb_out[2] = 0; - if (scolor.size() != 7 || scolor.front() != '#') - return false; - const char *c = scolor.data() + 1; - for (size_t i = 0; i < 3; ++ i) { - int digit1 = hex_digit_to_int(*c ++); - int digit2 = hex_digit_to_int(*c ++); - if (digit1 == -1 || digit2 == -1) - return false; - rgb_out[i] = (unsigned char)(digit1 * 16 + digit2); - } - return true; -} - -void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui) -{ - if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA) - return; - - unsigned char rgb[3]; - std::string extruder_color = this->printers.get_edited_preset().config.opt_string("extruder_colour", idx_extruder); - if (! parse_color(extruder_color, rgb)) - // Extruder color is not defined. - extruder_color.clear(); - - // Fill in the list from scratch. - ui->Freeze(); - ui->Clear(); - size_t selected_preset_item = 0; - const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]); - // Show wide icons if the currently selected preset is not compatible with the current printer, - // and draw a red flag in front of the selected preset. - bool wide_icons = selected_preset != nullptr && ! selected_preset->is_compatible && m_bitmapIncompatible != nullptr; - assert(selected_preset != nullptr); - std::map<wxString, wxBitmap*> nonsys_presets; - wxString selected_str = ""; - if (!this->filaments().front().is_visible) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); - for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) { - const Preset &preset = this->filaments.preset(i); - bool selected = this->filament_presets[idx_extruder] == preset.name; - if (! preset.is_visible || (! preset.is_compatible && ! selected)) - continue; - // Assign an extruder color to the selected item if the extruder color is defined. - std::string filament_rgb = preset.config.opt_string("filament_colour", 0); - std::string extruder_rgb = (selected && !extruder_color.empty()) ? extruder_color : filament_rgb; - bool single_bar = filament_rgb == extruder_rgb; - std::string bitmap_key = single_bar ? filament_rgb : filament_rgb + extruder_rgb; - // If the filament preset is not compatible and there is a "red flag" icon loaded, show it left - // to the filament color image. - if (wide_icons) - bitmap_key += preset.is_compatible ? ",cmpt" : ",ncmpt"; - bitmap_key += (preset.is_system || preset.is_default) ? ",syst" : ",nsyst"; - if (preset.is_dirty) - bitmap_key += ",drty"; - wxBitmap *bitmap = m_bitmapCache->find(bitmap_key); - if (bitmap == nullptr) { - // Create the bitmap with color bars. - std::vector<wxBitmap> bmps; - if (wide_icons) - // Paint a red flag for incompatible presets. - bmps.emplace_back(preset.is_compatible ? m_bitmapCache->mkclear(16, 16) : *m_bitmapIncompatible); - // Paint the color bars. - parse_color(filament_rgb, rgb); - bmps.emplace_back(m_bitmapCache->mksolid(single_bar ? 24 : 16, 16, rgb)); - if (! single_bar) { - parse_color(extruder_rgb, rgb); - bmps.emplace_back(m_bitmapCache->mksolid(8, 16, rgb)); - } - // Paint a lock at the system presets. - bmps.emplace_back(m_bitmapCache->mkclear(2, 16)); - bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmapLock : m_bitmapCache->mkclear(16, 16)); -// (preset.is_dirty ? *m_bitmapLockOpen : *m_bitmapLock) : m_bitmapCache->mkclear(16, 16)); - bitmap = m_bitmapCache->insert(bitmap_key, bmps); - } - - if (preset.is_default || preset.is_system){ - ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? wxNullBitmap : *bitmap); - if (selected) - selected_preset_item = ui->GetCount() - 1; - } - else - { - nonsys_presets.emplace(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()), - (bitmap == 0) ? &wxNullBitmap : bitmap); - if (selected) - selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()); - } - if (preset.is_default) - ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap); - } - - if (!nonsys_presets.empty()) - { - ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap); - for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) { - ui->Append(it->first, *it->second); - if (it->first == selected_str) - selected_preset_item = ui->GetCount() - 1; - } - } - ui->SetSelection(selected_preset_item); - ui->SetToolTip(ui->GetString(selected_preset_item)); - ui->Thaw(); -} - -void PresetBundle::set_default_suppressed(bool default_suppressed) -{ - prints.set_default_suppressed(default_suppressed); - filaments.set_default_suppressed(default_suppressed); - sla_materials.set_default_suppressed(default_suppressed); - printers.set_default_suppressed(default_suppressed); -} - -} // namespace Slic3r diff --git a/xs/src/slic3r/GUI/PresetBundle.hpp b/xs/src/slic3r/GUI/PresetBundle.hpp deleted file mode 100644 index 68ec534da..000000000 --- a/xs/src/slic3r/GUI/PresetBundle.hpp +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef slic3r_PresetBundle_hpp_ -#define slic3r_PresetBundle_hpp_ - -#include "AppConfig.hpp" -#include "Preset.hpp" - -#include <set> -#include <boost/filesystem/path.hpp> - -namespace Slic3r { - -namespace GUI { - class BitmapCache; -}; - -class PlaceholderParser; - -// Bundle of Print + Filament + Printer presets. -class PresetBundle -{ -public: - PresetBundle(); - ~PresetBundle(); - - // Remove all the presets but the "-- default --". - // Optionally remove all the files referenced by the presets from the user profile directory. - void reset(bool delete_files); - - void setup_directories(); - - // Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets. - // Load selections (current print, current filaments, current printer) from config.ini - // This is done just once on application start up. - void load_presets(const AppConfig &config); - - // Export selections (current print, current filaments, current printer) into config.ini - void export_selections(AppConfig &config); - // Export selections (current print, current filaments, current printer) into a placeholder parser. - void export_selections(PlaceholderParser &pp); - - PresetCollection prints; - PresetCollection filaments; - PresetCollection sla_materials; - PresetCollection printers; - // Filament preset names for a multi-extruder or multi-material print. - // extruders.size() should be the same as printers.get_edited_preset().config.nozzle_diameter.size() - std::vector<std::string> filament_presets; - - // The project configuration values are kept separated from the print/filament/printer preset, - // they are being serialized / deserialized from / to the .amf, .3mf, .config, .gcode, - // and they are being used by slicing core. - DynamicPrintConfig project_config; - - // There will be an entry for each system profile loaded, - // and the system profiles will point to the VendorProfile instances owned by PresetBundle::vendors. - std::set<VendorProfile> vendors; - - struct ObsoletePresets { - std::vector<std::string> prints; - std::vector<std::string> filaments; - std::vector<std::string> sla_materials; - std::vector<std::string> printers; - }; - ObsoletePresets obsolete_presets; - - bool has_defauls_only() const - { return prints.has_defaults_only() && filaments.has_defaults_only() && printers.has_defaults_only(); } - - DynamicPrintConfig full_config() const; - - // Load user configuration and store it into the user profiles. - // This method is called by the configuration wizard. - void load_config(const std::string &name, DynamicPrintConfig config) - { this->load_config_file_config(name, false, std::move(config)); } - - // Load an external config file containing the print, filament and printer presets. - // Instead of a config file, a G-code may be loaded containing the full set of parameters. - // In the future the configuration will likely be read from an AMF file as well. - // If the file is loaded successfully, its print / filament / printer profiles will be activated. - void load_config_file(const std::string &path); - - // Load an external config source containing the print, filament and printer presets. - // The given string must contain the full set of parameters (same as those exported to gcode). - // If the string is parsed successfully, its print / filament / printer profiles will be activated. - void load_config_string(const char* str, const char* source_filename = nullptr); - - // Load a config bundle file, into presets and store the loaded presets into separate files - // of the local configuration directory. - // Load settings into the provided settings instance. - // Activate the presets stored in the config bundle. - // Returns the number of presets loaded successfully. - enum { - // Save the profiles, which have been loaded. - LOAD_CFGBNDLE_SAVE = 1, - // Delete all old config profiles before loading. - LOAD_CFGBNDLE_RESET_USER_PROFILE = 2, - // Load a system config bundle. - LOAD_CFGBNDLE_SYSTEM = 4, - LOAD_CFGBUNDLE_VENDOR_ONLY = 8, - }; - // Load the config bundle, store it to the user profile directory by default. - size_t load_configbundle(const std::string &path, unsigned int flags = LOAD_CFGBNDLE_SAVE); - - // Export a config bundle file containing all the presets and the names of the active presets. - void export_configbundle(const std::string &path); // , const DynamicPrintConfig &settings); - - // Update a filament selection combo box on the platter for an idx_extruder. - void update_platter_filament_ui(unsigned int idx_extruder, wxBitmapComboBox *ui); - - // Enable / disable the "- default -" preset. - void set_default_suppressed(bool default_suppressed); - - // Set the filament preset name. As the name could come from the UI selection box, - // an optional "(modified)" suffix will be removed from the filament name. - void set_filament_preset(size_t idx, const std::string &name); - - // Read out the number of extruders from an active printer preset, - // update size and content of filament_presets. - void update_multi_material_filament_presets(); - - // Update the is_compatible flag of all print and filament presets depending on whether they are marked - // as compatible with the currently selected printer. - // Also updates the is_visible flag of each preset. - // If select_other_if_incompatible is true, then the print or filament preset is switched to some compatible - // preset if the current print or filament preset is not compatible. - void update_compatible_with_printer(bool select_other_if_incompatible); - - static bool parse_color(const std::string &scolor, unsigned char *rgb_out); - -private: - std::string load_system_presets(); - // Merge one vendor's presets with the other vendor's presets, report duplicates. - std::vector<std::string> merge_presets(PresetBundle &&other); - - // Set the "enabled" flag for printer vendors, printer models and printer variants - // based on the user configuration. - // If the "vendor" section is missing, enable all models and variants of the particular vendor. - void load_installed_printers(const AppConfig &config); - - // Load selections (current print, current filaments, current printer) from config.ini - // This is done just once on application start up. - void load_selections(const AppConfig &config); - - // Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path. - // and the external config is just referenced, not stored into user profile directory. - // If it is not an external config, then the config will be stored into the user profile directory. - void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config); - void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree); - bool load_compatible_bitmaps(); - - DynamicPrintConfig full_fff_config() const; - DynamicPrintConfig full_sla_config() const; - - // Indicator, that the preset is compatible with the selected printer. - wxBitmap *m_bitmapCompatible; - // Indicator, that the preset is NOT compatible with the selected printer. - wxBitmap *m_bitmapIncompatible; - // Indicator, that the preset is system and not modified. - wxBitmap *m_bitmapLock; - // Indicator, that the preset is system and user modified. - wxBitmap *m_bitmapLockOpen; - // Caching color bitmaps for the filament combo box. - GUI::BitmapCache *m_bitmapCache; -}; - -} // namespace Slic3r - -#endif /* slic3r_PresetBundle_hpp_ */ diff --git a/xs/src/slic3r/GUI/PresetHints.cpp b/xs/src/slic3r/GUI/PresetHints.cpp deleted file mode 100644 index d4c929c1c..000000000 --- a/xs/src/slic3r/GUI/PresetHints.cpp +++ /dev/null @@ -1,278 +0,0 @@ -//#undef NDEBUG -#include <cassert> - -#include "PresetBundle.hpp" -#include "PresetHints.hpp" -#include "Flow.hpp" - -#include <boost/algorithm/string/predicate.hpp> -#include <wx/intl.h> - -#include "../../libslic3r/libslic3r.h" -#include "GUI.hpp" - -namespace Slic3r { - -#define MIN_BUF_LENGTH 4096 -std::string PresetHints::cooling_description(const Preset &preset) -{ - std::string out; - char buf[MIN_BUF_LENGTH/*4096*/]; - if (preset.config.opt_bool("cooling", 0)) { - int slowdown_below_layer_time = preset.config.opt_int("slowdown_below_layer_time", 0); - int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); - int max_fan_speed = preset.config.opt_int("max_fan_speed", 0); - int min_print_speed = int(preset.config.opt_float("min_print_speed", 0) + 0.5); - int fan_below_layer_time = preset.config.opt_int("fan_below_layer_time", 0); - sprintf(buf, _CHB(L("If estimated layer time is below ~%ds, fan will run at %d%% and print speed will be reduced so that no less than %ds are spent on that layer (however, speed will never be reduced below %dmm/s).")), - slowdown_below_layer_time, max_fan_speed, slowdown_below_layer_time, min_print_speed); - out += buf; - if (fan_below_layer_time > slowdown_below_layer_time) { - sprintf(buf, _CHB(L("\nIf estimated layer time is greater, but still below ~%ds, fan will run at a proportionally decreasing speed between %d%% and %d%%.")), - fan_below_layer_time, max_fan_speed, min_fan_speed); - out += buf; - } - out += _CHB(L("\nDuring the other layers, fan ")); - } else { - out = _CHB(L("Fan ")); - } - if (preset.config.opt_bool("fan_always_on", 0)) { - int disable_fan_first_layers = preset.config.opt_int("disable_fan_first_layers", 0); - int min_fan_speed = preset.config.opt_int("min_fan_speed", 0); - sprintf(buf, _CHB(L("will always run at %d%% ")), min_fan_speed); - out += buf; - if (disable_fan_first_layers > 1) { - sprintf(buf, _CHB(L("except for the first %d layers")), disable_fan_first_layers); - out += buf; - } - else if (disable_fan_first_layers == 1) - out += _CHB(L("except for the first layer")); - } else - out += _CHB(L("will be turned off.")); - - return out; -} - -static const ConfigOptionFloatOrPercent& first_positive(const ConfigOptionFloatOrPercent *v1, const ConfigOptionFloatOrPercent &v2, const ConfigOptionFloatOrPercent &v3) -{ - return (v1 != nullptr && v1->value > 0) ? *v1 : ((v2.value > 0) ? v2 : v3); -} - -std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle &preset_bundle) -{ - // Find out, to which nozzle index is the current filament profile assigned. - int idx_extruder = 0; - int num_extruders = (int)preset_bundle.filament_presets.size(); - for (; idx_extruder < num_extruders; ++ idx_extruder) - if (preset_bundle.filament_presets[idx_extruder] == preset_bundle.filaments.get_selected_preset().name) - break; - if (idx_extruder == num_extruders) - // The current filament preset is not active for any extruder. - idx_extruder = -1; - - const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config; - const DynamicPrintConfig &filament_config = preset_bundle.filaments.get_edited_preset().config; - const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config; - - // Current printer values. - float nozzle_diameter = (float)printer_config.opt_float("nozzle_diameter", idx_extruder); - - // Print config values - double layer_height = print_config.opt_float("layer_height"); - double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); - double support_material_speed = print_config.opt_float("support_material_speed"); - double support_material_interface_speed = print_config.get_abs_value("support_material_interface_speed", support_material_speed); - double bridge_speed = print_config.opt_float("bridge_speed"); - double bridge_flow_ratio = print_config.opt_float("bridge_flow_ratio"); - double perimeter_speed = print_config.opt_float("perimeter_speed"); - double external_perimeter_speed = print_config.get_abs_value("external_perimeter_speed", perimeter_speed); - double gap_fill_speed = print_config.opt_float("gap_fill_speed"); - double infill_speed = print_config.opt_float("infill_speed"); - double small_perimeter_speed = print_config.get_abs_value("small_perimeter_speed", perimeter_speed); - double solid_infill_speed = print_config.get_abs_value("solid_infill_speed", infill_speed); - double top_solid_infill_speed = print_config.get_abs_value("top_solid_infill_speed", solid_infill_speed); - // Maximum print speed when auto-speed is enabled by setting any of the above speed values to zero. - double max_print_speed = print_config.opt_float("max_print_speed"); - // Maximum volumetric speed allowed for the print profile. - double max_volumetric_speed = print_config.opt_float("max_volumetric_speed"); - - const auto &extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("extrusion_width"); - const auto &external_perimeter_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("external_perimeter_extrusion_width"); - const auto &first_layer_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_extrusion_width"); - const auto &infill_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("infill_extrusion_width"); - const auto &perimeter_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("perimeter_extrusion_width"); - const auto &solid_infill_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("solid_infill_extrusion_width"); - const auto &support_material_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("support_material_extrusion_width"); - const auto &top_infill_extrusion_width = *print_config.option<ConfigOptionFloatOrPercent>("top_infill_extrusion_width"); - const auto &first_layer_speed = *print_config.option<ConfigOptionFloatOrPercent>("first_layer_speed"); - - // Index of an extruder assigned to a feature. If set to 0, an active extruder will be used for a multi-material print. - // If different from idx_extruder, it will not be taken into account for this hint. - auto feature_extruder_active = [idx_extruder, num_extruders](int i) { - return i <= 0 || i > num_extruders || idx_extruder == -1 || idx_extruder == i - 1; - }; - bool perimeter_extruder_active = feature_extruder_active(print_config.opt_int("perimeter_extruder")); - bool infill_extruder_active = feature_extruder_active(print_config.opt_int("infill_extruder")); - bool solid_infill_extruder_active = feature_extruder_active(print_config.opt_int("solid_infill_extruder")); - bool support_material_extruder_active = feature_extruder_active(print_config.opt_int("support_material_extruder")); - bool support_material_interface_extruder_active = feature_extruder_active(print_config.opt_int("support_material_interface_extruder")); - - // Current filament values - double filament_diameter = filament_config.opt_float("filament_diameter", 0); - double filament_crossection = M_PI * 0.25 * filament_diameter * filament_diameter; - double extrusion_multiplier = filament_config.opt_float("extrusion_multiplier", 0); - // The following value will be annotated by this hint, so it does not take part in the calculation. -// double filament_max_volumetric_speed = filament_config.opt_float("filament_max_volumetric_speed", 0); - - std::string out; - for (size_t idx_type = (first_layer_extrusion_width.value == 0) ? 1 : 0; idx_type < 3; ++ idx_type) { - // First test the maximum volumetric extrusion speed for non-bridging extrusions. - bool first_layer = idx_type == 0; - bool bridging = idx_type == 2; - const ConfigOptionFloatOrPercent *first_layer_extrusion_width_ptr = (first_layer && first_layer_extrusion_width.value > 0) ? - &first_layer_extrusion_width : nullptr; - const float lh = float(first_layer ? first_layer_height : layer_height); - const float bfr = bridging ? bridge_flow_ratio : 0.f; - double max_flow = 0.; - std::string max_flow_extrusion_type; - auto limit_by_first_layer_speed = [&first_layer_speed, first_layer](double speed_normal, double speed_max) { - if (first_layer && first_layer_speed.value > 0) - // Apply the first layer limit. - speed_normal = first_layer_speed.get_abs_value(speed_normal); - return (speed_normal > 0.) ? speed_normal : speed_max; - }; - if (perimeter_extruder_active) { - double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter, - first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : - limit_by_first_layer_speed(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed)); - if (max_flow < external_perimeter_rate) { - max_flow = external_perimeter_rate; - max_flow_extrusion_type = _CHB(L("external perimeters")); - } - double perimeter_rate = Flow::new_from_config_width(frPerimeter, - first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : - limit_by_first_layer_speed(std::max(perimeter_speed, small_perimeter_speed), max_print_speed)); - if (max_flow < perimeter_rate) { - max_flow = perimeter_rate; - max_flow_extrusion_type = _CHB(L("perimeters")); - } - } - if (! bridging && infill_extruder_active) { - double infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed); - if (max_flow < infill_rate) { - max_flow = infill_rate; - max_flow_extrusion_type = _CHB(L("infill")); - } - } - if (solid_infill_extruder_active) { - double solid_infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, 0).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed)); - if (max_flow < solid_infill_rate) { - max_flow = solid_infill_rate; - max_flow_extrusion_type = _CHB(L("solid infill")); - } - if (! bridging) { - double top_solid_infill_rate = Flow::new_from_config_width(frInfill, - first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed); - if (max_flow < top_solid_infill_rate) { - max_flow = top_solid_infill_rate; - max_flow_extrusion_type = _CHB(L("top solid infill")); - } - } - } - if (support_material_extruder_active) { - double support_material_rate = Flow::new_from_config_width(frSupportMaterial, - first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(support_material_speed, max_print_speed)); - if (max_flow < support_material_rate) { - max_flow = support_material_rate; - max_flow_extrusion_type = _CHB(L("support")); - } - } - if (support_material_interface_extruder_active) { - double support_material_interface_rate = Flow::new_from_config_width(frSupportMaterialInterface, - first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width), - nozzle_diameter, lh, bfr).mm3_per_mm() * - (bridging ? bridge_speed : limit_by_first_layer_speed(support_material_interface_speed, max_print_speed)); - if (max_flow < support_material_interface_rate) { - max_flow = support_material_interface_rate; - max_flow_extrusion_type = _CHB(L("support interface")); - } - } - //FIXME handle gap_fill_speed - if (! out.empty()) - out += "\n"; - out += (first_layer ? _CHB(L("First layer volumetric")) : (bridging ? _CHB(L("Bridging volumetric")) : _CHB(L("Volumetric")))); - out += _CHB(L(" flow rate is maximized ")); - bool limited_by_max_volumetric_speed = max_volumetric_speed > 0 && max_volumetric_speed < max_flow; - out += (limited_by_max_volumetric_speed ? - _CHB(L("by the print profile maximum")) : - (_CHB(L("when printing ")) + max_flow_extrusion_type)) - + _CHB(L(" with a volumetric rate ")); - if (limited_by_max_volumetric_speed) - max_flow = max_volumetric_speed; - char buf[MIN_BUF_LENGTH/*2048*/]; - sprintf(buf, _CHB(L("%3.2f mm³/s")), max_flow); - out += buf; - sprintf(buf, _CHB(L(" at filament speed %3.2f mm/s.")), max_flow / filament_crossection); - out += buf; - } - - return out; -} - -std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &preset_bundle) -{ - const DynamicPrintConfig &print_config = preset_bundle.prints .get_edited_preset().config; - const DynamicPrintConfig &printer_config = preset_bundle.printers .get_edited_preset().config; - - float layer_height = float(print_config.opt_float("layer_height")); - int num_perimeters = print_config.opt_int("perimeters"); - bool thin_walls = print_config.opt_bool("thin_walls"); - float nozzle_diameter = float(printer_config.opt_float("nozzle_diameter", 0)); - - std::string out; - if (layer_height <= 0.f){ - out += _CHB(L("Recommended object thin wall thickness: Not available due to invalid layer height.")); - return out; - } - - Flow external_perimeter_flow = Flow::new_from_config_width( - frExternalPerimeter, - *print_config.opt<ConfigOptionFloatOrPercent>("external_perimeter_extrusion_width"), - nozzle_diameter, layer_height, false); - Flow perimeter_flow = Flow::new_from_config_width( - frPerimeter, - *print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_width"), - nozzle_diameter, layer_height, false); - - - if (num_perimeters > 0) { - int num_lines = std::min(num_perimeters * 2, 10); - char buf[MIN_BUF_LENGTH/*256*/]; - sprintf(buf, _CHB(L("Recommended object thin wall thickness for layer height %.2f and ")), layer_height); - out += buf; - // Start with the width of two closely spaced - double width = external_perimeter_flow.width + external_perimeter_flow.spacing(); - for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) { - if (i > 2) - out += ", "; - sprintf(buf, _CHB(L("%d lines: %.2lf mm")), i, width); - out += buf; - width += perimeter_flow.spacing() * (thin_walls ? 1.f : 2.f); - } - } - return out; -} - -}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/PresetHints.hpp b/xs/src/slic3r/GUI/PresetHints.hpp deleted file mode 100644 index 39bf0b100..000000000 --- a/xs/src/slic3r/GUI/PresetHints.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef slic3r_PresetHints_hpp_ -#define slic3r_PresetHints_hpp_ - -#include <string> - -#include "PresetBundle.hpp" - -namespace Slic3r { - -// GUI utility functions to produce hint messages from the current profile. -class PresetHints -{ -public: - // Produce a textual description of the cooling logic of a currently active filament. - static std::string cooling_description(const Preset &preset); - - // Produce a textual description of the maximum flow achived for the current configuration - // (the current printer, filament and print settigns). - // This description will be useful for getting a gut feeling for the maximum volumetric - // print speed achievable with the extruder. - static std::string maximum_volumetric_flow_description(const PresetBundle &preset_bundle); - - // Produce a textual description of a recommended thin wall thickness - // from the provided number of perimeters and the external / internal perimeter width. - static std::string recommended_thin_wall_thickness(const PresetBundle &preset_bundle); -}; - -} // namespace Slic3r - -#endif /* slic3r_PresetHints_hpp_ */ diff --git a/xs/src/slic3r/GUI/ProgressIndicator.hpp b/xs/src/slic3r/GUI/ProgressIndicator.hpp deleted file mode 100644 index 0cf8b4a17..000000000 --- a/xs/src/slic3r/GUI/ProgressIndicator.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef IPROGRESSINDICATOR_HPP -#define IPROGRESSINDICATOR_HPP - -#include <string> -#include <functional> - -namespace Slic3r { - -/** - * @brief Generic progress indication interface. - */ -class ProgressIndicator { -public: - using CancelFn = std::function<void(void)>; // Cancel function signature. - -private: - float state_ = .0f, max_ = 1.f, step_; - CancelFn cancelfunc_ = [](){}; - -public: - - inline virtual ~ProgressIndicator() {} - - /// Get the maximum of the progress range. - float max() const { return max_; } - - /// Get the current progress state - float state() const { return state_; } - - /// Set the maximum of the progress range - virtual void max(float maxval) { max_ = maxval; } - - /// Set the current state of the progress. - virtual void state(float val) { state_ = val; } - - /** - * @brief Number of states int the progress. Can be used instead of giving a - * maximum value. - */ - virtual void states(unsigned statenum) { - step_ = max_ / statenum; - } - - /// Message shown on the next status update. - virtual void message(const std::string&) = 0; - - /// Title of the operation. - virtual void title(const std::string&) = 0; - - /// Formatted message for the next status update. Works just like sprintf. - virtual void message_fmt(const std::string& fmt, ...); - - /// Set up a cancel callback for the operation if feasible. - virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; } - - /** - * Explicitly shut down the progress indicator and call the associated - * callback. - */ - virtual void cancel() { cancelfunc_(); } - - /// Convenience function to call message and status update in one function. - void update(float st, const std::string& msg) { - message(msg); state(st); - } -}; - -} - -#endif // IPROGRESSINDICATOR_HPP diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp deleted file mode 100644 index 363e34cb2..000000000 --- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "ProgressStatusBar.hpp" - -#include <wx/timer.h> -#include <wx/gauge.h> -#include <wx/button.h> -#include <wx/statusbr.h> -#include <wx/frame.h> -#include "GUI.hpp" - -#include <iostream> - -namespace Slic3r { - -ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id): - self(new wxStatusBar(parent ? parent : GUI::get_main_frame(), - id == -1? wxID_ANY : id)), - timer_(new wxTimer(self)), - prog_ (new wxGauge(self, - wxGA_HORIZONTAL, - 100, - wxDefaultPosition, - wxDefaultSize)), - cancelbutton_(new wxButton(self, - -1, - "Cancel", - wxDefaultPosition, - wxDefaultSize)) -{ - prog_->Hide(); - cancelbutton_->Hide(); - - self->SetFieldsCount(3); - int w[] = {-1, 150, 155}; - self->SetStatusWidths(3, w); - - self->Bind(wxEVT_TIMER, [this](const wxTimerEvent&) { - if (prog_->IsShown()) timer_->Stop(); - if(is_busy()) prog_->Pulse(); - }); - - self->Bind(wxEVT_SIZE, [this](wxSizeEvent& event){ - wxRect rect; - self->GetFieldRect(1, rect); - auto offset = 0; - cancelbutton_->Move(rect.GetX() + offset, rect.GetY() + offset); - cancelbutton_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); - - self->GetFieldRect(2, rect); - prog_->Move(rect.GetX() + offset, rect.GetY() + offset); - prog_->SetSize(rect.GetWidth() - offset, rect.GetHeight()); - - event.Skip(); - }); - - cancelbutton_->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { - if(cancel_cb_) cancel_cb_(); - m_perl_cancel_callback.call(); - cancelbutton_->Hide(); - }); -} - -ProgressStatusBar::~ProgressStatusBar() { - if(timer_->IsRunning()) timer_->Stop(); -} - -int ProgressStatusBar::get_progress() const -{ - return prog_->GetValue(); -} - -void ProgressStatusBar::set_progress(int val) -{ - if(!prog_->IsShown()) show_progress(true); - - if(val == prog_->GetRange()) { - prog_->SetValue(0); - show_progress(false); - } else { - prog_->SetValue(val); - } -} - -int ProgressStatusBar::get_range() const -{ - return prog_->GetRange(); -} - -void ProgressStatusBar::set_range(int val) -{ - if(val != prog_->GetRange()) { - prog_->SetRange(val); - } -} - -void ProgressStatusBar::show_progress(bool show) -{ - prog_->Show(show); - prog_->Pulse(); -} - -void ProgressStatusBar::start_busy(int rate) -{ - busy_ = true; - show_progress(true); - if (!timer_->IsRunning()) { - timer_->Start(rate); - } -} - -void ProgressStatusBar::stop_busy() -{ - timer_->Stop(); - show_progress(false); - prog_->SetValue(0); - busy_ = false; -} - -void ProgressStatusBar::set_cancel_callback(ProgressStatusBar::CancelFn ccb) { - cancel_cb_ = ccb; - if(ccb) cancelbutton_->Show(); - else cancelbutton_->Hide(); -} - -void ProgressStatusBar::run(int rate) -{ - if(!timer_->IsRunning()) { - timer_->Start(rate); - } -} - -void ProgressStatusBar::embed(wxFrame *frame) -{ - wxFrame* mf = frame? frame : GUI::get_main_frame(); - mf->SetStatusBar(self); -} - -void ProgressStatusBar::set_status_text(const wxString& txt) -{ - self->SetStatusText(wxString::FromUTF8(txt.c_str())); -} - -void ProgressStatusBar::show_cancel_button() -{ - cancelbutton_->Show(); -} - -void ProgressStatusBar::hide_cancel_button() -{ - cancelbutton_->Hide(); -} - -} diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp deleted file mode 100644 index 9a7f58eee..000000000 --- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef PROGRESSSTATUSBAR_HPP -#define PROGRESSSTATUSBAR_HPP - -#include <memory> -#include <functional> - -#include "../../callback.hpp" - -class wxTimer; -class wxGauge; -class wxButton; -class wxTimerEvent; -class wxStatusBar; -class wxWindow; -class wxFrame; -class wxString; - -namespace Slic3r { - -/** - * @brief The ProgressStatusBar class is the widgets occupying the lower area - * of the Slicer main window. It consists of a message area to the left and a - * progress indication area to the right with an optional cancel button. - */ -class ProgressStatusBar { - wxStatusBar *self; // we cheat! It should be the base class but: perl! - wxTimer *timer_; - wxGauge *prog_; - wxButton *cancelbutton_; -public: - - /// Cancel callback function type - using CancelFn = std::function<void()>; - - ProgressStatusBar(wxWindow *parent = nullptr, int id = -1); - ~ProgressStatusBar(); - - int get_progress() const; - void set_progress(int); - int get_range() const; - void set_range(int = 100); - void show_progress(bool); - void start_busy(int = 100); - void stop_busy(); - inline bool is_busy() const { return busy_; } - void set_cancel_callback(CancelFn = CancelFn()); - inline void remove_cancel_callback() { set_cancel_callback(); } - void run(int rate); - void embed(wxFrame *frame = nullptr); - void set_status_text(const wxString& txt); - - // Temporary methods to satisfy Perl side - void show_cancel_button(); - void hide_cancel_button(); - - PerlCallback m_perl_cancel_callback; -private: - bool busy_ = false; - CancelFn cancel_cb_; -}; - -namespace GUI { - using Slic3r::ProgressStatusBar; -} - -} - -#endif // PROGRESSSTATUSBAR_HPP diff --git a/xs/src/slic3r/GUI/RammingChart.cpp b/xs/src/slic3r/GUI/RammingChart.cpp deleted file mode 100644 index 8954ff93b..000000000 --- a/xs/src/slic3r/GUI/RammingChart.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include <algorithm> -#include <wx/dcbuffer.h> - -#include "RammingChart.hpp" -#include "GUI.hpp" - - -wxDEFINE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent); - - -void Chart::draw() { - wxAutoBufferedPaintDC dc(this); // unbuffered DC caused flickering on win - - dc.SetBrush(GetBackgroundColour()); - dc.SetPen(GetBackgroundColour()); - dc.DrawRectangle(GetClientRect()); // otherwise the background would end up black on windows - - dc.SetPen(*wxBLACK_PEN); - dc.SetBrush(*wxWHITE_BRUSH); - dc.DrawRectangle(m_rect); - - if (visible_area.m_width < 0.499) { - dc.DrawText(_(L("NO RAMMING AT ALL")),wxPoint(m_rect.GetLeft()+m_rect.GetWidth()/2-50,m_rect.GetBottom()-m_rect.GetHeight()/2)); - return; - } - - - if (!m_line_to_draw.empty()) { - for (unsigned int i=0;i<m_line_to_draw.size()-2;++i) { - int color = 510*((m_rect.GetBottom()-(m_line_to_draw)[i])/double(m_rect.GetHeight())); - dc.SetPen( wxPen( wxColor(std::min(255,color),255-std::max(color-255,0),0), 1 ) ); - dc.DrawLine(m_rect.GetLeft()+1+i,(m_line_to_draw)[i],m_rect.GetLeft()+1+i,m_rect.GetBottom()); - } - dc.SetPen( wxPen( wxColor(0,0,0), 1 ) ); - for (unsigned int i=0;i<m_line_to_draw.size()-2;++i) { - if (splines) - dc.DrawLine(m_rect.GetLeft()+i,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i+1]); - else { - dc.DrawLine(m_rect.GetLeft()+i,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i]); - dc.DrawLine(m_rect.GetLeft()+i+1,(m_line_to_draw)[i],m_rect.GetLeft()+i+1,(m_line_to_draw)[i+1]); - } - } - } - - // draw draggable buttons - dc.SetBrush(*wxBLUE_BRUSH); - dc.SetPen( wxPen( wxColor(0,0,0), 1 ) ); - for (auto& button : m_buttons) - //dc.DrawRectangle(math_to_screen(button.get_pos())-wxPoint(side/2.,side/2.), wxSize(side,side)); - dc.DrawCircle(math_to_screen(button.get_pos()),side/2.); - //dc.DrawRectangle(math_to_screen(button.get_pos()-wxPoint2DDouble(0.125,0))-wxPoint(0,5),wxSize(50,10)); - - // draw x-axis: - float last_mark = -10000; - for (float math_x=int(visible_area.m_x*10)/10 ; math_x < (visible_area.m_x+visible_area.m_width) ; math_x+=0.1) { - int x = math_to_screen(wxPoint2DDouble(math_x,visible_area.m_y)).x; - int y = m_rect.GetBottom(); - if (x-last_mark < 50) continue; - dc.DrawLine(x,y+3,x,y-3); - dc.DrawText(wxString().Format(wxT("%.1f"), math_x),wxPoint(x-10,y+7)); - last_mark = x; - } - - // draw y-axis: - last_mark=10000; - for (int math_y=visible_area.m_y ; math_y < (visible_area.m_y+visible_area.m_height) ; math_y+=1) { - int y = math_to_screen(wxPoint2DDouble(visible_area.m_x,math_y)).y; - int x = m_rect.GetLeft(); - if (last_mark-y < 50) continue; - dc.DrawLine(x-3,y,x+3,y); - dc.DrawText(wxString()<<math_y,wxPoint(x-25,y-2/*7*/)); - last_mark = y; - } - - // axis labels: - wxString label = _(L("Time")) + " ("+_(L("s"))+")"; - int text_width = 0; - int text_height = 0; - dc.GetTextExtent(label,&text_width,&text_height); - dc.DrawText(label,wxPoint(0.5*(m_rect.GetRight()+m_rect.GetLeft())-text_width/2.f, m_rect.GetBottom()+25)); - label = _(L("Volumetric speed")) + " (" + _(L("mm")) + wxString("³/", wxConvUTF8) + _(L("s")) + ")"; - dc.GetTextExtent(label,&text_width,&text_height); - dc.DrawRotatedText(label,wxPoint(0,0.5*(m_rect.GetBottom()+m_rect.GetTop())+text_width/2.f),90); -} - -void Chart::mouse_right_button_clicked(wxMouseEvent& event) { - if (!manual_points_manipulation) - return; - wxPoint point = event.GetPosition(); - int button_index = which_button_is_clicked(point); - if (button_index != -1 && m_buttons.size()>2) { - m_buttons.erase(m_buttons.begin()+button_index); - recalculate_line(); - } -} - - - -void Chart::mouse_clicked(wxMouseEvent& event) { - wxPoint point = event.GetPosition(); - int button_index = which_button_is_clicked(point); - if ( button_index != -1) { - m_dragged = &m_buttons[button_index]; - m_previous_mouse = point; - } -} - - - -void Chart::mouse_moved(wxMouseEvent& event) { - if (!event.Dragging() || !m_dragged) return; - wxPoint pos = event.GetPosition(); - wxRect rect = m_rect; - rect.Deflate(side/2.); - if (!(rect.Contains(pos))) { // the mouse left chart area - mouse_left_window(event); - return; - } - int delta_x = pos.x - m_previous_mouse.x; - int delta_y = pos.y - m_previous_mouse.y; - m_dragged->move(fixed_x?0:double(delta_x)/m_rect.GetWidth() * visible_area.m_width,-double(delta_y)/m_rect.GetHeight() * visible_area.m_height); - m_previous_mouse = pos; - recalculate_line(); -} - - - -void Chart::mouse_double_clicked(wxMouseEvent& event) { - if (!manual_points_manipulation) - return; - wxPoint point = event.GetPosition(); - if (!m_rect.Contains(point)) // the click is outside the chart - return; - m_buttons.push_back(screen_to_math(point)); - std::sort(m_buttons.begin(),m_buttons.end()); - recalculate_line(); - return; -} - - - - -void Chart::recalculate_line() { - std::vector<wxPoint> points; - for (auto& but : m_buttons) { - points.push_back(wxPoint(math_to_screen(but.get_pos()))); - if (points.size()>1 && points.back().x==points[points.size()-2].x) points.pop_back(); - if (points.size()>1 && points.back().x > m_rect.GetRight()) { - points.pop_back(); - break; - } - } - std::sort(points.begin(),points.end(),[](wxPoint& a,wxPoint& b) { return a.x < b.x; }); - - m_line_to_draw.clear(); - m_total_volume = 0.f; - - - // Cubic spline interpolation: see https://en.wikiversity.org/wiki/Cubic_Spline_Interpolation#Methods - const bool boundary_first_derivative = true; // true - first derivative is 0 at the leftmost and rightmost point - // false - second ---- || ------- - const int N = points.size()-1; // last point can be accessed as N, we have N+1 total points - std::vector<float> diag(N+1); - std::vector<float> mu(N+1); - std::vector<float> lambda(N+1); - std::vector<float> h(N+1); - std::vector<float> rhs(N+1); - - // let's fill in inner equations - for (int i=1;i<=N;++i) h[i] = points[i].x-points[i-1].x; - std::fill(diag.begin(),diag.end(),2.f); - for (int i=1;i<=N-1;++i) { - mu[i] = h[i]/(h[i]+h[i+1]); - lambda[i] = 1.f - mu[i]; - rhs[i] = 6 * ( float(points[i+1].y-points[i].y )/(h[i+1]*(points[i+1].x-points[i-1].x)) - - float(points[i].y -points[i-1].y)/(h[i] *(points[i+1].x-points[i-1].x)) ); - } - - // now fill in the first and last equations, according to boundary conditions: - if (boundary_first_derivative) { - const float endpoints_derivative = 0; - lambda[0] = 1; - mu[N] = 1; - rhs[0] = (6.f/h[1]) * (float(points[0].y-points[1].y)/(points[0].x-points[1].x) - endpoints_derivative); - rhs[N] = (6.f/h[N]) * (endpoints_derivative - float(points[N-1].y-points[N].y)/(points[N-1].x-points[N].x)); - } - else { - lambda[0] = 0; - mu[N] = 0; - rhs[0] = 0; - rhs[N] = 0; - } - - // the trilinear system is ready to be solved: - for (int i=1;i<=N;++i) { - float multiple = mu[i]/diag[i-1]; // let's subtract proper multiple of above equation - diag[i]-= multiple * lambda[i-1]; - rhs[i] -= multiple * rhs[i-1]; - } - // now the back substitution (vector mu contains invalid values from now on): - rhs[N] = rhs[N]/diag[N]; - for (int i=N-1;i>=0;--i) - rhs[i] = (rhs[i]-lambda[i]*rhs[i+1])/diag[i]; - - - - - unsigned int i=1; - float y=0.f; - for (int x=m_rect.GetLeft(); x<=m_rect.GetRight() ; ++x) { - if (splines) { - if (i<points.size()-1 && points[i].x < x ) { - ++i; - } - if (points[0].x > x) - y = points[0].y; - else - if (points[N].x < x) - y = points[N].y; - else - y = (rhs[i-1]*pow(points[i].x-x,3)+rhs[i]*pow(x-points[i-1].x,3)) / (6*h[i]) + - (points[i-1].y-rhs[i-1]*h[i]*h[i]/6.f) * (points[i].x-x)/h[i] + - (points[i].y -rhs[i] *h[i]*h[i]/6.f) * (x-points[i-1].x)/h[i]; - m_line_to_draw.push_back(y); - } - else { - float x_math = screen_to_math(wxPoint(x,0)).m_x; - if (i+2<=points.size() && m_buttons[i+1].get_pos().m_x-0.125 < x_math) - ++i; - m_line_to_draw.push_back(math_to_screen(wxPoint2DDouble(x_math,m_buttons[i].get_pos().m_y)).y); - } - - - m_line_to_draw.back() = std::max(m_line_to_draw.back(), m_rect.GetTop()-1); - m_line_to_draw.back() = std::min(m_line_to_draw.back(), m_rect.GetBottom()-1); - m_total_volume += (m_rect.GetBottom() - m_line_to_draw.back()) * (visible_area.m_width / m_rect.GetWidth()) * (visible_area.m_height / m_rect.GetHeight()); - } - - wxPostEvent(this->GetParent(), wxCommandEvent(EVT_WIPE_TOWER_CHART_CHANGED)); - Refresh(); -} - - - -std::vector<float> Chart::get_ramming_speed(float sampling) const { - std::vector<float> speeds_out; - - const int number_of_samples = std::round( visible_area.m_width / sampling); - if (number_of_samples>0) { - const int dx = (m_line_to_draw.size()-1) / number_of_samples; - for (int j=0;j<number_of_samples;++j) { - float left = screen_to_math(wxPoint(0,m_line_to_draw[j*dx])).m_y; - float right = screen_to_math(wxPoint(0,m_line_to_draw[(j+1)*dx])).m_y; - speeds_out.push_back((left+right)/2.f); - } - } - return speeds_out; -} - - -std::vector<std::pair<float,float>> Chart::get_buttons() const { - std::vector<std::pair<float, float>> buttons_out; - for (const auto& button : m_buttons) - buttons_out.push_back(std::make_pair(float(button.get_pos().m_x),float(button.get_pos().m_y))); - return buttons_out; -} - - - - -BEGIN_EVENT_TABLE(Chart, wxWindow) -EVT_MOTION(Chart::mouse_moved) -EVT_LEFT_DOWN(Chart::mouse_clicked) -EVT_LEFT_UP(Chart::mouse_released) -EVT_LEFT_DCLICK(Chart::mouse_double_clicked) -EVT_RIGHT_DOWN(Chart::mouse_right_button_clicked) -EVT_LEAVE_WINDOW(Chart::mouse_left_window) -EVT_PAINT(Chart::paint_event) -END_EVENT_TABLE() diff --git a/xs/src/slic3r/GUI/RammingChart.hpp b/xs/src/slic3r/GUI/RammingChart.hpp deleted file mode 100644 index 7d3b9a962..000000000 --- a/xs/src/slic3r/GUI/RammingChart.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef RAMMING_CHART_H_ -#define RAMMING_CHART_H_ - -#include <vector> -#include <wx/wxprec.h> -#ifndef WX_PRECOMP - #include <wx/wx.h> -#endif - -wxDECLARE_EVENT(EVT_WIPE_TOWER_CHART_CHANGED, wxCommandEvent); - - -class Chart : public wxWindow { - -public: - Chart(wxWindow* parent, wxRect rect,const std::vector<std::pair<float,float>>& initial_buttons,int ramming_speed_size, float sampling) : - wxWindow(parent,wxID_ANY,rect.GetTopLeft(),rect.GetSize()) - { - SetBackgroundStyle(wxBG_STYLE_PAINT); - m_rect = wxRect(wxPoint(50,0),rect.GetSize()-wxSize(50,50)); - visible_area = wxRect2DDouble(0.0, 0.0, sampling*ramming_speed_size, 20.); - m_buttons.clear(); - if (initial_buttons.size()>0) - for (const auto& pair : initial_buttons) - m_buttons.push_back(wxPoint2DDouble(pair.first,pair.second)); - recalculate_line(); - } - void set_xy_range(float x,float y) { - x = int(x/0.5) * 0.5; - if (x>=0) visible_area.SetRight(x); - if (y>=0) visible_area.SetBottom(y); - recalculate_line(); - } - float get_volume() const { return m_total_volume; } - float get_time() const { return visible_area.m_width; } - - std::vector<float> get_ramming_speed(float sampling) const; //returns sampled ramming speed - std::vector<std::pair<float,float>> get_buttons() const; // returns buttons position - - void draw(); - - void mouse_clicked(wxMouseEvent& event); - void mouse_right_button_clicked(wxMouseEvent& event); - void mouse_moved(wxMouseEvent& event); - void mouse_double_clicked(wxMouseEvent& event); - void mouse_left_window(wxMouseEvent&) { m_dragged = nullptr; } - void mouse_released(wxMouseEvent&) { m_dragged = nullptr; } - void paint_event(wxPaintEvent&) { draw(); } - DECLARE_EVENT_TABLE() - - - -private: - static const bool fixed_x = true; - static const bool splines = true; - static const bool manual_points_manipulation = false; - static const int side = 10; // side of draggable button - - class ButtonToDrag { - public: - bool operator<(const ButtonToDrag& a) const { return m_pos.m_x < a.m_pos.m_x; } - ButtonToDrag(wxPoint2DDouble pos) : m_pos{pos} {}; - wxPoint2DDouble get_pos() const { return m_pos; } - void move(double x,double y) { m_pos.m_x+=x; m_pos.m_y+=y; } - private: - wxPoint2DDouble m_pos; // position in math coordinates - }; - - - - wxPoint math_to_screen(const wxPoint2DDouble& math) const { - wxPoint screen; - screen.x = (math.m_x-visible_area.m_x) * (m_rect.GetWidth() / visible_area.m_width ); - screen.y = (math.m_y-visible_area.m_y) * (m_rect.GetHeight() / visible_area.m_height ); - screen.y *= -1; - screen += m_rect.GetLeftBottom(); - return screen; - } - wxPoint2DDouble screen_to_math(const wxPoint& screen) const { - wxPoint2DDouble math = screen; - math -= m_rect.GetLeftBottom(); - math.m_y *= -1; - math.m_x *= visible_area.m_width / m_rect.GetWidth(); // scales to [0;1]x[0,1] - math.m_y *= visible_area.m_height / m_rect.GetHeight(); - return (math+visible_area.GetLeftTop()); - } - - int which_button_is_clicked(const wxPoint& point) const { - if (!m_rect.Contains(point)) - return -1; - for (unsigned int i=0;i<m_buttons.size();++i) { - wxRect rect(math_to_screen(m_buttons[i].get_pos())-wxPoint(side/2.,side/2.),wxSize(side,side)); // bounding rectangle of this button - if ( rect.Contains(point) ) - return i; - } - return (-1); - } - - - void recalculate_line(); - void recalculate_volume(); - - - wxRect m_rect; // rectangle on screen the chart is mapped into (screen coordinates) - wxPoint m_previous_mouse; - std::vector<ButtonToDrag> m_buttons; - std::vector<int> m_line_to_draw; - wxRect2DDouble visible_area; - ButtonToDrag* m_dragged = nullptr; - float m_total_volume = 0.f; - -}; - - -#endif // RAMMING_CHART_H_
\ No newline at end of file diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp deleted file mode 100644 index e0db63803..000000000 --- a/xs/src/slic3r/GUI/Tab.cpp +++ /dev/null @@ -1,3033 +0,0 @@ -#include "../../libslic3r/GCodeSender.hpp" -#include "Tab.hpp" -#include "PresetBundle.hpp" -#include "PresetHints.hpp" -#include "../../libslic3r/Utils.hpp" - -#include "slic3r/Utils/Http.hpp" -#include "slic3r/Utils/PrintHost.hpp" -#include "slic3r/Utils/Serial.hpp" -#include "BonjourDialog.hpp" -#include "WipeTowerDialog.hpp" -#include "ButtonsDescription.hpp" - -#include <wx/app.h> -#include <wx/button.h> -#include <wx/scrolwin.h> -#include <wx/sizer.h> - -#include <wx/bmpcbox.h> -#include <wx/bmpbuttn.h> -#include <wx/treectrl.h> -#include <wx/imaglist.h> -#include <wx/settings.h> -#include <wx/filedlg.h> - -#include <boost/algorithm/string/predicate.hpp> -#include "wxExtensions.hpp" -#include <wx/wupdlock.h> - -#include <chrono> - -namespace Slic3r { -namespace GUI { - -static wxString dots("…", wxConvUTF8); - -// sub new -void Tab::create_preset_tab(PresetBundle *preset_bundle) -{ - m_preset_bundle = preset_bundle; - - // Vertical sizer to hold the choice menu and the rest of the page. -#ifdef __WXOSX__ - auto *main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->SetSizeHints(this); - this->SetSizer(main_sizer); - - // Create additional panel to Fit() it from OnActivate() - // It's needed for tooltip showing on OSX - m_tmp_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL); - auto panel = m_tmp_panel; - auto sizer = new wxBoxSizer(wxVERTICAL); - m_tmp_panel->SetSizer(sizer); - m_tmp_panel->Layout(); - - main_sizer->Add(m_tmp_panel, 1, wxEXPAND | wxALL, 0); -#else - Tab *panel = this; - auto *sizer = new wxBoxSizer(wxVERTICAL); - sizer->SetSizeHints(panel); - panel->SetSizer(sizer); -#endif //__WXOSX__ - - // preset chooser - m_presets_choice = new wxBitmapComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxSize(270, -1), 0, 0,wxCB_READONLY); - - auto color = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - - //buttons - wxBitmap bmpMenu; - bmpMenu = wxBitmap(from_u8(Slic3r::var("disk.png")), wxBITMAP_TYPE_PNG); - m_btn_save_preset = new wxBitmapButton(panel, wxID_ANY, bmpMenu, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - if (wxMSW) m_btn_save_preset->SetBackgroundColour(color); - bmpMenu = wxBitmap(from_u8(Slic3r::var("delete.png")), wxBITMAP_TYPE_PNG); - m_btn_delete_preset = new wxBitmapButton(panel, wxID_ANY, bmpMenu, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - if (wxMSW) m_btn_delete_preset->SetBackgroundColour(color); - - m_show_incompatible_presets = false; - m_bmp_show_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-red-icon.png")), wxBITMAP_TYPE_PNG); - m_bmp_hide_incompatible_presets.LoadFile(from_u8(Slic3r::var("flag-green-icon.png")), wxBITMAP_TYPE_PNG); - m_btn_hide_incompatible_presets = new wxBitmapButton(panel, wxID_ANY, m_bmp_hide_incompatible_presets, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - if (wxMSW) m_btn_hide_incompatible_presets->SetBackgroundColour(color); - - m_btn_save_preset->SetToolTip(_(L("Save current ")) + m_title); - m_btn_delete_preset->SetToolTip(_(L("Delete this preset"))); - m_btn_delete_preset->Disable(); - - m_undo_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - m_undo_to_sys_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - m_question_btn = new wxButton(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - if (wxMSW) { - m_undo_btn->SetBackgroundColour(color); - m_undo_to_sys_btn->SetBackgroundColour(color); - m_question_btn->SetBackgroundColour(color); - } - - m_question_btn->SetToolTip(_(L("Hover the cursor over buttons to find more information \n" - "or click this button."))); - - // Determine the theme color of OS (dark or light) - auto luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); - // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. - m_bmp_value_lock .LoadFile(from_u8(var("sys_lock.png")), wxBITMAP_TYPE_PNG); - m_bmp_value_unlock .LoadFile(from_u8(var(luma >= 128 ? "sys_unlock.png" : "sys_unlock_grey.png")), wxBITMAP_TYPE_PNG); - m_bmp_non_system = &m_bmp_white_bullet; - // Bitmaps to be shown on the "Undo user changes" button next to each input field. - m_bmp_value_revert .LoadFile(from_u8(var(luma >= 128 ? "action_undo.png" : "action_undo_grey.png")), wxBITMAP_TYPE_PNG); - m_bmp_white_bullet .LoadFile(from_u8(var("bullet_white.png")), wxBITMAP_TYPE_PNG); - m_bmp_question .LoadFile(from_u8(var("question_mark_01.png")), wxBITMAP_TYPE_PNG); - - fill_icon_descriptions(); - set_tooltips_text(); - - m_undo_btn->SetBitmap(m_bmp_white_bullet); - m_undo_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(); })); - m_undo_to_sys_btn->SetBitmap(m_bmp_white_bullet); - m_undo_to_sys_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent){ on_roll_back_value(true); })); - m_question_btn->SetBitmap(m_bmp_question); - m_question_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent) - { - auto dlg = new ButtonsDescription(this, &m_icon_descriptions); - if (dlg->ShowModal() == wxID_OK){ - // Colors for ui "decoration" - for (Tab *tab : get_tabs_list()){ - tab->m_sys_label_clr = get_label_clr_sys(); - tab->m_modified_label_clr = get_label_clr_modified(); - tab->update_labels_colour(); - } - } - })); - - // Colors for ui "decoration" - m_sys_label_clr = get_label_clr_sys(); - m_modified_label_clr = get_label_clr_modified(); - m_default_text_clr = get_label_clr_default(); - - m_hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_hsizer, 0, wxBOTTOM, 3); - m_hsizer->Add(m_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); - m_hsizer->AddSpacer(4); - m_hsizer->Add(m_btn_save_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(4); - m_hsizer->Add(m_btn_delete_preset, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(16); - m_hsizer->Add(m_btn_hide_incompatible_presets, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(64); - m_hsizer->Add(m_undo_to_sys_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->Add(m_undo_btn, 0, wxALIGN_CENTER_VERTICAL); - m_hsizer->AddSpacer(32); - m_hsizer->Add(m_question_btn, 0, wxALIGN_CENTER_VERTICAL); -// m_hsizer->Add(m_cc_presets_choice, 1, wxLEFT | wxRIGHT | wxTOP | wxALIGN_CENTER_VERTICAL, 3); - - //Horizontal sizer to hold the tree and the selected page. - m_hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(m_hsizer, 1, wxEXPAND, 0); - - //left vertical sizer - m_left_sizer = new wxBoxSizer(wxVERTICAL); - m_hsizer->Add(m_left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 3); - - // tree - m_treectrl = new wxTreeCtrl(panel, wxID_ANY, wxDefaultPosition, wxSize(185, -1), - wxTR_NO_BUTTONS | wxTR_HIDE_ROOT | wxTR_SINGLE | wxTR_NO_LINES | wxBORDER_SUNKEN | wxWANTS_CHARS); - m_left_sizer->Add(m_treectrl, 1, wxEXPAND); - m_icons = new wxImageList(16, 16, true, 1); - // Index of the last icon inserted into $self->{icons}. - m_icon_count = -1; - m_treectrl->AssignImageList(m_icons); - m_treectrl->AddRoot("root"); - m_treectrl->SetIndent(0); - m_disable_tree_sel_changed_event = 0; - - m_treectrl->Bind(wxEVT_TREE_SEL_CHANGED, &Tab::OnTreeSelChange, this); - m_treectrl->Bind(wxEVT_KEY_DOWN, &Tab::OnKeyDown, this); - - m_presets_choice->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e){ - //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, - //! but the OSX version derived from wxOwnerDrawnCombo, instead of: - //! select_preset(m_presets_choice->GetStringSelection().ToStdString()); - //! we doing next: - int selected_item = m_presets_choice->GetSelection(); - if (m_selected_preset_item == selected_item && !m_presets->current_is_dirty()) - return; - if (selected_item >= 0){ - std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); - if (selected_string.find("-------") == 0 - /*selected_string == "------- System presets -------" || - selected_string == "------- User presets -------"*/){ - m_presets_choice->SetSelection(m_selected_preset_item); - return; - } - m_selected_preset_item = selected_item; - select_preset(selected_string); - } - })); - - m_btn_save_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ save_preset(); })); - m_btn_delete_preset->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ delete_preset(); })); - m_btn_hide_incompatible_presets->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e){ - toggle_show_hide_incompatible(); - })); - - // Initialize the DynamicPrintConfig by default keys/values. - build(); - rebuild_page_tree(); - update(); -} - -void Tab::load_initial_data() -{ - m_config = &m_presets->get_edited_preset().config; - m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet; - m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns; - m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; -} - -Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages /*= false*/) -{ - // Index of icon in an icon list $self->{icons}. - auto icon_idx = 0; - if (!icon.empty()) { - icon_idx = (m_icon_index.find(icon) == m_icon_index.end()) ? -1 : m_icon_index.at(icon); - if (icon_idx == -1) { - // Add a new icon to the icon list. - const auto img_icon = new wxIcon(from_u8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG); - m_icons->Add(*img_icon); - icon_idx = ++m_icon_count; - m_icon_index[icon] = icon_idx; - } - } - // Initialize the page. -#ifdef __WXOSX__ - auto panel = m_tmp_panel; -#else - auto panel = this; -#endif - PageShp page(new Page(panel, title, icon_idx)); - page->SetScrollbars(1, 1, 1, 1); - page->Hide(); - m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); - - if (!is_extruder_pages) - m_pages.push_back(page); - - page->set_config(m_config); - return page; -} - -void Tab::OnActivate() -{ -#ifdef __WXOSX__ - wxWindowUpdateLocker noUpdates(this); - - auto size = GetSizer()->GetSize(); - m_tmp_panel->GetSizer()->SetMinSize(size.x + m_size_move, size.y); - Fit(); - m_size_move *= -1; -#endif // __WXOSX__ -} - -void Tab::update_labels_colour() -{ - Freeze(); - //update options "decoration" - for (const auto opt : m_options_list) - { - const wxColour *color = &m_sys_label_clr; - - // value isn't equal to system value - if ((opt.second & osSystemValue) == 0){ - // value is equal to last saved - if ((opt.second & osInitValue) != 0) - color = &m_default_text_clr; - // value is modified - else - color = &m_modified_label_clr; - } - if (opt.first == "bed_shape" || opt.first == "compatible_printers") { - if (m_colored_Label != nullptr) { - m_colored_Label->SetForegroundColour(*color); - m_colored_Label->Refresh(true); - } - continue; - } - - Field* field = get_field(opt.first); - if (field == nullptr) continue; - field->set_label_colour_force(color); - } - Thaw(); - - auto cur_item = m_treectrl->GetFirstVisibleItem(); - while (cur_item){ - auto title = m_treectrl->GetItemText(cur_item); - for (auto page : m_pages) - { - if (page->title() != title) - continue; - - const wxColor *clr = !page->m_is_nonsys_values ? &m_sys_label_clr : - page->m_is_modified_values ? &m_modified_label_clr : - &m_default_text_clr; - - m_treectrl->SetItemTextColour(cur_item, *clr); - break; - } - cur_item = m_treectrl->GetNextVisible(cur_item); - } -} - -// Update UI according to changes -void Tab::update_changed_ui() -{ - if (m_postpone_update_ui) - return; - - const bool deep_compare = (m_name == "printer" || m_name == "sla_material"); - auto dirty_options = m_presets->current_dirty_options(deep_compare); - auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare); - if (name() == "printer"){ - TabPrinter* tab = static_cast<TabPrinter*>(this); - if (tab->m_initial_extruders_count != tab->m_extruders_count) - dirty_options.emplace_back("extruders_count"); - if (tab->m_sys_extruders_count != tab->m_extruders_count) - nonsys_options.emplace_back("extruders_count"); - } - - for (auto& it : m_options_list) - it.second = m_opt_status_value; - - for (auto opt_key : dirty_options) m_options_list[opt_key] &= ~osInitValue; - for (auto opt_key : nonsys_options) m_options_list[opt_key] &= ~osSystemValue; - - Freeze(); - //update options "decoration" - for (const auto opt : m_options_list) - { - bool is_nonsys_value = false; - bool is_modified_value = true; - const wxBitmap *sys_icon = &m_bmp_value_lock; - const wxBitmap *icon = &m_bmp_value_revert; - - const wxColour *color = &m_sys_label_clr; - - const wxString *sys_tt = &m_tt_value_lock; - const wxString *tt = &m_tt_value_revert; - - // value isn't equal to system value - if ((opt.second & osSystemValue) == 0){ - is_nonsys_value = true; - sys_icon = m_bmp_non_system; - sys_tt = m_tt_non_system; - // value is equal to last saved - if ((opt.second & osInitValue) != 0) - color = &m_default_text_clr; - // value is modified - else - color = &m_modified_label_clr; - } - if ((opt.second & osInitValue) != 0) - { - is_modified_value = false; - icon = &m_bmp_white_bullet; - tt = &m_tt_white_bullet; - } - if (opt.first == "bed_shape" || opt.first == "compatible_printers") { - if (m_colored_Label != nullptr) { - m_colored_Label->SetForegroundColour(*color); - m_colored_Label->Refresh(true); - } - continue; - } - - Field* field = get_field(opt.first); - if (field == nullptr) continue; - field->m_is_nonsys_value = is_nonsys_value; - field->m_is_modified_value = is_modified_value; - field->set_undo_bitmap(icon); - field->set_undo_to_sys_bitmap(sys_icon); - field->set_undo_tooltip(tt); - field->set_undo_to_sys_tooltip(sys_tt); - field->set_label_colour(color); - } - Thaw(); - - wxTheApp->CallAfter([this]() { - update_changed_tree_ui(); - }); -} - -void Tab::init_options_list() -{ - if (!m_options_list.empty()) - m_options_list.clear(); - - for (const auto opt_key : m_config->keys()) - m_options_list.emplace(opt_key, m_opt_status_value); -} - -template<class T> -void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, Tab *tab, const int& value) -{ - T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key)); - for (int i = 0; i < opt_cur->values.size(); i++) - map.emplace(opt_key + "#" + std::to_string(i), value); -} - -void TabPrinter::init_options_list() -{ - if (!m_options_list.empty()) - m_options_list.clear(); - - for (const auto opt_key : m_config->keys()) - { - if (opt_key == "bed_shape"){ - m_options_list.emplace(opt_key, m_opt_status_value); - continue; - } - switch (m_config->option(opt_key)->type()) - { - case coInts: add_correct_opts_to_options_list<ConfigOptionInts >(opt_key, m_options_list, this, m_opt_status_value); break; - case coBools: add_correct_opts_to_options_list<ConfigOptionBools >(opt_key, m_options_list, this, m_opt_status_value); break; - case coFloats: add_correct_opts_to_options_list<ConfigOptionFloats >(opt_key, m_options_list, this, m_opt_status_value); break; - case coStrings: add_correct_opts_to_options_list<ConfigOptionStrings >(opt_key, m_options_list, this, m_opt_status_value); break; - case coPercents:add_correct_opts_to_options_list<ConfigOptionPercents >(opt_key, m_options_list, this, m_opt_status_value); break; - case coPoints: add_correct_opts_to_options_list<ConfigOptionPoints >(opt_key, m_options_list, this, m_opt_status_value); break; - default: m_options_list.emplace(opt_key, m_opt_status_value); break; - } - } - m_options_list.emplace("extruders_count", m_opt_status_value); -} - -void TabSLAMaterial::init_options_list() -{ - if (!m_options_list.empty()) - m_options_list.clear(); - - for (const auto opt_key : m_config->keys()) - { - if (opt_key == "compatible_printers"){ - m_options_list.emplace(opt_key, m_opt_status_value); - continue; - } - switch (m_config->option(opt_key)->type()) - { - case coInts: add_correct_opts_to_options_list<ConfigOptionInts >(opt_key, m_options_list, this, m_opt_status_value); break; - case coBools: add_correct_opts_to_options_list<ConfigOptionBools >(opt_key, m_options_list, this, m_opt_status_value); break; - case coFloats: add_correct_opts_to_options_list<ConfigOptionFloats >(opt_key, m_options_list, this, m_opt_status_value); break; - case coStrings: add_correct_opts_to_options_list<ConfigOptionStrings >(opt_key, m_options_list, this, m_opt_status_value); break; - case coPercents:add_correct_opts_to_options_list<ConfigOptionPercents >(opt_key, m_options_list, this, m_opt_status_value); break; - case coPoints: add_correct_opts_to_options_list<ConfigOptionPoints >(opt_key, m_options_list, this, m_opt_status_value); break; - default: m_options_list.emplace(opt_key, m_opt_status_value); break; - } - } -} - -void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page) -{ - auto opt = m_options_list.find(opt_key); - if (sys_page) sys_page = (opt->second & osSystemValue) != 0; - if (!modified_page) modified_page = (opt->second & osInitValue) == 0; -} - -void Tab::update_changed_tree_ui() -{ - auto cur_item = m_treectrl->GetFirstVisibleItem(); - auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - while (cur_item){ - auto title = m_treectrl->GetItemText(cur_item); - for (auto page : m_pages) - { - if (page->title() != title) - continue; - bool sys_page = true; - bool modified_page = false; - if (title == _("General")){ - std::initializer_list<const char*> optional_keys{ "extruders_count", "bed_shape" }; - for (auto &opt_key : optional_keys) { - get_sys_and_mod_flags(opt_key, sys_page, modified_page); - } - } - if (title == _("Dependencies")){ - if (name() != "printer") - get_sys_and_mod_flags("compatible_printers", sys_page, modified_page); - else { - sys_page = m_presets->get_selected_preset_parent() ? true:false; - modified_page = false; - } - } - for (auto group : page->m_optgroups) - { - if (!sys_page && modified_page) - break; - for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { - const std::string& opt_key = it->first; - get_sys_and_mod_flags(opt_key, sys_page, modified_page); - } - } - - const wxColor *clr = sys_page ? &m_sys_label_clr : - modified_page ? &m_modified_label_clr : - &m_default_text_clr; - - if (page->set_item_colour(clr)) - m_treectrl->SetItemTextColour(cur_item, *clr); - - page->m_is_nonsys_values = !sys_page; - page->m_is_modified_values = modified_page; - - if (selection == title){ - m_is_nonsys_values = page->m_is_nonsys_values; - m_is_modified_values = page->m_is_modified_values; - } - break; - } - auto next_item = m_treectrl->GetNextVisible(cur_item); - cur_item = next_item; - } - update_undo_buttons(); -} - -void Tab::update_undo_buttons() -{ - m_undo_btn->SetBitmap(m_is_modified_values ? m_bmp_value_revert : m_bmp_white_bullet); - m_undo_to_sys_btn->SetBitmap(m_is_nonsys_values ? *m_bmp_non_system : m_bmp_value_lock); - - m_undo_btn->SetToolTip(m_is_modified_values ? m_ttg_value_revert : m_ttg_white_bullet); - m_undo_to_sys_btn->SetToolTip(m_is_nonsys_values ? *m_ttg_non_system : m_ttg_value_lock); -} - -void Tab::on_roll_back_value(const bool to_sys /*= true*/) -{ - int os; - if (to_sys) { - if (!m_is_nonsys_values) return; - os = osSystemValue; - } - else { - if (!m_is_modified_values) return; - os = osInitValue; - } - - m_postpone_update_ui = true; - - auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - for (auto page : m_pages) - if (page->title() == selection) { - for (auto group : page->m_optgroups){ - if (group->title == _("Capabilities")){ - if ((m_options_list["extruders_count"] & os) == 0) - to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); - } - if (group->title == _("Size and coordinates")){ - if ((m_options_list["bed_shape"] & os) == 0){ - to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); - load_key_value("bed_shape", true/*some value*/, true); - } - - } - if (group->title == _("Profile dependencies") && name() != "printer"){ - if ((m_options_list["compatible_printers"] & os) == 0){ - to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); - load_key_value("compatible_printers", true/*some value*/, true); - - bool is_empty = m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty(); - m_compatible_printers_checkbox->SetValue(is_empty); - is_empty ? m_compatible_printers_btn->Disable() : m_compatible_printers_btn->Enable(); - } - } - for (t_opt_map::iterator it = group->m_opt_map.begin(); it != group->m_opt_map.end(); ++it) { - const std::string& opt_key = it->first; - if ((m_options_list[opt_key] & os) == 0) - to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); - } - } - break; - } - - m_postpone_update_ui = false; - update_changed_ui(); -} - -// Update the combo box label of the selected preset based on its "dirty" state, -// comparing the selected preset config with $self->{config}. -void Tab::update_dirty(){ - m_presets->update_dirty_ui(m_presets_choice); - on_presets_changed(); - update_changed_ui(); -// update_dirty_presets(m_cc_presets_choice); -} - -void Tab::update_tab_ui() -{ - m_selected_preset_item = m_presets->update_tab_ui(m_presets_choice, m_show_incompatible_presets); -// update_tab_presets(m_cc_presets_choice, m_show_incompatible_presets); -// update_presetsctrl(m_presetctrl, m_show_incompatible_presets); -} - -// Load a provied DynamicConfig into the tab, modifying the active preset. -// This could be used for example by setting a Wipe Tower position by interactive manipulation in the 3D view. -void Tab::load_config(const DynamicPrintConfig& config) -{ - bool modified = 0; - for(auto opt_key : m_config->diff(config)) { - m_config->set_key_value(opt_key, config.option(opt_key)->clone()); - modified = 1; - } - if (modified) { - update_dirty(); - //# Initialize UI components with the config values. - reload_config(); - update(); - } -} - -// Reload current $self->{config} (aka $self->{presets}->edited_preset->config) into the UI fields. -void Tab::reload_config(){ - Freeze(); - for (auto page : m_pages) - page->reload_config(); - Thaw(); -} - -Field* Tab::get_field(const t_config_option_key& opt_key, int opt_index/* = -1*/) const -{ - Field* field = nullptr; - for (auto page : m_pages){ - field = page->get_field(opt_key, opt_index); - if (field != nullptr) - return field; - } - return field; -} - -// Set a key/value pair on this page. Return true if the value has been modified. -// Currently used for distributing extruders_count over preset pages of Slic3r::GUI::Tab::Printer -// after a preset is loaded. -bool Tab::set_value(const t_config_option_key& opt_key, const boost::any& value){ - bool changed = false; - for(auto page: m_pages) { - if (page->set_value(opt_key, value)) - changed = true; - } - return changed; -} - -// To be called by custom widgets, load a value into a config, -// update the preset selection boxes (the dirty flags) -// If value is saved before calling this function, put saved_value = true, -// and value can be some random value because in this case it will not been used -void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value /*= false*/) -{ - if (!saved_value) change_opt_value(*m_config, opt_key, value); - // Mark the print & filament enabled if they are compatible with the currently selected preset. - if (opt_key.compare("compatible_printers") == 0) { - // Don't select another profile if this profile happens to become incompatible. - m_preset_bundle->update_compatible_with_printer(false); - } - m_presets->update_dirty_ui(m_presets_choice); - on_presets_changed(); - update(); -} - -extern wxFrame *g_wxMainFrame; - -void Tab::on_value_change(const std::string& opt_key, const boost::any& value) -{ - if (m_event_value_change > 0) { - wxCommandEvent event(m_event_value_change); - std::string str_out = opt_key + " " + m_name; - event.SetString(str_out); - if (opt_key == "extruders_count") - { - int val = boost::any_cast<size_t>(value); - event.SetInt(val); - } - - if (opt_key == "printer_technology") - { - int val = boost::any_cast<PrinterTechnology>(value); - event.SetInt(val); - g_wxMainFrame->ProcessWindowEvent(event); - return; - } - - g_wxMainFrame->ProcessWindowEvent(event); - } - if (opt_key == "fill_density") - { - boost::any val = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, opt_key); - get_optgroup(ogFrequentlyChangingParameters)->set_value(opt_key, val); - } - if (opt_key == "support_material" || opt_key == "support_material_buildplate_only") - { - wxString new_selection = !m_config->opt_bool("support_material") ? - _("None") : - m_config->opt_bool("support_material_buildplate_only") ? - _("Support on build plate only") : - _("Everywhere"); - get_optgroup(ogFrequentlyChangingParameters)->set_value("support", new_selection); - } - if (opt_key == "brim_width") - { - bool val = m_config->opt_float("brim_width") > 0.0 ? true : false; - get_optgroup(ogFrequentlyChangingParameters)->set_value("brim", val); - } - - if (opt_key == "wipe_tower" || opt_key == "single_extruder_multi_material" || opt_key == "extruders_count" ) - update_wiping_button_visibility(); - - update(); -} - -// Show/hide the 'purging volumes' button -void Tab::update_wiping_button_visibility() { - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) - return; // ys_FIXME - bool wipe_tower_enabled = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value; - bool multiple_extruders = dynamic_cast<ConfigOptionFloats*>((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1; - bool single_extruder_mm = dynamic_cast<ConfigOptionBool*>( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value; - - get_wiping_dialog_button()->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm); - - (get_wiping_dialog_button()->GetParent())->Layout(); -} - - -// Call a callback to update the selection of presets on the platter: -// 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, -// to uddate number of "filament" selection boxes when the number of extruders change. -void Tab::on_presets_changed() -{ - if (m_event_presets_changed > 0) { - wxCommandEvent event(m_event_presets_changed); - event.SetString(m_name); - g_wxMainFrame->ProcessWindowEvent(event); - } - update_preset_description_line(); -} - -void Tab::update_preset_description_line() -{ - const Preset* parent = m_presets->get_selected_preset_parent(); - const Preset& preset = m_presets->get_edited_preset(); - - wxString description_line = preset.is_default ? - _(L("It's a default preset.")) : preset.is_system ? - _(L("It's a system preset.")) : - _(L("Current preset is inherited from ")) + (parent == nullptr ? - "default preset." : - ":\n\t" + parent->name); - - if (preset.is_default || preset.is_system) - description_line += "\n\t" + _(L("It can't be deleted or modified. ")) + - "\n\t" + _(L("Any modifications should be saved as a new preset inherited from this one. ")) + - "\n\t" + _(L("To do that please specify a new name for the preset.")); - - if (parent && parent->vendor) - { - description_line += "\n\n" + _(L("Additional information:")) + "\n"; - description_line += "\t" + _(L("vendor")) + ": " + (name()=="printer" ? "\n\t\t" : "") + parent->vendor->name + - ", ver: " + parent->vendor->config_version.to_string(); - if (name() == "printer"){ - const std::string &printer_model = preset.config.opt_string("printer_model"); - const std::string &default_print_profile = preset.config.opt_string("default_print_profile"); - const std::vector<std::string> &default_filament_profiles = preset.config.option<ConfigOptionStrings>("default_filament_profile")->values; - if (!printer_model.empty()) - description_line += "\n\n\t" + _(L("printer model")) + ": \n\t\t" + printer_model; - if (!default_print_profile.empty()) - description_line += "\n\n\t" + _(L("default print profile")) + ": \n\t\t" + default_print_profile; - if (!default_filament_profiles.empty()) - { - description_line += "\n\n\t" + _(L("default filament profile")) + ": \n\t\t"; - for (auto& profile : default_filament_profiles){ - if (&profile != &*default_filament_profiles.begin()) - description_line += ", "; - description_line += profile; - } - } - } - } - - m_parent_preset_description_line->SetText(description_line, false); -} - -void Tab::update_frequently_changed_parameters() -{ - boost::any value = get_optgroup(ogFrequentlyChangingParameters)->get_config_value(*m_config, "fill_density"); - get_optgroup(ogFrequentlyChangingParameters)->set_value("fill_density", value); - - wxString new_selection = !m_config->opt_bool("support_material") ? - _("None") : - m_config->opt_bool("support_material_buildplate_only") ? - _("Support on build plate only") : - _("Everywhere"); - get_optgroup(ogFrequentlyChangingParameters)->set_value("support", new_selection); - - bool val = m_config->opt_float("brim_width") > 0.0 ? true : false; - get_optgroup(ogFrequentlyChangingParameters)->set_value("brim", val); - - update_wiping_button_visibility(); -} - -void Tab::reload_compatible_printers_widget() -{ - bool has_any = !m_config->option<ConfigOptionStrings>("compatible_printers")->values.empty(); - has_any ? m_compatible_printers_btn->Enable() : m_compatible_printers_btn->Disable(); - m_compatible_printers_checkbox->SetValue(!has_any); - get_field("compatible_printers_condition")->toggle(!has_any); -} - -void TabPrint::build() -{ - m_presets = &m_preset_bundle->prints; - load_initial_data(); - - auto page = add_options_page(_(L("Layers and perimeters")), "layers.png"); - auto optgroup = page->new_optgroup(_(L("Layer height"))); - optgroup->append_single_option_line("layer_height"); - optgroup->append_single_option_line("first_layer_height"); - - optgroup = page->new_optgroup(_(L("Vertical shells"))); - optgroup->append_single_option_line("perimeters"); - optgroup->append_single_option_line("spiral_vase"); - - Line line { "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_recommended_thin_wall_thickness_description_line); - }; - optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Horizontal shells"))); - line = { _(L("Solid layers")), "" }; - line.append_option(optgroup->get_option("top_solid_layers")); - line.append_option(optgroup->get_option("bottom_solid_layers")); - optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Quality (slower slicing)"))); - optgroup->append_single_option_line("extra_perimeters"); - optgroup->append_single_option_line("ensure_vertical_shell_thickness"); - optgroup->append_single_option_line("avoid_crossing_perimeters"); - optgroup->append_single_option_line("thin_walls"); - optgroup->append_single_option_line("overhangs"); - - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("seam_position"); - optgroup->append_single_option_line("external_perimeters_first"); - - page = add_options_page(_(L("Infill")), "infill.png"); - optgroup = page->new_optgroup(_(L("Infill"))); - optgroup->append_single_option_line("fill_density"); - optgroup->append_single_option_line("fill_pattern"); - optgroup->append_single_option_line("external_fill_pattern"); - - optgroup = page->new_optgroup(_(L("Reducing printing time"))); - optgroup->append_single_option_line("infill_every_layers"); - optgroup->append_single_option_line("infill_only_where_needed"); - - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("solid_infill_every_layers"); - optgroup->append_single_option_line("fill_angle"); - optgroup->append_single_option_line("solid_infill_below_area"); - optgroup->append_single_option_line("bridge_angle"); - optgroup->append_single_option_line("only_retract_when_crossing_perimeters"); - optgroup->append_single_option_line("infill_first"); - - page = add_options_page(_(L("Skirt and brim")), "box.png"); - optgroup = page->new_optgroup(_(L("Skirt"))); - optgroup->append_single_option_line("skirts"); - optgroup->append_single_option_line("skirt_distance"); - optgroup->append_single_option_line("skirt_height"); - optgroup->append_single_option_line("min_skirt_length"); - - optgroup = page->new_optgroup(_(L("Brim"))); - optgroup->append_single_option_line("brim_width"); - - page = add_options_page(_(L("Support material")), "building.png"); - optgroup = page->new_optgroup(_(L("Support material"))); - optgroup->append_single_option_line("support_material"); - optgroup->append_single_option_line("support_material_auto"); - optgroup->append_single_option_line("support_material_threshold"); - optgroup->append_single_option_line("support_material_enforce_layers"); - - optgroup = page->new_optgroup(_(L("Raft"))); - optgroup->append_single_option_line("raft_layers"); -// # optgroup->append_single_option_line(get_option_("raft_contact_distance"); - - optgroup = page->new_optgroup(_(L("Options for support material and raft"))); - optgroup->append_single_option_line("support_material_contact_distance"); - optgroup->append_single_option_line("support_material_pattern"); - optgroup->append_single_option_line("support_material_with_sheath"); - optgroup->append_single_option_line("support_material_spacing"); - optgroup->append_single_option_line("support_material_angle"); - optgroup->append_single_option_line("support_material_interface_layers"); - optgroup->append_single_option_line("support_material_interface_spacing"); - optgroup->append_single_option_line("support_material_interface_contact_loops"); - optgroup->append_single_option_line("support_material_buildplate_only"); - optgroup->append_single_option_line("support_material_xy_spacing"); - optgroup->append_single_option_line("dont_support_bridges"); - optgroup->append_single_option_line("support_material_synchronize_layers"); - - page = add_options_page(_(L("Speed")), "time.png"); - optgroup = page->new_optgroup(_(L("Speed for print moves"))); - optgroup->append_single_option_line("perimeter_speed"); - optgroup->append_single_option_line("small_perimeter_speed"); - optgroup->append_single_option_line("external_perimeter_speed"); - optgroup->append_single_option_line("infill_speed"); - optgroup->append_single_option_line("solid_infill_speed"); - optgroup->append_single_option_line("top_solid_infill_speed"); - optgroup->append_single_option_line("support_material_speed"); - optgroup->append_single_option_line("support_material_interface_speed"); - optgroup->append_single_option_line("bridge_speed"); - optgroup->append_single_option_line("gap_fill_speed"); - - optgroup = page->new_optgroup(_(L("Speed for non-print moves"))); - optgroup->append_single_option_line("travel_speed"); - - optgroup = page->new_optgroup(_(L("Modifiers"))); - optgroup->append_single_option_line("first_layer_speed"); - - 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("default_acceleration"); - - optgroup = page->new_optgroup(_(L("Autospeed (advanced)"))); - optgroup->append_single_option_line("max_print_speed"); - optgroup->append_single_option_line("max_volumetric_speed"); - optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_positive"); - optgroup->append_single_option_line("max_volumetric_extrusion_rate_slope_negative"); - - page = add_options_page(_(L("Multiple Extruders")), "funnel.png"); - optgroup = page->new_optgroup(_(L("Extruders"))); - optgroup->append_single_option_line("perimeter_extruder"); - optgroup->append_single_option_line("infill_extruder"); - optgroup->append_single_option_line("solid_infill_extruder"); - optgroup->append_single_option_line("support_material_extruder"); - optgroup->append_single_option_line("support_material_interface_extruder"); - - optgroup = page->new_optgroup(_(L("Ooze prevention"))); - optgroup->append_single_option_line("ooze_prevention"); - optgroup->append_single_option_line("standby_temperature_delta"); - - optgroup = page->new_optgroup(_(L("Wipe tower"))); - optgroup->append_single_option_line("wipe_tower"); - optgroup->append_single_option_line("wipe_tower_x"); - optgroup->append_single_option_line("wipe_tower_y"); - optgroup->append_single_option_line("wipe_tower_width"); - optgroup->append_single_option_line("wipe_tower_rotation_angle"); - optgroup->append_single_option_line("wipe_tower_bridging"); - optgroup->append_single_option_line("single_extruder_multi_material_priming"); - - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("interface_shells"); - - page = add_options_page(_(L("Advanced")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Extrusion width"))); - optgroup->append_single_option_line("extrusion_width"); - optgroup->append_single_option_line("first_layer_extrusion_width"); - optgroup->append_single_option_line("perimeter_extrusion_width"); - optgroup->append_single_option_line("external_perimeter_extrusion_width"); - optgroup->append_single_option_line("infill_extrusion_width"); - optgroup->append_single_option_line("solid_infill_extrusion_width"); - optgroup->append_single_option_line("top_infill_extrusion_width"); - optgroup->append_single_option_line("support_material_extrusion_width"); - - optgroup = page->new_optgroup(_(L("Overlap"))); - optgroup->append_single_option_line("infill_overlap"); - - optgroup = page->new_optgroup(_(L("Flow"))); - optgroup->append_single_option_line("bridge_flow_ratio"); - - optgroup = page->new_optgroup(_(L("Other"))); - optgroup->append_single_option_line("clip_multipart_objects"); - optgroup->append_single_option_line("elefant_foot_compensation"); - optgroup->append_single_option_line("xy_size_compensation"); -// # optgroup->append_single_option_line("threads"); - optgroup->append_single_option_line("resolution"); - - page = add_options_page(_(L("Output options")), "page_white_go.png"); - optgroup = page->new_optgroup(_(L("Sequential printing"))); - optgroup->append_single_option_line("complete_objects"); - line = { _(L("Extruder clearance (mm)")), "" }; - Option option = optgroup->get_option("extruder_clearance_radius"); - option.opt.width = 60; - line.append_option(option); - option = optgroup->get_option("extruder_clearance_height"); - option.opt.width = 60; - line.append_option(option); - optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Output file"))); - optgroup->append_single_option_line("gcode_comments"); - option = optgroup->get_option("output_filename_format"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("Post-processing scripts")), 0); - option = optgroup->get_option("post_process"); - option.opt.full_width = true; - option.opt.height = 50; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); - option = optgroup->get_option("notes"); - option.opt.full_width = true; - option.opt.height = 250; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; - line.widget = [this](wxWindow* parent){ - return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); - }; - optgroup->append_line(line, &m_colored_Label); - - option = optgroup->get_option("compatible_printers_condition"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - - line = Line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_parent_preset_description_line); - }; - optgroup->append_line(line); -} - -// Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabPrint::reload_config(){ - reload_compatible_printers_widget(); - Tab::reload_config(); -} - -void TabPrint::update() -{ - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) - return; // ys_FIXME - - Freeze(); - - double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value; - - if (m_config->opt_bool("spiral_vase") && - !(m_config->opt_int("perimeters") == 1 && m_config->opt_int("top_solid_layers") == 0 && - fill_density == 0)) { - wxString msg_text = _(L("The Spiral Vase mode requires:\n" - "- one perimeter\n" - "- no top solid layers\n" - "- 0% fill density\n" - "- no support material\n" - "- no ensure_vertical_shell_thickness\n" - "\nShall I adjust those settings in order to enable Spiral Vase?")); - auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog->ShowModal() == wxID_YES) { - new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); - new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); - new_conf.set_key_value("support_material", new ConfigOptionBool(false)); - new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(false)); - fill_density = 0; - } - else { - new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false)); - } - load_config(new_conf); - on_value_change("fill_density", fill_density); - } - - if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && - m_config->opt_float("support_material_contact_distance") > 0. && - (m_config->opt_int("support_material_extruder") != 0 || m_config->opt_int("support_material_interface_extruder") != 0)) { - wxString msg_text = _(L("The Wipe Tower currently supports the non-soluble supports only\n" - "if they are printed with the current extruder without triggering a tool change.\n" - "(both support_material_extruder and support_material_interface_extruder need to be set to 0).\n" - "\nShall I adjust those settings in order to enable the Wipe Tower?")); - auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog->ShowModal() == wxID_YES) { - new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0)); - new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0)); - } - else - new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false)); - load_config(new_conf); - } - - if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && - m_config->opt_float("support_material_contact_distance") == 0 && - !m_config->opt_bool("support_material_synchronize_layers")) { - wxString msg_text = _(L("For the Wipe Tower to work with the soluble supports, the support layers\n" - "need to be synchronized with the object layers.\n" - "\nShall I synchronize support layers in order to enable the Wipe Tower?")); - auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog->ShowModal() == wxID_YES) { - new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true)); - } - else - new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false)); - load_config(new_conf); - } - - if (m_config->opt_bool("support_material")) { - // Ask only once. - if (!m_support_material_overhangs_queried) { - m_support_material_overhangs_queried = true; - if (!m_config->opt_bool("overhangs")/* != 1*/) { - wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n" - "- Detect bridging perimeters\n" - "\nShall I adjust those settings for supports?")); - auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Support Generator")), wxICON_WARNING | wxYES | wxNO | wxCANCEL); - DynamicPrintConfig new_conf = *m_config; - auto answer = dialog->ShowModal(); - if (answer == wxID_YES) { - // Enable "detect bridging perimeters". - new_conf.set_key_value("overhangs", new ConfigOptionBool(true)); - } else if (answer == wxID_NO) { - // Do nothing, leave supports on and "detect bridging perimeters" off. - } else if (answer == wxID_CANCEL) { - // Disable supports. - new_conf.set_key_value("support_material", new ConfigOptionBool(false)); - m_support_material_overhangs_queried = false; - } - load_config(new_conf); - } - } - } - else { - m_support_material_overhangs_queried = false; - } - - if (m_config->option<ConfigOptionPercent>("fill_density")->value == 100) { - auto fill_pattern = m_config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->value; - std::string str_fill_pattern = ""; - t_config_enum_values map_names = m_config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->get_enum_values(); - for (auto it : map_names) { - if (fill_pattern == it.second) { - str_fill_pattern = it.first; - break; - } - } - if (!str_fill_pattern.empty()){ - auto external_fill_pattern = m_config->def()->get("external_fill_pattern")->enum_values; - bool correct_100p_fill = false; - for (auto fill : external_fill_pattern) - { - if (str_fill_pattern.compare(fill) == 0) - correct_100p_fill = true; - } - // get fill_pattern name from enum_labels for using this one at dialog_msg - str_fill_pattern = m_config->def()->get("fill_pattern")->enum_labels[fill_pattern]; - if (!correct_100p_fill){ - wxString msg_text = _(L("The ")) + str_fill_pattern + _(L(" infill pattern is not supposed to work at 100% density.\n" - "\nShall I switch to rectilinear fill pattern?")); - auto dialog = new wxMessageDialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog->ShowModal() == wxID_YES) { - new_conf.set_key_value("fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear)); - fill_density = 100; - } - else - fill_density = m_presets->get_selected_preset().config.option<ConfigOptionPercent>("fill_density")->value; - new_conf.set_key_value("fill_density", new ConfigOptionPercent(fill_density)); - load_config(new_conf); - on_value_change("fill_density", fill_density); - } - } - } - - bool have_perimeters = m_config->opt_int("perimeters") > 0; - for (auto el : {"extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" }) - get_field(el)->toggle(have_perimeters); - - bool have_infill = m_config->option<ConfigOptionPercent>("fill_density")->value > 0; - // infill_extruder uses the same logic as in Print::extruders() - for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed", - "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" }) - get_field(el)->toggle(have_infill); - - bool have_solid_infill = m_config->opt_int("top_solid_layers") > 0 || m_config->opt_int("bottom_solid_layers") > 0; - // solid_infill_extruder uses the same logic as in Print::extruders() - for (auto el : {"external_fill_pattern", "infill_first", "solid_infill_extruder", - "solid_infill_extrusion_width", "solid_infill_speed" }) - get_field(el)->toggle(have_solid_infill); - - for (auto el : {"fill_angle", "bridge_angle", "infill_extrusion_width", - "infill_speed", "bridge_speed" }) - get_field(el)->toggle(have_infill || have_solid_infill); - - get_field("gap_fill_speed")->toggle(have_perimeters && have_infill); - - bool have_top_solid_infill = m_config->opt_int("top_solid_layers") > 0; - for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" }) - get_field(el)->toggle(have_top_solid_infill); - - bool have_default_acceleration = m_config->opt_float("default_acceleration") > 0; - for (auto el : {"perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration" }) - get_field(el)->toggle(have_default_acceleration); - - bool have_skirt = m_config->opt_int("skirts") > 0 || m_config->opt_float("min_skirt_length") > 0; - for (auto el : { "skirt_distance", "skirt_height" }) - get_field(el)->toggle(have_skirt); - - bool have_brim = m_config->opt_float("brim_width") > 0; - // perimeter_extruder uses the same logic as in Print::extruders() - get_field("perimeter_extruder")->toggle(have_perimeters || have_brim); - - bool have_raft = m_config->opt_int("raft_layers") > 0; - bool have_support_material = m_config->opt_bool("support_material") || have_raft; - bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto"); - bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0; - bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0; - for (auto el : {"support_material_pattern", "support_material_with_sheath", - "support_material_spacing", "support_material_angle", "support_material_interface_layers", - "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", - "support_material_xy_spacing" }) - get_field(el)->toggle(have_support_material); - get_field("support_material_threshold")->toggle(have_support_material_auto); - - for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder", - "support_material_interface_speed", "support_material_interface_contact_loops" }) - get_field(el)->toggle(have_support_material && have_support_interface); - get_field("support_material_synchronize_layers")->toggle(have_support_soluble); - - get_field("perimeter_extrusion_width")->toggle(have_perimeters || have_skirt || have_brim); - get_field("support_material_extruder")->toggle(have_support_material || have_skirt); - get_field("support_material_speed")->toggle(have_support_material || have_brim || have_skirt); - - bool have_sequential_printing = m_config->opt_bool("complete_objects"); - for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" }) - get_field(el)->toggle(have_sequential_printing); - - bool have_ooze_prevention = m_config->opt_bool("ooze_prevention"); - get_field("standby_temperature_delta")->toggle(have_ooze_prevention); - - bool have_wipe_tower = m_config->opt_bool("wipe_tower"); - for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"}) - get_field(el)->toggle(have_wipe_tower); - - m_recommended_thin_wall_thickness_description_line->SetText( - from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); - - Thaw(); -} - -void TabPrint::OnActivate() -{ - m_recommended_thin_wall_thickness_description_line->SetText( - from_u8(PresetHints::recommended_thin_wall_thickness(*m_preset_bundle))); - Tab::OnActivate(); -} - -void TabFilament::build() -{ - m_presets = &m_preset_bundle->filaments; - load_initial_data(); - - auto page = add_options_page(_(L("Filament")), "spool.png"); - auto optgroup = page->new_optgroup(_(L("Filament"))); - optgroup->append_single_option_line("filament_colour"); - optgroup->append_single_option_line("filament_diameter"); - optgroup->append_single_option_line("extrusion_multiplier"); - optgroup->append_single_option_line("filament_density"); - optgroup->append_single_option_line("filament_cost"); - - optgroup = page->new_optgroup(_(L("Temperature ")) + wxString("°C", wxConvUTF8)); - Line line = { _(L("Extruder")), "" }; - line.append_option(optgroup->get_option("first_layer_temperature")); - line.append_option(optgroup->get_option("temperature")); - optgroup->append_line(line); - - line = { _(L("Bed")), "" }; - line.append_option(optgroup->get_option("first_layer_bed_temperature")); - line.append_option(optgroup->get_option("bed_temperature")); - optgroup->append_line(line); - - page = add_options_page(_(L("Cooling")), "hourglass.png"); - optgroup = page->new_optgroup(_(L("Enable"))); - optgroup->append_single_option_line("fan_always_on"); - optgroup->append_single_option_line("cooling"); - - line = { "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_cooling_description_line); - }; - optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Fan settings"))); - line = { _(L("Fan speed")), "" }; - line.append_option(optgroup->get_option("min_fan_speed")); - line.append_option(optgroup->get_option("max_fan_speed")); - optgroup->append_line(line); - - optgroup->append_single_option_line("bridge_fan_speed"); - optgroup->append_single_option_line("disable_fan_first_layers"); - - optgroup = page->new_optgroup(_(L("Cooling thresholds")), 250); - optgroup->append_single_option_line("fan_below_layer_time"); - optgroup->append_single_option_line("slowdown_below_layer_time"); - optgroup->append_single_option_line("min_print_speed"); - - page = add_options_page(_(L("Advanced")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Filament properties"))); - optgroup->append_single_option_line("filament_type"); - optgroup->append_single_option_line("filament_soluble"); - - optgroup = page->new_optgroup(_(L("Print speed override"))); - optgroup->append_single_option_line("filament_max_volumetric_speed"); - - line = { "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_volumetric_speed_description_line); - }; - optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers"))); - optgroup->append_single_option_line("filament_loading_speed_start"); - optgroup->append_single_option_line("filament_loading_speed"); - optgroup->append_single_option_line("filament_unloading_speed_start"); - optgroup->append_single_option_line("filament_unloading_speed"); - optgroup->append_single_option_line("filament_load_time"); - optgroup->append_single_option_line("filament_unload_time"); - optgroup->append_single_option_line("filament_toolchange_delay"); - optgroup->append_single_option_line("filament_cooling_moves"); - optgroup->append_single_option_line("filament_cooling_initial_speed"); - optgroup->append_single_option_line("filament_cooling_final_speed"); - optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); - - line = { _(L("Ramming")), "" }; - line.widget = [this](wxWindow* parent){ - auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(ramming_dialog_btn); - - ramming_dialog_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e) - { - RammingDialog dlg(this,(m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0)); - if (dlg.ShowModal() == wxID_OK) - (m_config->option<ConfigOptionStrings>("filament_ramming_parameters"))->get_at(0) = dlg.get_parameters(); - })); - return sizer; - }; - optgroup->append_line(line); - - - page = add_options_page(_(L("Custom G-code")), "cog.png"); - optgroup = page->new_optgroup(_(L("Start G-code")), 0); - Option option = optgroup->get_option("start_filament_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("End G-code")), 0); - option = optgroup->get_option("end_filament_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); - optgroup->label_width = 0; - option = optgroup->get_option("filament_notes"); - option.opt.full_width = true; - option.opt.height = 250; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = { _(L("Compatible printers")), "" }; - line.widget = [this](wxWindow* parent){ - return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); - }; - optgroup->append_line(line, &m_colored_Label); - - option = optgroup->get_option("compatible_printers_condition"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - - line = Line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_parent_preset_description_line); - }; - optgroup->append_line(line); -} - -// Reload current config (aka presets->edited_preset->config) into the UI fields. -void TabFilament::reload_config(){ - reload_compatible_printers_widget(); - Tab::reload_config(); -} - -void TabFilament::update() -{ - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA) - return; // ys_FIXME - - Freeze(); - wxString text = from_u8(PresetHints::cooling_description(m_presets->get_edited_preset())); - m_cooling_description_line->SetText(text); - text = from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle)); - m_volumetric_speed_description_line->SetText(text); - - bool cooling = m_config->opt_bool("cooling", 0); - bool fan_always_on = cooling || m_config->opt_bool("fan_always_on", 0); - - for (auto el : { "max_fan_speed", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed" }) - get_field(el)->toggle(cooling); - - for (auto el : { "min_fan_speed", "disable_fan_first_layers" }) - get_field(el)->toggle(fan_always_on); - Thaw(); -} - -void TabFilament::OnActivate() -{ - m_volumetric_speed_description_line->SetText(from_u8(PresetHints::maximum_volumetric_flow_description(*m_preset_bundle))); - Tab::OnActivate(); -} - -wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticText) -{ - *StaticText = new ogStaticText(parent, ""); - - auto font = (new wxSystemSettings)->GetFont(wxSYS_DEFAULT_GUI_FONT); - (*StaticText)->SetFont(font); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(*StaticText, 1, wxEXPAND|wxALL, 0); - return sizer; -} - -bool Tab::current_preset_is_dirty() -{ - return m_presets->current_is_dirty(); -} - -void TabPrinter::build() -{ - m_presets = &m_preset_bundle->printers; - load_initial_data(); - - m_printer_technology = m_presets->get_selected_preset().printer_technology(); - - m_presets->get_selected_preset().printer_technology() == ptSLA ? build_sla() : build_fff(); - -// on_value_change("printer_technology", m_printer_technology); // to update show/hide preset ComboBoxes -} - -void TabPrinter::build_fff() -{ - if (!m_pages.empty()) - m_pages.resize(0); - // to avoid redundant memory allocation / deallocation during extruders count changing - m_pages.reserve(30); - - auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter")); - m_initial_extruders_count = m_extruders_count = nozzle_diameter->values.size(); - const Preset* parent_preset = m_presets->get_selected_preset_parent(); - m_sys_extruders_count = parent_preset == nullptr ? 0 : - static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size(); - - auto page = add_options_page(_(L("General")), "printer_empty.png"); - auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); - - Line line{ _(L("Bed shape")), "" }; - line.widget = [this](wxWindow* parent){ - auto btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetFont(Slic3r::GUI::small_font()); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) - { - auto dlg = new BedShapeDialog(this); - dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape")); - if (dlg->ShowModal() == wxID_OK){ - load_key_value("bed_shape", dlg->GetValue()); - update_changed_ui(); - } - })); - - return sizer; - }; - optgroup->append_line(line, &m_colored_Label); - optgroup->append_single_option_line("max_print_height"); - optgroup->append_single_option_line("z_offset"); - - optgroup = page->new_optgroup(_(L("Capabilities"))); - ConfigOptionDef def; - def.type = coInt, - def.default_value = new ConfigOptionInt(1); - def.label = L("Extruders"); - def.tooltip = L("Number of extruders of the printer."); - def.min = 1; - Option option(def, "extruders_count"); - optgroup->append_single_option_line(option); - optgroup->append_single_option_line("single_extruder_multi_material"); - - optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){ - size_t extruders_count = boost::any_cast<int>(optgroup->get_value("extruders_count")); - wxTheApp->CallAfter([this, opt_key, value, extruders_count](){ - if (opt_key.compare("extruders_count")==0 || opt_key.compare("single_extruder_multi_material")==0) { - extruders_count_changed(extruders_count); - update_dirty(); - if (opt_key.compare("single_extruder_multi_material")==0) // the single_extruder_multimaterial was added to force pages - on_value_change(opt_key, value); // rebuild - let's make sure the on_value_change is not skipped - } - else { - update_dirty(); - on_value_change(opt_key, value); - } - }); - }; - - -#if 0 - if (!m_no_controller) - { - optgroup = page->new_optgroup(_(L("USB/Serial connection"))); - line = {_(L("Serial port")), ""}; - Option serial_port = optgroup->get_option("serial_port"); - serial_port.side_widget = ([this](wxWindow* parent){ - auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(Slic3r::var("arrow_rotate_clockwise.png")), wxBITMAP_TYPE_PNG), - wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); - btn->SetToolTip(_(L("Rescan serial ports"))); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) {update_serial_ports(); }); - return sizer; - }); - auto serial_test = [this](wxWindow* parent){ - auto btn = m_serial_test_btn = new wxButton(parent, wxID_ANY, - _(L("Test")), wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetFont(Slic3r::GUI::small_font()); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, parent](wxCommandEvent e){ - auto sender = Slic3r::make_unique<GCodeSender>(); - auto res = sender->connect( - m_config->opt_string("serial_port"), - m_config->opt_int("serial_speed") - ); - if (res && sender->wait_connected()) { - show_info(parent, _(L("Connection to printer works correctly.")), _(L("Success!"))); - } - else { - show_error(parent, _(L("Connection failed."))); - } - }); - return sizer; - }; - - line.append_option(serial_port); - line.append_option(optgroup->get_option("serial_speed")); - line.append_widget(serial_test); - optgroup->append_line(line); - } -#endif - - optgroup = page->new_optgroup(_(L("Printer Host upload"))); - - optgroup->append_single_option_line("host_type"); - - auto printhost_browse = [this, optgroup] (wxWindow* parent) { - auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { - BonjourDialog dialog(parent); - if (dialog.show_and_lookup()) { - optgroup->set_value("print_host", std::move(dialog.get_selected()), true); - } - }); - - return sizer; - }; - - auto print_host_test = [this](wxWindow* parent) { - auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), - wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config)); - if (! host) { - const auto text = wxString::Format("%s", - _(L("Could not get a valid Printer Host reference"))); - show_error(this, text); - return; - } - wxString msg; - if (host->test(msg)) { - show_info(this, host->get_test_ok_msg(), _(L("Success!"))); - } else { - show_error(this, host->get_test_failed_msg(msg)); - } - }); - - return sizer; - }; - - Line host_line = optgroup->create_single_option_line("print_host"); - host_line.append_widget(printhost_browse); - host_line.append_widget(print_host_test); - optgroup->append_line(host_line); - optgroup->append_single_option_line("printhost_apikey"); - - if (Http::ca_file_supported()) { - - Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); - - auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e){ - static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); - wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_CANCEL) { - optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); - } - }); - - return sizer; - }; - - cafile_line.append_widget(printhost_cafile_browse); - optgroup->append_line(cafile_line); - - auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, - _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt); - return sizer; - }; - - Line cafile_hint { "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = std::move(printhost_cafile_hint); - optgroup->append_line(cafile_hint); - - } - - optgroup = page->new_optgroup(_(L("Firmware"))); - optgroup->append_single_option_line("gcode_flavor"); - optgroup->append_single_option_line("silent_mode"); - optgroup->append_single_option_line("remaining_times"); - - optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){ - wxTheApp->CallAfter([this, opt_key, value](){ - if (opt_key.compare("silent_mode") == 0) { - bool val = boost::any_cast<bool>(value); - if (m_use_silent_mode != val) { - m_rebuild_kinematics_page = true; - m_use_silent_mode = val; - } - } - build_extruder_pages(); - update_dirty(); - on_value_change(opt_key, value); - }); - }; - - optgroup = page->new_optgroup(_(L("Advanced"))); - optgroup->append_single_option_line("use_relative_e_distances"); - optgroup->append_single_option_line("use_firmware_retraction"); - optgroup->append_single_option_line("use_volumetric_e"); - optgroup->append_single_option_line("variable_layer_height"); - - page = add_options_page(_(L("Custom G-code")), "cog.png"); - optgroup = page->new_optgroup(_(L("Start G-code")), 0); - option = optgroup->get_option("start_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("End G-code")), 0); - option = optgroup->get_option("end_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("Before layer change G-code")), 0); - option = optgroup->get_option("before_layer_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("After layer change G-code")), 0); - option = optgroup->get_option("layer_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("Tool change G-code")), 0); - option = optgroup->get_option("toolchange_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - optgroup = page->new_optgroup(_(L("Between objects G-code (for sequential printing)")), 0); - option = optgroup->get_option("between_objects_gcode"); - option.opt.full_width = true; - option.opt.height = 150; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); - option = optgroup->get_option("printer_notes"); - option.opt.full_width = true; - option.opt.height = 250; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = Line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_parent_preset_description_line); - }; - optgroup->append_line(line); - - build_extruder_pages(); - -#if 0 - if (!m_no_controller) - update_serial_ports(); -#endif -} - -void TabPrinter::build_sla() -{ - if (!m_pages.empty()) - m_pages.resize(0); - auto page = add_options_page(_(L("General")), "printer_empty.png"); - auto optgroup = page->new_optgroup(_(L("Size and coordinates"))); - - Line line{ _(L("Bed shape")), "" }; - line.widget = [this](wxWindow* parent){ - auto btn = new wxButton(parent, wxID_ANY, _(L(" Set ")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - // btn->SetFont(Slic3r::GUI::small_font); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) - { - auto dlg = new BedShapeDialog(this); - dlg->build_dialog(m_config->option<ConfigOptionPoints>("bed_shape")); - if (dlg->ShowModal() == wxID_OK){ - load_key_value("bed_shape", dlg->GetValue()); - update_changed_ui(); - } - })); - - return sizer; - }; - optgroup->append_line(line, &m_colored_Label); - optgroup->append_single_option_line("max_print_height"); - - optgroup = page->new_optgroup(_(L("Display"))); - optgroup->append_single_option_line("display_width"); - optgroup->append_single_option_line("display_height"); - - auto option = optgroup->get_option("display_pixels_x"); - line = { _(option.opt.full_label), "" }; - line.append_option(option); - line.append_option(optgroup->get_option("display_pixels_y")); - optgroup->append_line(line); - - optgroup = page->new_optgroup(_(L("Corrections"))); - line = Line{ m_config->def()->get("printer_correction")->full_label, "" }; - std::vector<std::string> axes{ "X", "Y", "Z" }; - int id = 0; - for (auto& axis : axes) { - auto opt = optgroup->get_option("printer_correction", id); - opt.opt.label = axis; - line.append_option(opt); - ++id; - } - optgroup->append_line(line); - - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); - option = optgroup->get_option("printer_notes"); - option.opt.full_width = true; - option.opt.height = 250; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); - line = Line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_parent_preset_description_line); - }; - optgroup->append_line(line); -} - -void TabPrinter::update_serial_ports(){ - Field *field = get_field("serial_port"); - Choice *choice = static_cast<Choice *>(field); - choice->set_values(Utils::scan_serial_ports()); -} - -void TabPrinter::extruders_count_changed(size_t extruders_count){ - m_extruders_count = extruders_count; - m_preset_bundle->printers.get_edited_preset().set_num_extruders(extruders_count); - m_preset_bundle->update_multi_material_filament_presets(); - build_extruder_pages(); - reload_config(); - on_value_change("extruders_count", extruders_count); - update_objects_list_extruder_column(extruders_count); -} - -void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key) -{ - auto option = optgroup->get_option(opt_key, 0); - auto line = Line{ option.opt.full_label, "" }; - line.append_option(option); - if (m_use_silent_mode) - line.append_option(optgroup->get_option(opt_key, 1)); - optgroup->append_line(line); -} - -PageShp TabPrinter::build_kinematics_page() -{ - auto page = add_options_page(_(L("Machine limits")), "cog.png", true); - - if (m_use_silent_mode) { - // Legend for OptionsGroups - auto optgroup = page->new_optgroup(_(L(""))); - optgroup->set_show_modified_btns_val(false); - optgroup->label_width = 230; - auto line = Line{ "", "" }; - - ConfigOptionDef def; - def.type = coString; - def.width = 150; - def.gui_type = "legend"; - def.tooltip = L("Values in this column are for Full Power mode"); - def.default_value = new ConfigOptionString{ L("Full Power") }; - - auto option = Option(def, "full_power_legend"); - line.append_option(option); - - def.tooltip = L("Values in this column are for Silent mode"); - def.default_value = new ConfigOptionString{ L("Silent") }; - option = Option(def, "silent_legend"); - line.append_option(option); - - optgroup->append_line(line); - } - - std::vector<std::string> axes{ "x", "y", "z", "e" }; - auto optgroup = page->new_optgroup(_(L("Maximum feedrates"))); - for (const std::string &axis : axes) { - append_option_line(optgroup, "machine_max_feedrate_" + axis); - } - - optgroup = page->new_optgroup(_(L("Maximum accelerations"))); - for (const std::string &axis : axes) { - append_option_line(optgroup, "machine_max_acceleration_" + axis); - } - append_option_line(optgroup, "machine_max_acceleration_extruding"); - append_option_line(optgroup, "machine_max_acceleration_retracting"); - - optgroup = page->new_optgroup(_(L("Jerk limits"))); - for (const std::string &axis : axes) { - append_option_line(optgroup, "machine_max_jerk_" + axis); - } - - optgroup = page->new_optgroup(_(L("Minimum feedrates"))); - append_option_line(optgroup, "machine_min_extruding_rate"); - append_option_line(optgroup, "machine_min_travel_rate"); - - return page; -} - - -void TabPrinter::build_extruder_pages() -{ - size_t n_before_extruders = 2; // Count of pages before Extruder pages - bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin; - - // Add/delete Kinematics page according to is_marlin_flavor - size_t existed_page = 0; - for (int i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already - if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) { - if (!is_marlin_flavor || m_rebuild_kinematics_page) - m_pages.erase(m_pages.begin() + i); - else - existed_page = i; - break; - } - - if (existed_page < n_before_extruders && is_marlin_flavor){ - auto page = build_kinematics_page(); - m_pages.insert(m_pages.begin() + n_before_extruders, page); - } - - if (is_marlin_flavor) - n_before_extruders++; - size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page - - if (m_extruders_count_old == m_extruders_count || - (m_has_single_extruder_MM_page && m_extruders_count == 1)) - { - // if we have a single extruder MM setup, add a page with configuration options: - for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already - if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) { - m_pages.erase(m_pages.begin() + i); - break; - } - m_has_single_extruder_MM_page = false; - } - if (m_extruders_count > 1 && m_config->opt_bool("single_extruder_multi_material") && !m_has_single_extruder_MM_page) { - // create a page, but pretend it's an extruder page, so we can add it to m_pages ourselves - auto page = add_options_page(_(L("Single extruder MM setup")), "printer_empty.png", true); - auto optgroup = page->new_optgroup(_(L("Single extruder multimaterial parameters"))); - optgroup->append_single_option_line("cooling_tube_retraction"); - optgroup->append_single_option_line("cooling_tube_length"); - optgroup->append_single_option_line("parking_pos_retraction"); - optgroup->append_single_option_line("extra_loading_move"); - m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page); - m_has_single_extruder_MM_page = true; - } - - - for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx){ - //# build page - char buf[MIN_BUF_LENGTH_FOR_L]; - sprintf(buf, _CHB(L("Extruder %d")), extruder_idx + 1); - auto page = add_options_page(from_u8(buf), "funnel.png", true); - m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page); - - auto optgroup = page->new_optgroup(_(L("Size"))); - optgroup->append_single_option_line("nozzle_diameter", extruder_idx); - - optgroup = page->new_optgroup(_(L("Layer height limits"))); - optgroup->append_single_option_line("min_layer_height", extruder_idx); - optgroup->append_single_option_line("max_layer_height", extruder_idx); - - - optgroup = page->new_optgroup(_(L("Position (for multi-extruder printers)"))); - optgroup->append_single_option_line("extruder_offset", extruder_idx); - - optgroup = page->new_optgroup(_(L("Retraction"))); - optgroup->append_single_option_line("retract_length", extruder_idx); - optgroup->append_single_option_line("retract_lift", extruder_idx); - Line line = { _(L("Only lift Z")), "" }; - line.append_option(optgroup->get_option("retract_lift_above", extruder_idx)); - line.append_option(optgroup->get_option("retract_lift_below", extruder_idx)); - optgroup->append_line(line); - - optgroup->append_single_option_line("retract_speed", extruder_idx); - optgroup->append_single_option_line("deretract_speed", extruder_idx); - optgroup->append_single_option_line("retract_restart_extra", extruder_idx); - optgroup->append_single_option_line("retract_before_travel", extruder_idx); - optgroup->append_single_option_line("retract_layer_change", extruder_idx); - optgroup->append_single_option_line("wipe", extruder_idx); - optgroup->append_single_option_line("retract_before_wipe", extruder_idx); - - optgroup = page->new_optgroup(_(L("Retraction when tool is disabled (advanced settings for multi-extruder setups)"))); - optgroup->append_single_option_line("retract_length_toolchange", extruder_idx); - optgroup->append_single_option_line("retract_restart_extra_toolchange", extruder_idx); - - optgroup = page->new_optgroup(_(L("Preview"))); - optgroup->append_single_option_line("extruder_colour", extruder_idx); - } - - // # remove extra pages - if (m_extruders_count < m_extruders_count_old) - m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count, - m_pages.begin() + n_before_extruders + m_extruders_count_old); - - m_extruders_count_old = m_extruders_count; - rebuild_page_tree(); -} - -// this gets executed after preset is loaded and before GUI fields are updated -void TabPrinter::on_preset_loaded() -{ - // update the extruders count field - auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter")); - int extruders_count = nozzle_diameter->values.size(); - set_value("extruders_count", extruders_count); - // update the GUI field according to the number of nozzle diameters supplied - extruders_count_changed(extruders_count); -} - -void TabPrinter::update_pages() -{ - // update m_pages ONLY if printer technology is changed - if (m_presets->get_edited_preset().printer_technology() == m_printer_technology) - return; - - // hide all old pages - for (auto& el : m_pages) - el.get()->Hide(); - - // set m_pages to m_pages_(technology before changing) - m_printer_technology == ptFFF ? m_pages.swap(m_pages_fff) : m_pages.swap(m_pages_sla); - - // build Tab according to the technology, if it's not exist jet OR - // set m_pages_(technology after changing) to m_pages - if (m_presets->get_edited_preset().printer_technology() == ptFFF) - m_pages_fff.empty() ? build_fff() : m_pages.swap(m_pages_fff); - else - m_pages_sla.empty() ? build_sla() : m_pages.swap(m_pages_sla); - - rebuild_page_tree(true); - - on_value_change("printer_technology", m_presets->get_edited_preset().printer_technology()); // to update show/hide preset ComboBoxes -} - -void TabPrinter::update() -{ - m_presets->get_edited_preset().printer_technology() == ptFFF ? update_fff() : update_sla(); -} - -void TabPrinter::update_fff() -{ - Freeze(); - - bool en; - auto serial_speed = get_field("serial_speed"); - if (serial_speed != nullptr) { - en = !m_config->opt_string("serial_port").empty(); - get_field("serial_speed")->toggle(en); - if (m_config->opt_int("serial_speed") != 0 && en) - m_serial_test_btn->Enable(); - else - m_serial_test_btn->Disable(); - } - - { - std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config)); - m_print_host_test_btn->Enable(!m_config->opt_string("print_host").empty() && host->can_test()); - m_printhost_browse_btn->Enable(host->has_auto_discovery()); - } - - bool have_multiple_extruders = m_extruders_count > 1; - get_field("toolchange_gcode")->toggle(have_multiple_extruders); - get_field("single_extruder_multi_material")->toggle(have_multiple_extruders); - - bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin; - - { - Field *sm = get_field("silent_mode"); - if (! is_marlin_flavor) - // Disable silent mode for non-marlin firmwares. - get_field("silent_mode")->toggle(false); - if (is_marlin_flavor) - sm->enable(); - else - sm->disable(); - } - - if (m_use_silent_mode != m_config->opt_bool("silent_mode")) { - m_rebuild_kinematics_page = true; - m_use_silent_mode = m_config->opt_bool("silent_mode"); - } - - for (size_t i = 0; i < m_extruders_count; ++i) { - bool have_retract_length = m_config->opt_float("retract_length", i) > 0; - - // when using firmware retraction, firmware decides retraction length - bool use_firmware_retraction = m_config->opt_bool("use_firmware_retraction"); - get_field("retract_length", i)->toggle(!use_firmware_retraction); - - // user can customize travel length if we have retraction length or we"re using - // firmware retraction - get_field("retract_before_travel", i)->toggle(have_retract_length || use_firmware_retraction); - - // user can customize other retraction options if retraction is enabled - bool retraction = (have_retract_length || use_firmware_retraction); - std::vector<std::string> vec = { "retract_lift", "retract_layer_change" }; - for (auto el : vec) - get_field(el, i)->toggle(retraction); - - // retract lift above / below only applies if using retract lift - vec.resize(0); - vec = { "retract_lift_above", "retract_lift_below" }; - for (auto el : vec) - get_field(el, i)->toggle(retraction && m_config->opt_float("retract_lift", i) > 0); - - // some options only apply when not using firmware retraction - vec.resize(0); - vec = { "retract_speed", "deretract_speed", "retract_before_wipe", "retract_restart_extra", "wipe" }; - for (auto el : vec) - get_field(el, i)->toggle(retraction && !use_firmware_retraction); - - bool wipe = m_config->opt_bool("wipe", i); - get_field("retract_before_wipe", i)->toggle(wipe); - - if (use_firmware_retraction && wipe) { - auto dialog = new wxMessageDialog(parent(), - _(L("The Wipe option is not available when using the Firmware Retraction mode.\n" - "\nShall I disable it in order to enable Firmware Retraction?")), - _(L("Firmware Retraction")), wxICON_WARNING | wxYES | wxNO); - - DynamicPrintConfig new_conf = *m_config; - if (dialog->ShowModal() == wxID_YES) { - auto wipe = static_cast<ConfigOptionBools*>(m_config->option("wipe")->clone()); - for (int w = 0; w < wipe->values.size(); w++) - wipe->values[w] = false; - new_conf.set_key_value("wipe", wipe); - } - else { - new_conf.set_key_value("use_firmware_retraction", new ConfigOptionBool(false)); - } - load_config(new_conf); - } - - get_field("retract_length_toolchange", i)->toggle(have_multiple_extruders); - - bool toolchange_retraction = m_config->opt_float("retract_length_toolchange", i) > 0; - get_field("retract_restart_extra_toolchange", i)->toggle - (have_multiple_extruders && toolchange_retraction); - } - - Thaw(); -} - -void TabPrinter::update_sla(){ ; } - -// Initialize the UI from the current preset -void Tab::load_current_preset() -{ - auto preset = m_presets->get_edited_preset(); - - (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); - - update(); - // For the printer profile, generate the extruder pages. - if (preset.printer_technology() == ptFFF) - on_preset_loaded(); - // Reload preset pages with the new configuration values. - reload_config(); - - m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet; - m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns; - m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns; - - m_undo_to_sys_btn->Enable(!preset.is_default); - - // use CallAfter because some field triggers schedule on_change calls using CallAfter, - // and we don't want them to be called after this update_dirty() as they would mark the - // preset dirty again - // (not sure this is true anymore now that update_dirty is idempotent) - wxTheApp->CallAfter([this]{ - // checking out if this Tab exists till this moment - if (!checked_tab(this)) - return; - update_tab_ui(); - - // update show/hide tabs - if (m_name == "printer"){ - PrinterTechnology& printer_technology = m_presets->get_edited_preset().printer_technology(); - if (printer_technology != static_cast<TabPrinter*>(this)->m_printer_technology) - { - for (auto& tab : get_preset_tabs()){ - if (tab.technology != printer_technology) - { - int page_id = get_tab_panel()->FindPage(tab.panel); - get_tab_panel()->GetPage(page_id)->Show(false); - get_tab_panel()->RemovePage(page_id); - } - else - get_tab_panel()->InsertPage(get_tab_panel()->FindPage(this), tab.panel, tab.panel->title()); - } - - static_cast<TabPrinter*>(this)->m_printer_technology = printer_technology; - } - } - - on_presets_changed(); - - if (name() == "print") - update_frequently_changed_parameters(); - if (m_name == "printer"){ - static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count; - const Preset* parent_preset = m_presets->get_selected_preset_parent(); - static_cast<TabPrinter*>(this)->m_sys_extruders_count = parent_preset == nullptr ? 0 : - static_cast<const ConfigOptionFloats*>(parent_preset->config.option("nozzle_diameter"))->values.size(); - } - m_opt_status_value = (m_presets->get_selected_preset_parent() ? osSystemValue : 0) | osInitValue; - init_options_list(); - update_changed_ui(); - }); -} - -//Regerenerate content of the page tree. -void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) -{ - Freeze(); - // get label of the currently selected item - auto selected = m_treectrl->GetItemText(m_treectrl->GetSelection()); - auto rootItem = m_treectrl->GetRootItem(); - - auto have_selection = 0; - m_treectrl->DeleteChildren(rootItem); - for (auto p : m_pages) - { - auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); - m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); - if (p->title() == selected) { - if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange - m_disable_tree_sel_changed_event = !tree_sel_change_event; - m_treectrl->SelectItem(itemId); - m_disable_tree_sel_changed_event = false; - have_selection = 1; - } - } - - if (!have_selection) { - // this is triggered on first load, so we don't disable the sel change event - m_treectrl->SelectItem(m_treectrl->GetFirstVisibleItem());//! (treectrl->GetFirstChild(rootItem)); - } - Thaw(); -} - -// Called by the UI combo box when the user switches profiles. -// Select a preset by a name.If !defined(name), then the default preset is selected. -// If the current profile is modified, user is asked to save the changes. -void Tab::select_preset(std::string preset_name /*= ""*/) -{ - // If no name is provided, select the "-- default --" preset. - if (preset_name.empty()) - preset_name = m_presets->default_preset().name; - auto current_dirty = m_presets->current_is_dirty(); - auto printer_tab = m_presets->name() == "printer"; - auto canceled = false; - m_reload_dependent_tabs = {}; - if (current_dirty && !may_discard_current_dirty_preset()) { - canceled = true; - } else if (printer_tab) { - // Before switching the printer to a new one, verify, whether the currently active print and filament - // are compatible with the new printer. - // If they are not compatible and the current print or filament are dirty, let user decide - // whether to discard the changes or keep the current printer selection. - // - // 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); - PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology(); - PrinterTechnology new_printer_technology = new_printer_preset.printer_technology(); - struct PresetUpdate { - std::string name; - PresetCollection *presets; - PrinterTechnology technology; - bool old_preset_dirty; - bool new_preset_compatible; - }; - std::vector<PresetUpdate> updates = { - { "print", &m_preset_bundle->prints, ptFFF }, - { "filament", &m_preset_bundle->filaments, ptFFF }, - { "sla_material", &m_preset_bundle->sla_materials, ptSLA } - }; - 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); - if (! canceled) - canceled = pu.old_preset_dirty && ! pu.new_preset_compatible && ! may_discard_current_dirty_preset(pu.presets, preset_name); - } - if (! canceled) { - for (PresetUpdate &pu : updates) { - // The preset will be switched to a different, compatible preset, or the '-- default --'. - if (pu.technology == new_printer_technology) - m_reload_dependent_tabs.emplace_back(pu.name); - if (pu.old_preset_dirty) - pu.presets->discard_current_changes(); - } - } - } - 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. - on_presets_changed(); - } else { - if (current_dirty) - m_presets->discard_current_changes() ; - m_presets->select_preset_by_name(preset_name, false); - // Mark the print & filament enabled if they are compatible with the currently selected preset. - // The following method should not discard changes of current print or filament presets on change of a printer profile, - // if they are compatible with the current printer. - if (current_dirty || printer_tab) - m_preset_bundle->update_compatible_with_printer(true); - // Initialize the UI from the current preset. - if (printer_tab) - static_cast<TabPrinter*>(this)->update_pages(); - load_current_preset(); - } -} - -// If the current preset is dirty, the user is asked whether the changes may be discarded. -// if the current preset was not dirty, or the user agreed to discard the changes, 1 is returned. -bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr*/, const std::string& new_printer_name /*= ""*/) -{ - if (presets == nullptr) presets = m_presets; - // Display a dialog showing the dirty options in a human readable form. - auto old_preset = presets->get_edited_preset(); - auto type_name = presets->name(); - auto tab = " "; - auto name = old_preset.is_default ? - _(L("Default ")) + type_name + _(L(" preset")) : - (type_name + _(L(" preset\n")) + tab + old_preset.name); - // Collect descriptions of the dirty options. - std::vector<std::string> option_names; - for(auto opt_key: presets->current_dirty_options()) { - auto opt = m_config->def()->options.at(opt_key); - std::string name = ""; - if (!opt.category.empty()) - name += opt.category + " > "; - name += !opt.full_label.empty() ? - opt.full_label : - opt.label; - option_names.push_back(name); - } - // Show a confirmation dialog with the list of dirty options. - std::string changes = ""; - for (auto changed_name : option_names) - changes += tab + changed_name + "\n"; - auto message = (!new_printer_name.empty()) ? - name + _(L("\n\nis not compatible with printer\n")) +tab + new_printer_name+ _(L("\n\nand it has the following unsaved changes:")) : - name + _(L("\n\nhas the following unsaved changes:")); - auto confirm = new wxMessageDialog(parent(), - message + "\n" +changes +_(L("\n\nDiscard changes and continue anyway?")), - _(L("Unsaved Changes")), wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION); - return confirm->ShowModal() == wxID_YES; -} - -void Tab::OnTreeSelChange(wxTreeEvent& event) -{ - if (m_disable_tree_sel_changed_event) return; - -// There is a bug related to Ubuntu overlay scrollbars, see https://github.com/prusa3d/Slic3r/issues/898 and https://github.com/prusa3d/Slic3r/issues/952. -// The issue apparently manifests when Show()ing a window with overlay scrollbars while the UI is frozen. For this reason, -// we will Thaw the UI prematurely on Linux. This means destroing the no_updates object prematurely. -#ifdef __linux__ - std::unique_ptr<wxWindowUpdateLocker> no_updates(new wxWindowUpdateLocker(this)); -#else - wxWindowUpdateLocker noUpdates(this); -#endif - - Page* page = nullptr; - auto selection = m_treectrl->GetItemText(m_treectrl->GetSelection()); - for (auto p : m_pages) - if (p->title() == selection) - { - page = p.get(); - m_is_nonsys_values = page->m_is_nonsys_values; - m_is_modified_values = page->m_is_modified_values; - break; - } - if (page == nullptr) return; - - for (auto& el : m_pages) - el.get()->Hide(); - -#ifdef __linux__ - no_updates.reset(nullptr); -#endif - - page->Show(); - m_hsizer->Layout(); - Refresh(); - - update_undo_buttons(); -} - -void Tab::OnKeyDown(wxKeyEvent& event) -{ - if (event.GetKeyCode() == WXK_TAB) - m_treectrl->Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward); - else - event.Skip(); -} - -// Save the current preset into file. -// This removes the "dirty" flag of the preset, possibly creates a new preset under a new name, -// and activates the new preset. -// Wizard calls save_preset with a name "My Settings", otherwise no name is provided and this method -// opens a Slic3r::GUI::SavePresetWindow dialog. -void Tab::save_preset(std::string name /*= ""*/) -{ - // since buttons(and choices too) don't get focus on Mac, we set focus manually - // to the treectrl so that the EVT_* events are fired for the input field having - // focus currently.is there anything better than this ? -//! m_treectrl->OnSetFocus(); - - if (name.empty()) { - auto preset = m_presets->get_selected_preset(); - auto default_name = preset.is_default ? "Untitled" : preset.name; - bool have_extention = boost::iends_with(default_name, ".ini"); - if (have_extention) - { - size_t len = default_name.length()-4; - default_name.resize(len); - } - //[map $_->name, grep !$_->default && !$_->external, @{$self->{presets}}], - std::vector<std::string> values; - for (size_t i = 0; i < m_presets->size(); ++i) { - const Preset &preset = m_presets->preset(i); - if (preset.is_default || preset.is_system || preset.is_external) - continue; - values.push_back(preset.name); - } - - auto dlg = new SavePresetWindow(parent()); - dlg->build(title(), default_name, values); - if (dlg->ShowModal() != wxID_OK) - return; - name = dlg->get_name(); - if (name == ""){ - show_error(this, _(L("The supplied name is empty. It can't be saved."))); - return; - } - const Preset *existing = m_presets->find_preset(name, false); - if (existing && (existing->is_default || existing->is_system)) { - show_error(this, _(L("Cannot overwrite a system profile."))); - return; - } - if (existing && (existing->is_external)) { - show_error(this, _(L("Cannot overwrite an external profile."))); - return; - } - } - - // Save the preset into Slic3r::data_dir / presets / section_name / preset_name.ini - m_presets->save_current_preset(name); - // Mark the print & filament enabled if they are compatible with the currently selected preset. - m_preset_bundle->update_compatible_with_printer(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. - on_presets_changed(); - // If current profile is saved, "delete preset" button have to be enabled - m_btn_delete_preset->Enable(true); - - if (m_name == "printer") - static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count; - update_changed_ui(); -} - -// Called for a currently selected preset. -void Tab::delete_preset() -{ - auto current_preset = m_presets->get_selected_preset(); - // Don't let the user delete the ' - default - ' configuration. - wxString action = current_preset.is_external ? _(L("remove")) : _(L("delete")); - wxString msg = _(L("Are you sure you want to ")) + action + _(L(" the selected preset?")); - action = current_preset.is_external ? _(L("Remove")) : _(L("Delete")); - wxString title = action + _(L(" Preset")); - if (current_preset.is_default || - wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) - return; - // Delete the file and select some other reasonable preset. - // The 'external' presets will only be removed from the preset list, their files will not be deleted. - try{ m_presets->delete_current_preset(); } - catch (const std::exception &e) - { - return; - } - // Load the newly selected preset into the UI, update selection combo boxes with their dirty flags. - load_current_preset(); -} - -void Tab::toggle_show_hide_incompatible() -{ - m_show_incompatible_presets = !m_show_incompatible_presets; - update_show_hide_incompatible_button(); - update_tab_ui(); -} - -void Tab::update_show_hide_incompatible_button() -{ - m_btn_hide_incompatible_presets->SetBitmap(m_show_incompatible_presets ? - m_bmp_show_incompatible_presets : m_bmp_hide_incompatible_presets); - m_btn_hide_incompatible_presets->SetToolTip(m_show_incompatible_presets ? - "Both compatible an incompatible presets are shown. Click to hide presets not compatible with the current printer." : - "Only compatible presets are shown. Click to show both the presets compatible and not compatible with the current printer."); -} - -void Tab::update_ui_from_settings() -{ - // Show the 'show / hide presets' button only for the print and filament tabs, and only if enabled - // in application preferences. - m_show_btn_incompatible_presets = get_app_config()->get("show_incompatible_presets")[0] == '1' ? true : false; - bool show = m_show_btn_incompatible_presets && m_presets->name().compare("printer") != 0; - show ? m_btn_hide_incompatible_presets->Show() : m_btn_hide_incompatible_presets->Hide(); - // If the 'show / hide presets' button is hidden, hide the incompatible presets. - if (show) { - update_show_hide_incompatible_button(); - } - else { - if (m_show_incompatible_presets) { - m_show_incompatible_presets = false; - update_tab_ui(); - } - } -} - -// Return a callback to create a Tab widget to mark the preferences as compatible / incompatible to the current printer. -wxSizer* Tab::compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn) -{ - *checkbox = new wxCheckBox(parent, wxID_ANY, _(L("All"))); - *btn = new wxButton(parent, wxID_ANY, _(L(" Set "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - - (*btn)->SetBitmap(wxBitmap(from_u8(Slic3r::var("printer_empty.png")), wxBITMAP_TYPE_PNG)); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add((*checkbox), 0, wxALIGN_CENTER_VERTICAL); - sizer->Add((*btn), 0, wxALIGN_CENTER_VERTICAL); - - (*checkbox)->Bind(wxEVT_CHECKBOX, ([=](wxCommandEvent e) - { - (*btn)->Enable(!(*checkbox)->GetValue()); - // All printers have been made compatible with this preset. - if ((*checkbox)->GetValue()) - load_key_value("compatible_printers", std::vector<std::string> {}); - get_field("compatible_printers_condition")->toggle((*checkbox)->GetValue()); - update_changed_ui(); - }) ); - - (*btn)->Bind(wxEVT_BUTTON, ([this, parent, checkbox, btn](wxCommandEvent e) - { - // # Collect names of non-default non-external printer profiles. - PresetCollection *printers = &m_preset_bundle->printers; - wxArrayString presets; - for (size_t idx = 0; idx < printers->size(); ++idx) - { - Preset& preset = printers->preset(idx); - if (!preset.is_default && !preset.is_external && !preset.is_system) - presets.Add(preset.name); - } - - wxMultiChoiceDialog dlg(parent, - _(L("Select the printers this profile is compatible with.")), - _(L("Compatible printers")), presets); - // # Collect and set indices of printers marked as compatible. - wxArrayInt selections; - auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(m_config->option("compatible_printers")); - if (compatible_printers != nullptr || !compatible_printers->values.empty()) - for (auto preset_name : compatible_printers->values) - for (size_t idx = 0; idx < presets.GetCount(); ++idx) - if (presets[idx].compare(preset_name) == 0) - { - selections.Add(idx); - break; - } - dlg.SetSelections(selections); - std::vector<std::string> value; - // Show the dialog. - if (dlg.ShowModal() == wxID_OK) { - selections.Clear(); - selections = dlg.GetSelections(); - for (auto idx : selections) - value.push_back(presets[idx].ToStdString()); - if (value.empty()) { - (*checkbox)->SetValue(1); - (*btn)->Disable(); - } - // All printers have been made compatible with this preset. - load_key_value("compatible_printers", value); - update_changed_ui(); - } - })); - return sizer; -} - -void Tab::update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible) -{ - if (ui == nullptr) - return; - ui->Freeze(); - ui->DeleteAllItems(); - auto presets = m_presets->get_presets(); - auto idx_selected = m_presets->get_idx_selected(); - auto suffix_modified = m_presets->get_suffix_modified(); - int icon_compatible = 0; - int icon_incompatible = 1; - int cnt_items = 0; - - auto root_sys = ui->AppendContainer(wxDataViewItem(0), _(L("System presets"))); - auto root_def = ui->AppendContainer(wxDataViewItem(0), _(L("Default presets"))); - - auto show_def = get_app_config()->get("no_defaults")[0] != '1'; - - for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) { - const Preset &preset = presets[i]; - if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) - continue; - - auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str()); - - wxDataViewItem item; - if (preset.is_system) - item = ui->AppendItem(root_sys, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else if (show_def && preset.is_default) - item = ui->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent = m_presets->get_preset_parent(preset); - if (parent == nullptr) - item = ui->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent_name = parent->name; - - wxDataViewTreeStoreContainerNode *node = ui->GetStore()->FindContainerNode(root_sys); - if (node) - { - wxDataViewTreeStoreNodeList::iterator iter; - for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++) - { - wxDataViewTreeStoreNode* child = *iter; - auto child_item = child->GetItem(); - auto item_text = ui->GetItemText(child_item); - if (item_text == parent_name) - { - auto added_child = ui->AppendItem(child->GetItem(), preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - if (!added_child){ - ui->DeleteItem(child->GetItem()); - auto new_parent = ui->AppendContainer(root_sys, parent_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - ui->AppendItem(new_parent, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - } - break; - } - } - } - } - } - - cnt_items++; - if (i == idx_selected){ - ui->Select(item); - m_cc_presets_choice->SetText(preset_name); - } - } - if (ui->GetStore()->GetChildCount(root_def) == 0) - ui->DeleteItem(root_def); - - ui->Thaw(); -} - -void Tab::update_tab_presets(wxComboCtrl* ui, bool show_incompatible) -{ - if (ui == nullptr) - return; - ui->Freeze(); - ui->Clear(); - auto presets = m_presets->get_presets(); - auto idx_selected = m_presets->get_idx_selected(); - auto suffix_modified = m_presets->get_suffix_modified(); - int icon_compatible = 0; - int icon_incompatible = 1; - int cnt_items = 0; - - wxDataViewTreeCtrlComboPopup* popup = wxDynamicCast(m_cc_presets_choice->GetPopupControl(), wxDataViewTreeCtrlComboPopup); - if (popup != nullptr) - { - popup->DeleteAllItems(); - - auto root_sys = popup->AppendContainer(wxDataViewItem(0), _(L("System presets"))); - auto root_def = popup->AppendContainer(wxDataViewItem(0), _(L("Default presets"))); - - auto show_def = get_app_config()->get("no_defaults")[0] != '1'; - - for (size_t i = presets.front().is_visible ? 0 : 1; i < presets.size(); ++i) { - const Preset &preset = presets[i]; - if (!preset.is_visible || (!show_incompatible && !preset.is_compatible && i != idx_selected)) - continue; - - auto preset_name = wxString::FromUTF8((preset.name + (preset.is_dirty ? suffix_modified : "")).c_str()); - - wxDataViewItem item; - if (preset.is_system) - item = popup->AppendItem(root_sys, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else if (show_def && preset.is_default) - item = popup->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent = m_presets->get_preset_parent(preset); - if (parent == nullptr) - item = popup->AppendItem(root_def, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - else - { - auto parent_name = parent->name; - - wxDataViewTreeStoreContainerNode *node = popup->GetStore()->FindContainerNode(root_sys); - if (node) - { - wxDataViewTreeStoreNodeList::iterator iter; - for (iter = node->GetChildren().begin(); iter != node->GetChildren().end(); iter++) - { - wxDataViewTreeStoreNode* child = *iter; - auto child_item = child->GetItem(); - auto item_text = popup->GetItemText(child_item); - if (item_text == parent_name) - { - auto added_child = popup->AppendItem(child->GetItem(), preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - if (!added_child){ - popup->DeleteItem(child->GetItem()); - auto new_parent = popup->AppendContainer(root_sys, parent_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - popup->AppendItem(new_parent, preset_name, - preset.is_compatible ? icon_compatible : icon_incompatible); - } - break; - } - } - } - } - } - - cnt_items++; - if (i == idx_selected){ - popup->Select(item); - m_cc_presets_choice->SetText(preset_name); - } - } - if (popup->GetStore()->GetChildCount(root_def) == 0) - popup->DeleteItem(root_def); - } - ui->Thaw(); -} - -void Tab::fill_icon_descriptions() -{ - m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_lock, L("LOCKED LOCK;" - "indicates that the settings are the same as the system values for the current option group"))); - - m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_unlock, L("UNLOCKED LOCK;" - "indicates that some settings were changed and are not equal to the system values for " - "the current option group.\n" - "Click the UNLOCKED LOCK icon to reset all settings for current option group to " - "the system values."))); - - m_icon_descriptions.push_back(t_icon_description(&m_bmp_white_bullet, L("WHITE BULLET;" - "for the left button: \tindicates a non-system preset,\n" - "for the right button: \tindicates that the settings hasn't been modified."))); - - m_icon_descriptions.push_back(t_icon_description(&m_bmp_value_revert, L("BACK ARROW;" - "indicates that the settings were changed and are not equal to the last saved preset for " - "the current option group.\n" - "Click the BACK ARROW icon to reset all settings for the current option group to " - "the last saved preset."))); -} - -void Tab::set_tooltips_text() -{ -// m_undo_to_sys_btn->SetToolTip(_(L( "LOCKED LOCK icon indicates that the settings are the same as the system values " -// "for the current option group.\n" -// "UNLOCKED LOCK icon indicates that some settings were changed and are not equal " -// "to the system values for the current option group.\n" -// "WHITE BULLET icon indicates a non system preset.\n\n" -// "Click the UNLOCKED LOCK icon to reset all settings for current option group to " -// "the system values."))); -// -// m_undo_btn->SetToolTip(_(L( "WHITE BULLET icon indicates that the settings are the same as in the last saved" -// "preset for the current option group.\n" -// "BACK ARROW icon indicates that the settings were changed and are not equal to " -// "the last saved preset for the current option group.\n\n" -// "Click the BACK ARROW icon to reset all settings for the current option group to " -// "the last saved preset."))); - - // --- Tooltip text for reset buttons (for whole options group) - // Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field. - m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system values " - "for the current option group")); - m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal " - "to the system values for the current option group.\n" - "Click to reset all settings for current option group to the system values.")); - m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system preset.")); - m_ttg_non_system = &m_ttg_white_bullet_ns; - // Text to be shown on the "Undo user changes" button next to each input field. - m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved " - "preset for the current option group.")); - m_ttg_value_revert = _(L("BACK ARROW icon indicates that the settings were changed and are not equal to " - "the last saved preset for the current option group.\n" - "Click to reset all settings for the current option group to the last saved preset.")); - - // --- Tooltip text for reset buttons (for each option in group) - // Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field. - m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system value.")); - m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal " - "to the system value.\n" - "Click to reset current value to the system value.")); - // m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset.")); - m_tt_non_system = &m_ttg_white_bullet_ns; - // Text to be shown on the "Undo user changes" button next to each input field. - m_tt_white_bullet = _(L("WHITE BULLET icon indicates that the value is the same as in the last saved preset.")); - m_tt_value_revert = _(L("BACK ARROW icon indicates that the value was changed and is not equal to the last saved preset.\n" - "Click to reset current value to the last saved preset.")); -} - -void Page::reload_config() -{ - for (auto group : m_optgroups) - group->reload_config(); -} - -Field* Page::get_field(const t_config_option_key& opt_key, int opt_index /*= -1*/) const -{ - Field* field = nullptr; - for (auto opt : m_optgroups){ - field = opt->get_fieldc(opt_key, opt_index); - if (field != nullptr) - return field; - } - return field; -} - -bool Page::set_value(const t_config_option_key& opt_key, const boost::any& value){ - bool changed = false; - for(auto optgroup: m_optgroups) { - if (optgroup->set_value(opt_key, value)) - changed = 1 ; - } - return changed; -} - -// package Slic3r::GUI::Tab::Page; -ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_label_width /*= -1*/) -{ - //! config_ have to be "right" - ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(this, title, m_config, true); - if (noncommon_label_width >= 0) - optgroup->label_width = noncommon_label_width; - -#ifdef __WXOSX__ - auto tab = GetParent()->GetParent(); -#else - auto tab = GetParent(); -#endif - optgroup->m_on_change = [this, tab](t_config_option_key opt_key, boost::any value){ - //! This function will be called from OptionGroup. - //! Using of CallAfter is redundant. - //! And in some cases it causes update() function to be recalled again -//! wxTheApp->CallAfter([this, opt_key, value]() { - static_cast<Tab*>(tab)->update_dirty(); - static_cast<Tab*>(tab)->on_value_change(opt_key, value); -//! }); - }; - - optgroup->m_get_initial_config = [this, tab](){ - DynamicPrintConfig config = static_cast<Tab*>(tab)->m_presets->get_selected_preset().config; - return config; - }; - - optgroup->m_get_sys_config = [this, tab](){ - DynamicPrintConfig config = static_cast<Tab*>(tab)->m_presets->get_selected_preset_parent()->config; - return config; - }; - - optgroup->have_sys_config = [this, tab](){ - return static_cast<Tab*>(tab)->m_presets->get_selected_preset_parent() != nullptr; - }; - - vsizer()->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10); - m_optgroups.push_back(optgroup); - - return optgroup; -} - -void SavePresetWindow::build(const wxString& title, const std::string& default_name, std::vector<std::string> &values) -{ - auto text = new wxStaticText(this, wxID_ANY, _(L("Save ")) + title + _(L(" as:")), - wxDefaultPosition, wxDefaultSize); - m_combo = new wxComboBox(this, wxID_ANY, from_u8(default_name), - wxDefaultPosition, wxDefaultSize, 0, 0, wxTE_PROCESS_ENTER); - for (auto value : values) - m_combo->Append(from_u8(value)); - auto buttons = CreateStdDialogButtonSizer(wxOK | wxCANCEL); - - auto sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(text, 0, wxEXPAND | wxALL, 10); - sizer->Add(m_combo, 0, wxEXPAND | wxLEFT | wxRIGHT, 10); - sizer->Add(buttons, 0, wxALIGN_CENTER_HORIZONTAL | wxALL, 10); - - wxButton* btn = static_cast<wxButton*>(FindWindowById(wxID_OK, this)); - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { accept(); }); - m_combo->Bind(wxEVT_TEXT_ENTER, [this](wxCommandEvent&) { accept(); }); - - SetSizer(sizer); - sizer->SetSizeHints(this); -} - -void SavePresetWindow::accept() -{ - m_chosen_name = normalize_utf8_nfc(m_combo->GetValue().ToUTF8()); - if (!m_chosen_name.empty()) { - const char* unusable_symbols = "<>:/\\|?*\""; - bool is_unusable_symbol = false; - bool is_unusable_postfix = false; - const std::string unusable_postfix = PresetCollection::get_suffix_modified();//"(modified)"; - for (size_t i = 0; i < std::strlen(unusable_symbols); i++){ - if (m_chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos){ - is_unusable_symbol = true; - break; - } - } - if (m_chosen_name.find(unusable_postfix) != std::string::npos) - is_unusable_postfix = true; - - if (is_unusable_symbol) { - show_error(this,_(L("The supplied name is not valid;")) + "\n" + - _(L("the following characters are not allowed:")) + " <>:/\\|?*\""); - } - else if (is_unusable_postfix){ - show_error(this,_(L("The supplied name is not valid;")) + "\n" + - _(L("the following postfix are not allowed:")) + "\n\t" + //unusable_postfix); - wxString::FromUTF8(unusable_postfix.c_str())); - } - else if (m_chosen_name.compare("- default -") == 0) { - show_error(this, _(L("The supplied name is not available."))); - } - else { - EndModal(wxID_OK); - } - } -} - -void TabSLAMaterial::build() -{ - m_presets = &m_preset_bundle->sla_materials; - load_initial_data(); - - auto page = add_options_page(_(L("Material")), "package_green.png"); - - auto optgroup = page->new_optgroup(_(L("Layers"))); - optgroup->append_single_option_line("layer_height"); - optgroup->append_single_option_line("initial_layer_height"); - - optgroup = page->new_optgroup(_(L("Exposure"))); - optgroup->append_single_option_line("exposure_time"); - optgroup->append_single_option_line("initial_exposure_time"); - - optgroup = page->new_optgroup(_(L("Corrections"))); - optgroup->label_width = 190; - std::vector<std::string> corrections = { "material_correction_printing", "material_correction_curing" }; - std::vector<std::string> axes{ "X", "Y", "Z" }; - for (auto& opt_key : corrections){ - auto line = Line{ m_config->def()->get(opt_key)->full_label, "" }; - int id = 0; - for (auto& axis : axes) { - auto opt = optgroup->get_option(opt_key, id); - opt.opt.label = axis; - opt.opt.width = 60; - line.append_option(opt); - ++id; - } - optgroup->append_line(line); - } - - page = add_options_page(_(L("Notes")), "note.png"); - optgroup = page->new_optgroup(_(L("Notes")), 0); - optgroup->label_width = 0; - Option option = optgroup->get_option("material_notes"); - option.opt.full_width = true; - option.opt.height = 250; - optgroup->append_single_option_line(option); - - page = add_options_page(_(L("Dependencies")), "wrench.png"); - optgroup = page->new_optgroup(_(L("Profile dependencies"))); - auto line = Line { _(L("Compatible printers")), "" }; - line.widget = [this](wxWindow* parent){ - return compatible_printers_widget(parent, &m_compatible_printers_checkbox, &m_compatible_printers_btn); - }; - optgroup->append_line(line, &m_colored_Label); - - option = optgroup->get_option("compatible_printers_condition"); - option.opt.full_width = true; - optgroup->append_single_option_line(option); - - line = Line{ "", "" }; - line.full_width = 1; - line.widget = [this](wxWindow* parent) { - return description_line_widget(parent, &m_parent_preset_description_line); - }; - optgroup->append_line(line); -} - -void TabSLAMaterial::update() -{ - if (get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptFFF) - return; // ys_FIXME -} - -} // GUI -} // Slic3r diff --git a/xs/src/slic3r/GUI/Tab.hpp b/xs/src/slic3r/GUI/Tab.hpp deleted file mode 100644 index e4e37d4eb..000000000 --- a/xs/src/slic3r/GUI/Tab.hpp +++ /dev/null @@ -1,385 +0,0 @@ -#ifndef slic3r_Tab_hpp_ -#define slic3r_Tab_hpp_ - -// The "Expert" tab at the right of the main tabbed window. -// -// This file implements following packages: -// Slic3r::GUI::Tab; -// Slic3r::GUI::Tab::Print; -// Slic3r::GUI::Tab::Filament; -// Slic3r::GUI::Tab::Printer; -// Slic3r::GUI::Tab::Page -// - Option page: For example, the Slic3r::GUI::Tab::Print has option pages "Layers and perimeters", "Infill", "Skirt and brim" ... -// Slic3r::GUI::SavePresetWindow -// - Dialog to select a new preset name to store the configuration. -// Slic3r::GUI::Tab::Preset; -// - Single preset item: name, file is default or external. - -#include <wx/panel.h> -#include <wx/notebook.h> -#include <wx/scrolwin.h> -#include <wx/sizer.h> -#include <wx/bmpcbox.h> -#include <wx/bmpbuttn.h> -#include <wx/treectrl.h> -#include <wx/imaglist.h> -#include <wx/statbox.h> -#include <wx/dataview.h> - -#include <map> -#include <vector> -#include <memory> - -#include "BedShapeDialog.hpp" - -//!enum { ID_TAB_TREE = wxID_HIGHEST + 1 }; - -namespace Slic3r { -namespace GUI { - -typedef std::pair<wxBitmap*, std::string> t_icon_description; -typedef std::vector<std::pair<wxBitmap*, std::string>> t_icon_descriptions; - -// Single Tab page containing a{ vsizer } of{ optgroups } -// package Slic3r::GUI::Tab::Page; -using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>; -class Page : public wxScrolledWindow -{ - wxWindow* m_parent; - wxString m_title; - size_t m_iconID; - wxBoxSizer* m_vsizer; -public: - Page(wxWindow* parent, const wxString title, const int iconID) : - m_parent(parent), - m_title(title), - m_iconID(iconID) - { - Create(m_parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - m_vsizer = new wxBoxSizer(wxVERTICAL); - m_item_color = &get_label_clr_default(); - SetSizer(m_vsizer); - } - ~Page(){} - - bool m_is_modified_values{ false }; - bool m_is_nonsys_values{ true }; - -public: - std::vector <ConfigOptionsGroupShp> m_optgroups; - DynamicPrintConfig* m_config; - - wxBoxSizer* vsizer() const { return m_vsizer; } - wxWindow* parent() const { return m_parent; } - wxString title() const { return m_title; } - size_t iconID() const { return m_iconID; } - void set_config(DynamicPrintConfig* config_in) { m_config = config_in; } - void reload_config(); - Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; - bool set_value(const t_config_option_key& opt_key, const boost::any& value); - ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1); - - bool set_item_colour(const wxColour *clr) { - if (m_item_color != clr) { - m_item_color = clr; - return true; - } - return false; - } - - const wxColour get_item_colour() { - return *m_item_color; - } - -protected: - // Color of TreeCtrlItem. The wxColour will be updated only if the new wxColour pointer differs from the currently rendered one. - const wxColour* m_item_color; -}; - -// Slic3r::GUI::Tab; - -using PageShp = std::shared_ptr<Page>; -class Tab: public wxPanel -{ - wxNotebook* m_parent; -#ifdef __WXOSX__ - wxPanel* m_tmp_panel; - int m_size_move = -1; -#endif // __WXOSX__ -protected: - std::string m_name; - const wxString m_title; - wxBitmapComboBox* m_presets_choice; - wxBitmapButton* m_btn_save_preset; - wxBitmapButton* m_btn_delete_preset; - wxBitmapButton* m_btn_hide_incompatible_presets; - wxBoxSizer* m_hsizer; - wxBoxSizer* m_left_sizer; - wxTreeCtrl* m_treectrl; - wxImageList* m_icons; - wxCheckBox* m_compatible_printers_checkbox; - wxButton* m_compatible_printers_btn; - wxButton* m_undo_btn; - wxButton* m_undo_to_sys_btn; - wxButton* m_question_btn; - wxComboCtrl* m_cc_presets_choice; - wxDataViewTreeCtrl* m_presetctrl; - wxImageList* m_preset_icons; - - // Cached bitmaps. - // A "flag" icon to be displayned next to the preset name in the Tab's combo box. - wxBitmap m_bmp_show_incompatible_presets; - wxBitmap m_bmp_hide_incompatible_presets; - // Bitmaps to be shown on the "Revert to system" aka "Lock to system" button next to each input field. - wxBitmap m_bmp_value_lock; - wxBitmap m_bmp_value_unlock; - wxBitmap m_bmp_white_bullet; - // The following bitmap points to either m_bmp_value_unlock or m_bmp_white_bullet, depending on whether the current preset has a parent preset. - wxBitmap *m_bmp_non_system; - // Bitmaps to be shown on the "Undo user changes" button next to each input field. - wxBitmap m_bmp_value_revert; -// wxBitmap m_bmp_value_unmodified; - wxBitmap m_bmp_question; - - // Colors for ui "decoration" - wxColour m_sys_label_clr; - wxColour m_modified_label_clr; - wxColour m_default_text_clr; - - // Tooltip text for reset buttons (for whole options group) - wxString m_ttg_value_lock; - wxString m_ttg_value_unlock; - wxString m_ttg_white_bullet_ns; - // The following text points to either m_ttg_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset. - wxString *m_ttg_non_system; - // Tooltip text to be shown on the "Undo user changes" button next to each input field. - wxString m_ttg_white_bullet; - wxString m_ttg_value_revert; - - // Tooltip text for reset buttons (for each option in group) - wxString m_tt_value_lock; - wxString m_tt_value_unlock; - // The following text points to either m_tt_value_unlock or m_ttg_white_bullet_ns, depending on whether the current preset has a parent preset. - wxString *m_tt_non_system; - // Tooltip text to be shown on the "Undo user changes" button next to each input field. - wxString m_tt_white_bullet; - wxString m_tt_value_revert; - - int m_icon_count; - std::map<std::string, size_t> m_icon_index; // Map from an icon file name to its index - std::vector<PageShp> m_pages; - bool m_disable_tree_sel_changed_event; - bool m_show_incompatible_presets; - - std::vector<std::string> m_reload_dependent_tabs = {}; - enum OptStatus { osSystemValue = 1, osInitValue = 2 }; - std::map<std::string, int> m_options_list; - int m_opt_status_value = 0; - - t_icon_descriptions m_icon_descriptions = {}; - - // The two following two event IDs are generated at Plater.pm by calling Wx::NewEventType. - wxEventType m_event_value_change = 0; - wxEventType m_event_presets_changed = 0; - - bool m_is_modified_values{ false }; - bool m_is_nonsys_values{ true }; - bool m_postpone_update_ui {false}; - - size_t m_selected_preset_item{ 0 }; - -public: - PresetBundle* m_preset_bundle; - bool m_show_btn_incompatible_presets = false; - PresetCollection* m_presets; - DynamicPrintConfig* m_config; - ogStaticText* m_parent_preset_description_line; - wxStaticText* m_colored_Label = nullptr; - -public: - Tab() {} - Tab(wxNotebook* parent, const wxString& title, const char* name) : - m_parent(parent), m_title(title), m_name(name) { - Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL, name); - get_tabs_list().push_back(this); - } - ~Tab(){ - delete_tab_from_list(this); - } - - wxWindow* parent() const { return m_parent; } - wxString title() const { return m_title; } - std::string name() const { return m_name; } - - // Set the events to the callbacks posted to the main frame window (currently implemented in Perl). - void set_event_value_change(wxEventType evt) { m_event_value_change = evt; } - void set_event_presets_changed(wxEventType evt) { m_event_presets_changed = evt; } - - void create_preset_tab(PresetBundle *preset_bundle); - void load_current_preset(); - void rebuild_page_tree(bool tree_sel_change_event = false); - void select_preset(std::string preset_name = ""); - bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = ""); - wxSizer* compatible_printers_widget(wxWindow* parent, wxCheckBox** checkbox, wxButton** btn); - - void update_presetsctrl(wxDataViewTreeCtrl* ui, bool show_incompatible); - void load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value = false); - void reload_compatible_printers_widget(); - - void OnTreeSelChange(wxTreeEvent& event); - void OnKeyDown(wxKeyEvent& event); - - void save_preset(std::string name = ""); - void delete_preset(); - void toggle_show_hide_incompatible(); - void update_show_hide_incompatible_button(); - void update_ui_from_settings(); - void update_labels_colour(); - void update_changed_ui(); - void get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool& modified_page); - void update_changed_tree_ui(); - void update_undo_buttons(); - - void on_roll_back_value(const bool to_sys = false); - - PageShp add_options_page(const wxString& title, const std::string& icon, bool is_extruder_pages = false); - - virtual void OnActivate(); - virtual void on_preset_loaded(){} - virtual void build() = 0; - virtual void update() = 0; - virtual void init_options_list(); - void load_initial_data(); - void update_dirty(); - void update_tab_ui(); - void load_config(const DynamicPrintConfig& config); - virtual void reload_config(); - Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const; - bool set_value(const t_config_option_key& opt_key, const boost::any& value); - wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText); - bool current_preset_is_dirty(); - - DynamicPrintConfig* get_config() { return m_config; } - PresetCollection* get_presets() { return m_presets; } - std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; } - size_t get_selected_preset_item() { return m_selected_preset_item; } - - void on_value_change(const std::string& opt_key, const boost::any& value); - - void update_wiping_button_visibility(); -protected: - void on_presets_changed(); - void update_preset_description_line(); - void update_frequently_changed_parameters(); - void update_tab_presets(wxComboCtrl* ui, bool show_incompatible); - void fill_icon_descriptions(); - void set_tooltips_text(); -}; - -//Slic3r::GUI::Tab::Print; -class TabPrint : public Tab -{ -public: - TabPrint() {} - TabPrint(wxNotebook* parent) : - Tab(parent, _(L("Print Settings")), "print") {} - ~TabPrint(){} - - ogStaticText* m_recommended_thin_wall_thickness_description_line; - bool m_support_material_overhangs_queried = false; - - void build() override; - void reload_config() override; - void update() override; - void OnActivate() override; -}; - -//Slic3r::GUI::Tab::Filament; -class TabFilament : public Tab -{ - ogStaticText* m_volumetric_speed_description_line; - ogStaticText* m_cooling_description_line; -public: - TabFilament() {} - TabFilament(wxNotebook* parent) : - Tab(parent, _(L("Filament Settings")), "filament") {} - ~TabFilament(){} - - void build() override; - void reload_config() override; - void update() override; - void OnActivate() override; -}; - -//Slic3r::GUI::Tab::Printer; -class TabPrinter : public Tab -{ - bool m_has_single_extruder_MM_page = false; - bool m_use_silent_mode = false; - void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key); - bool m_rebuild_kinematics_page = false; - - std::vector<PageShp> m_pages_fff; - std::vector<PageShp> m_pages_sla; -public: - wxButton* m_serial_test_btn; - wxButton* m_print_host_test_btn; - wxButton* m_printhost_browse_btn; - - size_t m_extruders_count; - size_t m_extruders_count_old = 0; - size_t m_initial_extruders_count; - size_t m_sys_extruders_count; - - PrinterTechnology m_printer_technology = ptFFF; - - TabPrinter() {} - TabPrinter(wxNotebook* parent) : Tab(parent, _(L("Printer Settings")), "printer") {} - ~TabPrinter(){} - - void build() override; - void build_fff(); - void build_sla(); - void update() override; - void update_fff(); - void update_sla(); - void update_pages(); // update m_pages according to printer technology - void update_serial_ports(); - void extruders_count_changed(size_t extruders_count); - PageShp build_kinematics_page(); - void build_extruder_pages(); - void on_preset_loaded() override; - void init_options_list() override; -}; - -class TabSLAMaterial : public Tab -{ -public: - TabSLAMaterial() {} - TabSLAMaterial(wxNotebook* parent) : - Tab(parent, _(L("SLA Material Settings")), "sla_material") {} - ~TabSLAMaterial(){} - - void build() override; - void update() override; - void init_options_list() override; -}; - -class SavePresetWindow :public wxDialog -{ -public: - SavePresetWindow(wxWindow* parent) :wxDialog(parent, wxID_ANY, _(L("Save preset"))){} - ~SavePresetWindow(){} - - std::string m_chosen_name; - wxComboBox* m_combo; - - void build(const wxString& title, const std::string& default_name, std::vector<std::string> &values); - void accept(); - std::string get_name() { return m_chosen_name; } -}; - -} // GUI -} // Slic3r - -#endif /* slic3r_Tab_hpp_ */ diff --git a/xs/src/slic3r/GUI/TabIface.cpp b/xs/src/slic3r/GUI/TabIface.cpp deleted file mode 100644 index 29833322b..000000000 --- a/xs/src/slic3r/GUI/TabIface.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "TabIface.hpp" -#include "Tab.hpp" - -namespace Slic3r { - -void TabIface::load_current_preset() { m_tab->load_current_preset(); } -void TabIface::update_tab_ui() { m_tab->update_tab_ui(); } -void TabIface::update_ui_from_settings() { m_tab->update_ui_from_settings();} -void TabIface::select_preset(char* name) { m_tab->select_preset(name);} -void TabIface::load_config(DynamicPrintConfig* config) { m_tab->load_config(*config);} -void TabIface::load_key_value(char* opt_key, char* value){ m_tab->load_key_value(opt_key, static_cast<std::string>(value)); } -bool TabIface::current_preset_is_dirty() { return m_tab->current_preset_is_dirty();} -void TabIface::OnActivate() { return m_tab->OnActivate();} -size_t TabIface::get_selected_preset_item() { return m_tab->get_selected_preset_item(); } -std::string TabIface::title() { return m_tab->title().ToUTF8().data(); } -DynamicPrintConfig* TabIface::get_config() { return m_tab->get_config(); } -PresetCollection* TabIface::get_presets() { return m_tab!=nullptr ? m_tab->get_presets() : nullptr; } -std::vector<std::string> TabIface::get_dependent_tabs() { return m_tab->get_dependent_tabs(); } - -}; // namespace Slic3r diff --git a/xs/src/slic3r/GUI/TabIface.hpp b/xs/src/slic3r/GUI/TabIface.hpp deleted file mode 100644 index 2f7f4e8e7..000000000 --- a/xs/src/slic3r/GUI/TabIface.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef slic3r_TabIface_hpp_ -#define slic3r_TabIface_hpp_ - -#include <vector> -#include <string> - -namespace Slic3r { - class DynamicPrintConfig; - class PresetCollection; - -namespace GUI { - class Tab; -} - -class TabIface { -public: - TabIface() : m_tab(nullptr) {} - TabIface(GUI::Tab *tab) : m_tab(tab) {} -// TabIface(const TabIface &rhs) : m_tab(rhs.m_tab) {} - - void load_current_preset(); - void update_tab_ui(); - void update_ui_from_settings(); - void select_preset(char* name); - std::string title(); - void load_config(DynamicPrintConfig* config); - void load_key_value(char* opt_key, char* value); - bool current_preset_is_dirty(); - void OnActivate(); - DynamicPrintConfig* get_config(); - PresetCollection* get_presets(); - std::vector<std::string> get_dependent_tabs(); - size_t get_selected_preset_item(); - -protected: - GUI::Tab *m_tab; -}; // namespace GUI - -}; // namespace Slic3r - -#endif /* slic3r_TabIface_hpp_ */ diff --git a/xs/src/slic3r/GUI/UpdateDialogs.cpp b/xs/src/slic3r/GUI/UpdateDialogs.cpp deleted file mode 100644 index 70d9c851c..000000000 --- a/xs/src/slic3r/GUI/UpdateDialogs.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "UpdateDialogs.hpp" - -#include <wx/settings.h> -#include <wx/sizer.h> -#include <wx/event.h> -#include <wx/stattext.h> -#include <wx/button.h> -#include <wx/hyperlink.h> -#include <wx/statbmp.h> -#include <wx/checkbox.h> - -#include "libslic3r/libslic3r.h" -#include "libslic3r/Utils.hpp" -#include "GUI.hpp" -#include "ConfigWizard.hpp" - -namespace Slic3r { -namespace GUI { - - -static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update"); - - -// MsgUpdateSlic3r - -MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) : - MsgDialog(nullptr, _(L("Update available")), _(L("New version of Slic3r PE is available"))), - ver_current(ver_current), - ver_online(ver_online) -{ - const auto url = wxString::Format("https://github.com/prusa3d/Slic3r/releases/tag/version_%s", ver_online.to_string()); - auto *link = new wxHyperlinkCtrl(this, wxID_ANY, url, url); - - auto *text = new wxStaticText(this, wxID_ANY, _(L("To download, follow the link below."))); - const auto link_width = link->GetSize().GetWidth(); - text->Wrap(CONTENT_WIDTH > link_width ? CONTENT_WIDTH : link_width); - content_sizer->Add(text); - content_sizer->AddSpacer(VERT_SPACING); - - auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING); - versions->Add(new wxStaticText(this, wxID_ANY, _(L("Current version:")))); - versions->Add(new wxStaticText(this, wxID_ANY, ver_current.to_string())); - versions->Add(new wxStaticText(this, wxID_ANY, _(L("New version:")))); - versions->Add(new wxStaticText(this, wxID_ANY, ver_online.to_string())); - content_sizer->Add(versions); - content_sizer->AddSpacer(VERT_SPACING); - - content_sizer->Add(link); - content_sizer->AddSpacer(2*VERT_SPACING); - - cbox = new wxCheckBox(this, wxID_ANY, _(L("Don't notify about new releases any more"))); - content_sizer->Add(cbox); - content_sizer->AddSpacer(VERT_SPACING); - - Fit(); -} - -MsgUpdateSlic3r::~MsgUpdateSlic3r() {} - -bool MsgUpdateSlic3r::disable_version_check() const -{ - return cbox->GetValue(); -} - - -// MsgUpdateConfig - -MsgUpdateConfig::MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates) : - MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE) -{ - auto *text = new wxStaticText(this, wxID_ANY, _(L( - "Would you like to install it?\n\n" - "Note that a full configuration snapshot will be created first. It can then be restored at any time " - "should there be a problem with the new version.\n\n" - "Updated configuration bundles:" - ))); - text->Wrap(CONTENT_WIDTH); - content_sizer->Add(text); - content_sizer->AddSpacer(VERT_SPACING); - - auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING); - for (const auto &update : updates) { - auto *text_vendor = new wxStaticText(this, wxID_ANY, update.first); - text_vendor->SetFont(boldfont); - versions->Add(text_vendor); - versions->Add(new wxStaticText(this, wxID_ANY, update.second)); - } - - content_sizer->Add(versions); - content_sizer->AddSpacer(2*VERT_SPACING); - - auto *btn_cancel = new wxButton(this, wxID_CANCEL); - btn_sizer->Add(btn_cancel); - btn_sizer->AddSpacer(HORIZ_SPACING); - auto *btn_ok = new wxButton(this, wxID_OK); - btn_sizer->Add(btn_ok); - btn_ok->SetFocus(); - - Fit(); -} - -MsgUpdateConfig::~MsgUpdateConfig() {} - - -// MsgDataIncompatible - -MsgDataIncompatible::MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats) : - MsgDialog(nullptr, _(L("Slic3r incompatibility")), _(L("Slic3r configuration is incompatible")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), wxID_NONE) -{ - auto *text = new wxStaticText(this, wxID_ANY, _(L( - "This version of Slic3r PE is not compatible with currently installed configuration bundles.\n" - "This probably happened as a result of running an older Slic3r PE after using a newer one.\n\n" - - "You may either exit Slic3r and try again with a newer version, or you may re-run the initial configuration. " - "Doing so will create a backup snapshot of the existing configuration before installing files compatible with this Slic3r.\n" - ))); - text->Wrap(CONTENT_WIDTH); - content_sizer->Add(text); - - auto *text2 = new wxStaticText(this, wxID_ANY, wxString::Format(_(L("This Slic3r PE version: %s")), SLIC3R_VERSION)); - text2->Wrap(CONTENT_WIDTH); - content_sizer->Add(text2); - content_sizer->AddSpacer(VERT_SPACING); - - auto *text3 = new wxStaticText(this, wxID_ANY, _(L("Incompatible bundles:"))); - text3->Wrap(CONTENT_WIDTH); - content_sizer->Add(text3); - content_sizer->AddSpacer(VERT_SPACING); - - auto *versions = new wxFlexGridSizer(2, 0, VERT_SPACING); - for (const auto &incompat : incompats) { - auto *text_vendor = new wxStaticText(this, wxID_ANY, incompat.first); - text_vendor->SetFont(boldfont); - versions->Add(text_vendor); - versions->Add(new wxStaticText(this, wxID_ANY, incompat.second)); - } - - content_sizer->Add(versions); - content_sizer->AddSpacer(2*VERT_SPACING); - - auto *btn_exit = new wxButton(this, wxID_EXIT, _(L("Exit Slic3r"))); - btn_sizer->Add(btn_exit); - btn_sizer->AddSpacer(HORIZ_SPACING); - auto *btn_reconf = new wxButton(this, wxID_REPLACE, _(L("Re-configure"))); - btn_sizer->Add(btn_reconf); - btn_exit->SetFocus(); - - auto exiter = [this](const wxCommandEvent& evt) { this->EndModal(evt.GetId()); }; - btn_exit->Bind(wxEVT_BUTTON, exiter); - btn_reconf->Bind(wxEVT_BUTTON, exiter); - - Fit(); -} - -MsgDataIncompatible::~MsgDataIncompatible() {} - - -// MsgDataLegacy - -MsgDataLegacy::MsgDataLegacy() : - MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update"))) -{ - auto *text = new wxStaticText(this, wxID_ANY, wxString::Format( - _(L( - "Slic3r PE now uses an updated configuration structure.\n\n" - - "So called 'System presets' have been introduced, which hold the built-in default settings for various " - "printers. These System presets cannot be modified, instead, users now may create their " - "own presets inheriting settings from one of the System presets.\n" - "An inheriting preset may either inherit a particular value from its parent or override it with a customized value.\n\n" - - "Please proceed with the %s that follows to set up the new presets " - "and to choose whether to enable automatic preset updates." - )), - ConfigWizard::name() - )); - text->Wrap(CONTENT_WIDTH); - content_sizer->Add(text); - content_sizer->AddSpacer(VERT_SPACING); - - auto *text2 = new wxStaticText(this, wxID_ANY, _(L("For more information please visit our wiki page:"))); - static const wxString url("https://github.com/prusa3d/Slic3r/wiki/Slic3r-PE-1.40-configuration-update"); - // The wiki page name is intentionally not localized: - auto *link = new wxHyperlinkCtrl(this, wxID_ANY, "Slic3r PE 1.40 configuration update", CONFIG_UPDATE_WIKI_URL); - content_sizer->Add(text2); - content_sizer->Add(link); - content_sizer->AddSpacer(VERT_SPACING); - - Fit(); -} - -MsgDataLegacy::~MsgDataLegacy() {} - - -} -} diff --git a/xs/src/slic3r/GUI/UpdateDialogs.hpp b/xs/src/slic3r/GUI/UpdateDialogs.hpp deleted file mode 100644 index 62548b98b..000000000 --- a/xs/src/slic3r/GUI/UpdateDialogs.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef slic3r_UpdateDialogs_hpp_ -#define slic3r_UpdateDialogs_hpp_ - -#include <string> -#include <unordered_map> - -#include "slic3r/Utils/Semver.hpp" -#include "MsgDialog.hpp" - -class wxBoxSizer; -class wxCheckBox; - -namespace Slic3r { - -namespace GUI { - - -// A confirmation dialog listing configuration updates -class MsgUpdateSlic3r : public MsgDialog -{ -public: - MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online); - MsgUpdateSlic3r(MsgUpdateSlic3r &&) = delete; - MsgUpdateSlic3r(const MsgUpdateSlic3r &) = delete; - MsgUpdateSlic3r &operator=(MsgUpdateSlic3r &&) = delete; - MsgUpdateSlic3r &operator=(const MsgUpdateSlic3r &) = delete; - virtual ~MsgUpdateSlic3r(); - - // Tells whether the user checked the "don't bother me again" checkbox - bool disable_version_check() const; - -private: - const Semver &ver_current; - const Semver &ver_online; - wxCheckBox *cbox; -}; - - -// Confirmation dialog informing about configuration update. Lists updated bundles & their versions. -class MsgUpdateConfig : public MsgDialog -{ -public: - // updates is a map of "vendor name" -> "version (comment)" - MsgUpdateConfig(const std::unordered_map<std::string, std::string> &updates); - MsgUpdateConfig(MsgUpdateConfig &&) = delete; - MsgUpdateConfig(const MsgUpdateConfig &) = delete; - MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete; - MsgUpdateConfig &operator=(const MsgUpdateConfig &) = delete; - ~MsgUpdateConfig(); -}; - -// Informs about currently installed bundles not being compatible with the running Slic3r. Asks about action. -class MsgDataIncompatible : public MsgDialog -{ -public: - // incompats is a map of "vendor name" -> "version restrictions" - MsgDataIncompatible(const std::unordered_map<std::string, wxString> &incompats); - MsgDataIncompatible(MsgDataIncompatible &&) = delete; - MsgDataIncompatible(const MsgDataIncompatible &) = delete; - MsgDataIncompatible &operator=(MsgDataIncompatible &&) = delete; - MsgDataIncompatible &operator=(const MsgDataIncompatible &) = delete; - ~MsgDataIncompatible(); -}; - -// Informs about a legacy data directory - an update from Slic3r PE < 1.40 -class MsgDataLegacy : public MsgDialog -{ -public: - MsgDataLegacy(); - MsgDataLegacy(MsgDataLegacy &&) = delete; - MsgDataLegacy(const MsgDataLegacy &) = delete; - MsgDataLegacy &operator=(MsgDataLegacy &&) = delete; - MsgDataLegacy &operator=(const MsgDataLegacy &) = delete; - ~MsgDataLegacy(); -}; - - -} -} - -#endif diff --git a/xs/src/slic3r/GUI/Widget.hpp b/xs/src/slic3r/GUI/Widget.hpp deleted file mode 100644 index bcf772469..000000000 --- a/xs/src/slic3r/GUI/Widget.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef WIDGET_HPP -#define WIDGET_HPP -#include <wx/wxprec.h> -#ifndef WX_PRECOM -#include <wx/wx.h> -#endif - -class Widget { -protected: - wxSizer* _sizer; -public: - Widget(): _sizer(nullptr) { } - bool valid() const { return _sizer != nullptr; } - wxSizer* sizer() const { return _sizer; } -}; -#endif diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.cpp b/xs/src/slic3r/GUI/WipeTowerDialog.cpp deleted file mode 100644 index eef4017c1..000000000 --- a/xs/src/slic3r/GUI/WipeTowerDialog.cpp +++ /dev/null @@ -1,338 +0,0 @@ -#include <algorithm> -#include <sstream> -#include "WipeTowerDialog.hpp" -#include "GUI.hpp" - -#include <wx/sizer.h> - -RammingDialog::RammingDialog(wxWindow* parent,const std::string& parameters) -: wxDialog(parent, wxID_ANY, _(L("Ramming customization")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) -{ - m_panel_ramming = new RammingPanel(this,parameters); - - // Not found another way of getting the background colours of RammingDialog, RammingPanel and Chart correct than setting - // them all explicitely. Reading the parent colour yielded colour that didn't really match it, no wxSYS_COLOUR_... matched - // colour used for the dialog. Same issue (and "solution") here : https://forums.wxwidgets.org/viewtopic.php?f=1&t=39608 - // Whoever can fix this, feel free to do so. - this-> SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK)); - m_panel_ramming->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_FRAMEBK)); - m_panel_ramming->Show(true); - this->Show(); - - auto main_sizer = new wxBoxSizer(wxVERTICAL); - main_sizer->Add(m_panel_ramming, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5); - main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, 10); - SetSizer(main_sizer); - main_sizer->SetSizeHints(this); - - this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); }); - - this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) { - m_output_data = m_panel_ramming->get_parameters(); - EndModal(wxID_OK); - },wxID_OK); - this->Show(); - wxMessageDialog(this,_(L("Ramming denotes the rapid extrusion just before a tool change in a single-extruder MM printer. Its purpose is to " - "properly shape the end of the unloaded filament so it does not prevent insertion of the new filament and can itself " - "be reinserted later. This phase is important and different materials can require different extrusion speeds to get " - "the good shape. For this reason, the extrusion rates during ramming are adjustable.\n\nThis is an expert-level " - "setting, incorrect adjustment will likely lead to jams, extruder wheel grinding into filament etc.")),_(L("Warning")),wxOK|wxICON_EXCLAMATION).ShowModal(); -} - - - - - -RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters) -: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxPoint(50,50), wxSize(800,350),wxBORDER_RAISED*/) -{ - auto sizer_chart = new wxBoxSizer(wxVERTICAL); - auto sizer_param = new wxBoxSizer(wxVERTICAL); - - std::stringstream stream{ parameters }; - stream >> m_ramming_line_width_multiplicator >> m_ramming_step_multiplicator; - int ramming_speed_size = 0; - float dummy = 0.f; - while (stream >> dummy) - ++ramming_speed_size; - stream.clear(); - stream.get(); - - std::vector<std::pair<float, float>> buttons; - float x = 0.f; - float y = 0.f; - while (stream >> x >> y) - buttons.push_back(std::make_pair(x, y)); - - m_chart = new Chart(this, wxRect(10, 10, 480, 360), buttons, ramming_speed_size, 0.25f); - m_chart->SetBackgroundColour(parent->GetBackgroundColour()); // see comment in RammingDialog constructor - sizer_chart->Add(m_chart, 0, wxALL, 5); - - m_widget_time = new wxSpinCtrlDouble(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0.,5.0,3.,0.5); - m_widget_volume = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,0,10000,0); - m_widget_ramming_line_width_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100); - m_widget_ramming_step_multiplicator = new wxSpinCtrl(this,wxID_ANY,wxEmptyString,wxDefaultPosition,wxSize(75, -1),wxSP_ARROW_KEYS,10,200,100); - - auto gsizer_param = new wxFlexGridSizer(2, 5, 15); - gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total ramming time")) + " (" + _(L("s")) + "):")), 0, wxALIGN_CENTER_VERTICAL); - gsizer_param->Add(m_widget_time); - gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Total rammed volume")) + " (" + _(L("mm")) + wxString("³):", wxConvUTF8))), 0, wxALIGN_CENTER_VERTICAL); - gsizer_param->Add(m_widget_volume); - gsizer_param->AddSpacer(20); - gsizer_param->AddSpacer(20); - gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line width")) + " (%):")), 0, wxALIGN_CENTER_VERTICAL); - gsizer_param->Add(m_widget_ramming_line_width_multiplicator); - gsizer_param->Add(new wxStaticText(this, wxID_ANY, wxString(_(L("Ramming line spacing")) + " (%):")), 0, wxALIGN_CENTER_VERTICAL); - gsizer_param->Add(m_widget_ramming_step_multiplicator); - - sizer_param->Add(gsizer_param, 0, wxTOP, 100); - - m_widget_time->SetValue(m_chart->get_time()); - m_widget_time->SetDigits(2); - m_widget_volume->SetValue(m_chart->get_volume()); - m_widget_volume->Disable(); - m_widget_ramming_line_width_multiplicator->SetValue(m_ramming_line_width_multiplicator); - m_widget_ramming_step_multiplicator->SetValue(m_ramming_step_multiplicator); - - m_widget_ramming_step_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); }); - m_widget_ramming_line_width_multiplicator->Bind(wxEVT_TEXT,[this](wxCommandEvent&) { line_parameters_changed(); }); - - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(sizer_chart, 0, wxALL, 5); - sizer->Add(sizer_param, 0, wxALL, 10); - - sizer->SetSizeHints(this); - SetSizer(sizer); - - m_widget_time->Bind(wxEVT_TEXT,[this](wxCommandEvent&) {m_chart->set_xy_range(m_widget_time->GetValue(),-1);}); - m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value - m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value - Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} ); - Refresh(this); -} - -void RammingPanel::line_parameters_changed() { - m_ramming_line_width_multiplicator = m_widget_ramming_line_width_multiplicator->GetValue(); - m_ramming_step_multiplicator = m_widget_ramming_step_multiplicator->GetValue(); -} - -std::string RammingPanel::get_parameters() -{ - std::vector<float> speeds = m_chart->get_ramming_speed(0.25f); - std::vector<std::pair<float,float>> buttons = m_chart->get_buttons(); - std::stringstream stream; - stream << m_ramming_line_width_multiplicator << " " << m_ramming_step_multiplicator; - for (const float& speed_value : speeds) - stream << " " << speed_value; - stream << "|"; - for (const auto& button : buttons) - stream << " " << button.first << " " << button.second; - return stream.str(); -} - - -#define ITEM_WIDTH 60 -// Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode: -WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders) -: wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) -{ - auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize); - m_panel_wiping = new WipingPanel(this,matrix,extruders, widget_button); - - auto main_sizer = new wxBoxSizer(wxVERTICAL); - - // set min sizer width according to extruders count - const auto sizer_width = (int)((sqrt(matrix.size()) + 2.8)*ITEM_WIDTH); - main_sizer->SetMinSize(wxSize(sizer_width, -1)); - - main_sizer->Add(m_panel_wiping, 0, wxEXPAND | wxALL, 5); - main_sizer->Add(widget_button, 0, wxALIGN_CENTER_HORIZONTAL | wxCENTER | wxBOTTOM, 5); - main_sizer->Add(CreateButtonSizer(wxOK | wxCANCEL), 0, wxALIGN_CENTER_HORIZONTAL | wxBOTTOM, 10); - SetSizer(main_sizer); - main_sizer->SetSizeHints(this); - - this->Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& e) { EndModal(wxCANCEL); }); - - this->Bind(wxEVT_BUTTON,[this](wxCommandEvent&) { // if OK button is clicked.. - m_output_matrix = m_panel_wiping->read_matrix_values(); // ..query wiping panel and save returned values - m_output_extruders = m_panel_wiping->read_extruders_values(); // so they can be recovered later by calling get_...() - EndModal(wxID_OK); - },wxID_OK); - - this->Show(); -} - -// This function allows to "play" with sizers parameters (like align or border) -void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift/*=0*/) -{ - sizer->Add(new wxStaticText(page, wxID_ANY, info,wxDefaultPosition,wxSize(0,50)), 0, wxEXPAND | wxLEFT, 15); - auto table_sizer = new wxBoxSizer(wxVERTICAL); - sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift); - table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50); - table_sizer->Add(grid_sizer, 0, wxALIGN_CENTER | wxTOP, 10); -} - -// This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers) -WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button) -: wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxBORDER_RAISED*/) -{ - m_widget_button = widget_button; // pointer to the button in parent dialog - m_widget_button->Bind(wxEVT_BUTTON,[this](wxCommandEvent&){ toggle_advanced(true); }); - - m_number_of_extruders = (int)(sqrt(matrix.size())+0.001); - - // Create two switched panels with their own sizers - m_sizer_simple = new wxBoxSizer(wxVERTICAL); - m_sizer_advanced = new wxBoxSizer(wxVERTICAL); - m_page_simple = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - m_page_advanced = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); - m_page_simple->SetSizer(m_sizer_simple); - m_page_advanced->SetSizer(m_sizer_advanced); - - auto gridsizer_simple = new wxGridSizer(3, 5, 10); - m_gridsizer_advanced = new wxGridSizer(m_number_of_extruders+1, 5, 1); - - // First create controls for advanced mode and assign them to m_page_advanced: - for (unsigned int i = 0; i < m_number_of_extruders; ++i) { - edit_boxes.push_back(std::vector<wxTextCtrl*>(0)); - - for (unsigned int j = 0; j < m_number_of_extruders; ++j) { - edit_boxes.back().push_back(new wxTextCtrl(m_page_advanced, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(ITEM_WIDTH, -1))); - if (i == j) - edit_boxes[i][j]->Disable(); - else - edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i])); - } - } - m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString(""))); - for (unsigned int i = 0; i < m_number_of_extruders; ++i) - m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - for (unsigned int i = 0; i < m_number_of_extruders; ++i) { - m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - for (unsigned int j = 0; j < m_number_of_extruders; ++j) - m_gridsizer_advanced->Add(edit_boxes[j][i], 0); - } - - // collect and format sizer - format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced, - _(L("Here you can adjust required purging volume (mm³) for any given pair of tools.")), - _(L("Extruder changed to"))); - - // Hide preview page before new page creating - // It allows to do that from a beginning of the main panel - m_page_advanced->Hide(); - - // Now the same for simple mode: - gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString("")), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("unloaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - gridsizer_simple->Add(new wxStaticText(m_page_simple,wxID_ANY,wxString(_(L("loaded")))), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - - for (unsigned int i=0;i<m_number_of_extruders;++i) { - m_old.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(80, -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i])); - m_new.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(80, -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1])); - gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - gridsizer_simple->Add(m_old.back(),0); - gridsizer_simple->Add(m_new.back(),0); - } - - // collect and format sizer - format_sizer(m_sizer_simple, m_page_simple, gridsizer_simple, - _(L("Total purging volume is calculated by summing two values below, depending on which tools are loaded/unloaded.")), - _(L("Volume to purge (mm³) when the filament is being")), 50); - - m_sizer = new wxBoxSizer(wxVERTICAL); - m_sizer->Add(m_page_simple, 0, wxEXPAND | wxALL, 25); - m_sizer->Add(m_page_advanced, 0, wxEXPAND | wxALL, 25); - - m_sizer->SetSizeHints(this); - SetSizer(m_sizer); - - toggle_advanced(); // to show/hide what is appropriate - - m_page_advanced->Bind(wxEVT_PAINT,[this](wxPaintEvent&) { - wxPaintDC dc(m_page_advanced); - int y_pos = 0.5 * (edit_boxes[0][0]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetPosition().y + edit_boxes[0][edit_boxes.size()-1]->GetSize().y); - wxString label = _(L("From")); - int text_width = 0; - int text_height = 0; - dc.GetTextExtent(label,&text_width,&text_height); - int xpos = m_gridsizer_advanced->GetPosition().x; - dc.DrawRotatedText(label,xpos-text_height,y_pos + text_width/2.f,90); - }); -} - - - - -// Reads values from the (advanced) wiping matrix: -std::vector<float> WipingPanel::read_matrix_values() { - if (!m_advanced) - fill_in_matrix(); - std::vector<float> output; - for (unsigned int i=0;i<m_number_of_extruders;++i) { - for (unsigned int j=0;j<m_number_of_extruders;++j) { - double val = 0.; - edit_boxes[j][i]->GetValue().ToDouble(&val); - output.push_back((float)val); - } - } - return output; -} - -// Reads values from simple mode to save them for next time: -std::vector<float> WipingPanel::read_extruders_values() { - std::vector<float> output; - for (unsigned int i=0;i<m_number_of_extruders;++i) { - output.push_back(m_old[i]->GetValue()); - output.push_back(m_new[i]->GetValue()); - } - return output; -} - -// This updates the "advanced" matrix based on values from "simple" mode -void WipingPanel::fill_in_matrix() { - for (unsigned i=0;i<m_number_of_extruders;++i) { - for (unsigned j=0;j<m_number_of_extruders;++j) { - if (i==j) continue; - edit_boxes[j][i]->SetValue(wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue())); - } - } -} - - - -// Function to check if simple and advanced settings are matching -bool WipingPanel::advanced_matches_simple() { - for (unsigned i=0;i<m_number_of_extruders;++i) { - for (unsigned j=0;j<m_number_of_extruders;++j) { - if (i==j) continue; - if (edit_boxes[j][i]->GetValue() != (wxString("")<< (m_old[i]->GetValue() + m_new[j]->GetValue()))) - return false; - } - } - return true; -} - - -// Switches the dialog from simple to advanced mode and vice versa -void WipingPanel::toggle_advanced(bool user_action) { - if (m_advanced && !advanced_matches_simple() && user_action) { - if (wxMessageDialog(this,wxString(_(L("Switching to simple settings will discard changes done in the advanced mode!\n\nDo you want to proceed?"))), - wxString(_(L("Warning"))),wxYES_NO|wxICON_EXCLAMATION).ShowModal() != wxID_YES) - return; - } - if (user_action) - m_advanced = !m_advanced; // user demands a change -> toggle - else - m_advanced = !advanced_matches_simple(); // if called from constructor, show what is appropriate - - (m_advanced ? m_page_advanced : m_page_simple)->Show(); - (!m_advanced ? m_page_advanced : m_page_simple)->Hide(); - - m_widget_button->SetLabel(m_advanced ? _(L("Show simplified settings")) : _(L("Show advanced settings"))); - if (m_advanced) - if (user_action) fill_in_matrix(); // otherwise keep values loaded from config - - m_sizer->Layout(); - Refresh(); -} diff --git a/xs/src/slic3r/GUI/WipeTowerDialog.hpp b/xs/src/slic3r/GUI/WipeTowerDialog.hpp deleted file mode 100644 index d858062da..000000000 --- a/xs/src/slic3r/GUI/WipeTowerDialog.hpp +++ /dev/null @@ -1,90 +0,0 @@ -#ifndef _WIPE_TOWER_DIALOG_H_ -#define _WIPE_TOWER_DIALOG_H_ - -#include <wx/spinctrl.h> -#include <wx/stattext.h> -#include <wx/textctrl.h> -#include <wx/checkbox.h> -#include <wx/msgdlg.h> - -#include "RammingChart.hpp" - - -class RammingPanel : public wxPanel { -public: - RammingPanel(wxWindow* parent); - RammingPanel(wxWindow* parent,const std::string& data); - std::string get_parameters(); - -private: - Chart* m_chart = nullptr; - wxSpinCtrl* m_widget_volume = nullptr; - wxSpinCtrl* m_widget_ramming_line_width_multiplicator = nullptr; - wxSpinCtrl* m_widget_ramming_step_multiplicator = nullptr; - wxSpinCtrlDouble* m_widget_time = nullptr; - int m_ramming_step_multiplicator; - int m_ramming_line_width_multiplicator; - - void line_parameters_changed(); -}; - - -class RammingDialog : public wxDialog { -public: - RammingDialog(wxWindow* parent,const std::string& parameters); - std::string get_parameters() { return m_output_data; } -private: - RammingPanel* m_panel_ramming = nullptr; - std::string m_output_data; -}; - - - - - - - -class WipingPanel : public wxPanel { -public: - WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button); - std::vector<float> read_matrix_values(); - std::vector<float> read_extruders_values(); - void toggle_advanced(bool user_action = false); - void format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_sizer, const wxString& info, const wxString& table_title, int table_lshift=0); - -private: - void fill_in_matrix(); - bool advanced_matches_simple(); - - std::vector<wxSpinCtrl*> m_old; - std::vector<wxSpinCtrl*> m_new; - std::vector<std::vector<wxTextCtrl*>> edit_boxes; - unsigned int m_number_of_extruders = 0; - bool m_advanced = false; - wxPanel* m_page_simple = nullptr; - wxPanel* m_page_advanced = nullptr; - wxBoxSizer* m_sizer = nullptr; - wxBoxSizer* m_sizer_simple = nullptr; - wxBoxSizer* m_sizer_advanced = nullptr; - wxGridSizer* m_gridsizer_advanced = nullptr; - wxButton* m_widget_button = nullptr; -}; - - - - - -class WipingDialog : public wxDialog { -public: - WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders); - std::vector<float> get_matrix() const { return m_output_matrix; } - std::vector<float> get_extruders() const { return m_output_extruders; } - - -private: - WipingPanel* m_panel_wiping = nullptr; - std::vector<float> m_output_matrix; - std::vector<float> m_output_extruders; -}; - -#endif // _WIPE_TOWER_DIALOG_H_
\ No newline at end of file diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp deleted file mode 100644 index 13730a497..000000000 --- a/xs/src/slic3r/GUI/wxExtensions.cpp +++ /dev/null @@ -1,1662 +0,0 @@ -#include "wxExtensions.hpp" - -#include "GUI.hpp" -#include "../../libslic3r/Utils.hpp" -#include "BitmapCache.hpp" - -#include <wx/sizer.h> -#include <wx/statline.h> -#include <wx/dcclient.h> -#include <wx/numformatter.h> - -const unsigned int wxCheckListBoxComboPopup::DefaultWidth = 200; -const unsigned int wxCheckListBoxComboPopup::DefaultHeight = 200; -const unsigned int wxCheckListBoxComboPopup::DefaultItemHeight = 18; - -bool wxCheckListBoxComboPopup::Create(wxWindow* parent) -{ - return wxCheckListBox::Create(parent, wxID_HIGHEST + 1, wxPoint(0, 0)); -} - -wxWindow* wxCheckListBoxComboPopup::GetControl() -{ - return this; -} - -void wxCheckListBoxComboPopup::SetStringValue(const wxString& value) -{ - m_text = value; -} - -wxString wxCheckListBoxComboPopup::GetStringValue() const -{ - return m_text; -} - -wxSize wxCheckListBoxComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) -{ - // matches owner wxComboCtrl's width - // and sets height dinamically in dependence of contained items count - - wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) - { - wxSize size = GetComboCtrl()->GetSize(); - - unsigned int count = GetCount(); - if (count > 0) - size.SetHeight(count * DefaultItemHeight); - else - size.SetHeight(DefaultHeight); - - return size; - } - else - return wxSize(DefaultWidth, DefaultHeight); -} - -void wxCheckListBoxComboPopup::OnKeyEvent(wxKeyEvent& evt) -{ - // filters out all the keys which are not working properly - switch (evt.GetKeyCode()) - { - case WXK_LEFT: - case WXK_UP: - case WXK_RIGHT: - case WXK_DOWN: - case WXK_PAGEUP: - case WXK_PAGEDOWN: - case WXK_END: - case WXK_HOME: - case WXK_NUMPAD_LEFT: - case WXK_NUMPAD_UP: - case WXK_NUMPAD_RIGHT: - case WXK_NUMPAD_DOWN: - case WXK_NUMPAD_PAGEUP: - case WXK_NUMPAD_PAGEDOWN: - case WXK_NUMPAD_END: - case WXK_NUMPAD_HOME: - { - break; - } - default: - { - evt.Skip(); - break; - } - } -} - -void wxCheckListBoxComboPopup::OnCheckListBox(wxCommandEvent& evt) -{ - // forwards the checklistbox event to the owner wxComboCtrl - - if (m_check_box_events_status == OnCheckListBoxFunction::FreeToProceed ) - { - wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) { - wxCommandEvent event(wxEVT_CHECKLISTBOX, cmb->GetId()); - event.SetEventObject(cmb); - cmb->ProcessWindowEvent(event); - } - } - - evt.Skip(); - - #ifndef _WIN32 // events are sent differently on OSX+Linux vs Win (more description in header file) - if ( m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed ) - // this happens if the event was resent by OnListBoxSelection - next call to OnListBoxSelection is due to user clicking the text, so the function should - // explicitly change the state on the checkbox - m_check_box_events_status = OnCheckListBoxFunction::WasRefusedLastTime; - else - // if the user clicked the checkbox square, this event was sent before OnListBoxSelection was called, so we don't want it to resend it - m_check_box_events_status = OnCheckListBoxFunction::RefuseToProceed; - #endif -} - -void wxCheckListBoxComboPopup::OnListBoxSelection(wxCommandEvent& evt) -{ - // transforms list box item selection event into checklistbox item toggle event - - int selId = GetSelection(); - if (selId != wxNOT_FOUND) - { - #ifndef _WIN32 - if (m_check_box_events_status == OnCheckListBoxFunction::RefuseToProceed) - #endif - Check((unsigned int)selId, !IsChecked((unsigned int)selId)); - - m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; // so the checkbox reacts to square-click the next time - - SetSelection(wxNOT_FOUND); - wxCommandEvent event(wxEVT_CHECKLISTBOX, GetId()); - event.SetInt(selId); - event.SetEventObject(this); - ProcessEvent(event); - } -} - - -// *** wxDataViewTreeCtrlComboPopup *** - -const unsigned int wxDataViewTreeCtrlComboPopup::DefaultWidth = 270; -const unsigned int wxDataViewTreeCtrlComboPopup::DefaultHeight = 200; -const unsigned int wxDataViewTreeCtrlComboPopup::DefaultItemHeight = 22; - -bool wxDataViewTreeCtrlComboPopup::Create(wxWindow* parent) -{ - return wxDataViewTreeCtrl::Create(parent, wxID_ANY/*HIGHEST + 1*/, wxPoint(0, 0), wxDefaultSize/*wxSize(270, -1)*/, wxDV_NO_HEADER); -} -/* -wxSize wxDataViewTreeCtrlComboPopup::GetAdjustedSize(int minWidth, int prefHeight, int maxHeight) -{ - // matches owner wxComboCtrl's width - // and sets height dinamically in dependence of contained items count - wxComboCtrl* cmb = GetComboCtrl(); - if (cmb != nullptr) - { - wxSize size = GetComboCtrl()->GetSize(); - if (m_cnt_open_items > 0) - size.SetHeight(m_cnt_open_items * DefaultItemHeight); - else - size.SetHeight(DefaultHeight); - - return size; - } - else - return wxSize(DefaultWidth, DefaultHeight); -} -*/ -void wxDataViewTreeCtrlComboPopup::OnKeyEvent(wxKeyEvent& evt) -{ - // filters out all the keys which are not working properly - if (evt.GetKeyCode() == WXK_UP) - { - return; - } - else if (evt.GetKeyCode() == WXK_DOWN) - { - return; - } - else - { - evt.Skip(); - return; - } -} - -void wxDataViewTreeCtrlComboPopup::OnDataViewTreeCtrlSelection(wxCommandEvent& evt) -{ - wxComboCtrl* cmb = GetComboCtrl(); - auto selected = GetItemText(GetSelection()); - cmb->SetText(selected); -} - -// ---------------------------------------------------------------------------- -// *** PrusaCollapsiblePane *** -// ---------------------------------------------------------------------------- -void PrusaCollapsiblePane::OnStateChange(const wxSize& sz) -{ -#ifdef __WXOSX__ - wxCollapsiblePane::OnStateChange(sz); -#else - SetSize(sz); - - if (this->HasFlag(wxCP_NO_TLW_RESIZE)) - { - // the user asked to explicitly handle the resizing itself... - return; - } - - auto top = GetParent(); //right_panel - if (!top) - return; - - wxSizer *sizer = top->GetSizer(); - if (!sizer) - return; - - const wxSize newBestSize = sizer->ComputeFittingClientSize(top); - top->SetMinClientSize(newBestSize); - - wxWindowUpdateLocker noUpdates_p(top->GetParent()); - // we shouldn't attempt to resize a maximized window, whatever happens - // if (!top->IsMaximized()) - // top->SetClientSize(newBestSize); - top->GetParent()->Layout(); - top->Refresh(); -#endif //__WXOSX__ -} - -// ---------------------------------------------------------------------------- -// *** PrusaCollapsiblePaneMSW *** used only #ifdef __WXMSW__ -// ---------------------------------------------------------------------------- -#ifdef __WXMSW__ -bool PrusaCollapsiblePaneMSW::Create(wxWindow *parent, wxWindowID id, const wxString& label, - const wxPoint& pos, const wxSize& size, long style, const wxValidator& val, const wxString& name) -{ - if (!wxControl::Create(parent, id, pos, size, style, val, name)) - return false; - m_pStaticLine = NULL; - m_strLabel = label; - - // sizer containing the expand button and possibly a static line - m_sz = new wxBoxSizer(wxHORIZONTAL); - - m_bmp_close.LoadFile(Slic3r::GUI::from_u8(Slic3r::var("disclosure_triangle_close.png")), wxBITMAP_TYPE_PNG); - m_bmp_open.LoadFile(Slic3r::GUI::from_u8(Slic3r::var("disclosure_triangle_open.png")), wxBITMAP_TYPE_PNG); - - m_pDisclosureTriangleButton = new wxButton(this, wxID_ANY, m_strLabel, wxPoint(0, 0), - wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER); - UpdateBtnBmp(); - m_pDisclosureTriangleButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) - { - if (event.GetEventObject() != m_pDisclosureTriangleButton) - { - event.Skip(); - return; - } - - Collapse(!IsCollapsed()); - - // this change was generated by the user - send the event - wxCollapsiblePaneEvent ev(this, GetId(), IsCollapsed()); - GetEventHandler()->ProcessEvent(ev); - }); - - m_sz->Add(m_pDisclosureTriangleButton, 0, wxLEFT | wxTOP | wxBOTTOM, GetBorder()); - - // do not set sz as our sizers since we handle the pane window without using sizers - m_pPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, - wxTAB_TRAVERSAL | wxNO_BORDER, wxT("wxCollapsiblePanePane")); - - wxColour& clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - m_pDisclosureTriangleButton->SetBackgroundColour(clr); - this->SetBackgroundColour(clr); - m_pPane->SetBackgroundColour(clr); - - // start as collapsed: - m_pPane->Hide(); - - return true; -} - -void PrusaCollapsiblePaneMSW::UpdateBtnBmp() -{ - if (IsCollapsed()) - m_pDisclosureTriangleButton->SetBitmap(m_bmp_close); - else{ - m_pDisclosureTriangleButton->SetBitmap(m_bmp_open); - // To updating button bitmap it's needed to lost focus on this button, so - // we set focus to mainframe - //GetParent()->GetParent()->GetParent()->SetFocus(); - //or to pane - GetPane()->SetFocus(); - } - Layout(); -} - -void PrusaCollapsiblePaneMSW::SetLabel(const wxString &label) -{ - m_strLabel = label; - m_pDisclosureTriangleButton->SetLabel(m_strLabel); - Layout(); -} - -bool PrusaCollapsiblePaneMSW::Layout() -{ - if (!m_pDisclosureTriangleButton || !m_pPane || !m_sz) - return false; // we need to complete the creation first! - - wxSize oursz(GetSize()); - - // move & resize the button and the static line - m_sz->SetDimension(0, 0, oursz.GetWidth(), m_sz->GetMinSize().GetHeight()); - m_sz->Layout(); - - if (IsExpanded()) - { - // move & resize the container window - int yoffset = m_sz->GetSize().GetHeight() + GetBorder(); - m_pPane->SetSize(0, yoffset, - oursz.x, oursz.y - yoffset); - - // this is very important to make the pane window layout show correctly - m_pPane->Layout(); - } - - return true; -} - -void PrusaCollapsiblePaneMSW::Collapse(bool collapse) -{ - // optimization - if (IsCollapsed() == collapse) - return; - - InvalidateBestSize(); - - // update our state - m_pPane->Show(!collapse); - - // update button bitmap - UpdateBtnBmp(); - - OnStateChange(GetBestSize()); -} -#endif //__WXMSW__ - -// ***************************************************************************** -// ---------------------------------------------------------------------------- -// PrusaObjectDataViewModelNode -// ---------------------------------------------------------------------------- - -void PrusaObjectDataViewModelNode::set_object_action_icon() { - m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("add_object.png")), wxBITMAP_TYPE_PNG); -} -void PrusaObjectDataViewModelNode::set_part_action_icon() { - m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG); -} - -Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr; -bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector<std::string>& categories) -{ - if (m_type != "settings" || m_opt_categories == categories) - return false; - - m_opt_categories = categories; - m_name = wxEmptyString; - m_icon = m_empty_icon; - - auto categories_icon = Slic3r::GUI::get_category_icon(); - - for (auto& cat : m_opt_categories) - m_name += cat + "; "; - - wxBitmap *bmp = m_bitmap_cache->find(m_name.ToStdString()); - if (bmp == nullptr) { - std::vector<wxBitmap> bmps; - for (auto& cat : m_opt_categories) - bmps.emplace_back(categories_icon.find(cat) == categories_icon.end() ? - wxNullBitmap : categories_icon.at(cat)); - bmp = m_bitmap_cache->insert(m_name.ToStdString(), bmps); - } - - m_bmp = *bmp; - - return true; -} - -// ***************************************************************************** -// ---------------------------------------------------------------------------- -// PrusaObjectDataViewModel -// ---------------------------------------------------------------------------- - -PrusaObjectDataViewModel::PrusaObjectDataViewModel() -{ - m_bitmap_cache = new Slic3r::GUI::BitmapCache; -} - -PrusaObjectDataViewModel::~PrusaObjectDataViewModel() -{ - for (auto object : m_objects) - delete object; - delete m_bitmap_cache; - m_bitmap_cache = nullptr; -} - -wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name) -{ - auto root = new PrusaObjectDataViewModelNode(name); - m_objects.push_back(root); - // notify control - wxDataViewItem child((void*)root); - wxDataViewItem parent((void*)NULL); - ItemAdded(parent, child); - return child; -} - -wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int instances_count/*, int scale*/) -{ - auto root = new PrusaObjectDataViewModelNode(name, instances_count); - m_objects.push_back(root); - // notify control - wxDataViewItem child((void*)root); - wxDataViewItem parent((void*)NULL); - ItemAdded(parent, child); - return child; -} - -wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item, - const wxString &name, - const wxBitmap& icon, - const int extruder/* = 0*/, - const bool create_frst_child/* = true*/) -{ - PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); - if (!root) return wxDataViewItem(0); - - const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder); - - if (create_frst_child && (root->GetChildren().Count() == 0 || - (root->GetChildren().Count() == 1 && root->GetNthChild(0)->m_type == "settings"))) - { - const auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG); - const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0); - root->Append(node); - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - } - - const auto volume_id = root->GetChildCount() > 0 && root->GetNthChild(0)->m_type == "settings" ? - root->GetChildCount() - 1 : root->GetChildCount(); - - const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id); - root->Append(node); - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - return child; -} - -wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item) -{ - PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID(); - if (!root) return wxDataViewItem(0); - - const auto node = new PrusaObjectDataViewModelNode(root); - root->Insert(node, 0); - // notify control - const wxDataViewItem child((void*)node); - ItemAdded(parent_item, child); - return child; -} - -wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item) -{ - auto ret_item = wxDataViewItem(0); - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return ret_item; - - auto node_parent = node->GetParent(); - wxDataViewItem parent(node_parent); - - // first remove the node from the parent's array of children; - // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ - // thus removing the node from it doesn't result in freeing it - if (node_parent){ - auto id = node_parent->GetChildren().Index(node); - auto v_id = node->GetVolumeId(); - node_parent->GetChildren().Remove(node); - if (id > 0){ - if(id == node_parent->GetChildCount()) id--; - ret_item = wxDataViewItem(node_parent->GetChildren().Item(id)); - } - - //update volume_id value for remaining child-nodes - auto children = node_parent->GetChildren(); - for (size_t i = 0; i < node_parent->GetChildCount() && v_id>=0; i++) - { - auto volume_id = children[i]->GetVolumeId(); - if (volume_id > v_id) - children[i]->SetVolumeId(volume_id-1); - } - } - else - { - auto it = find(m_objects.begin(), m_objects.end(), node); - auto id = it - m_objects.begin(); - if (it != m_objects.end()) - m_objects.erase(it); - if (id > 0){ - if(id == m_objects.size()) id--; - ret_item = wxDataViewItem(m_objects[id]); - } - } - // free the node - delete node; - - // set m_containet to FALSE if parent has no child - if (node_parent) { -#ifndef __WXGTK__ - if (node_parent->GetChildCount() == 0) - node_parent->m_container = false; -#endif //__WXGTK__ - ret_item = parent; - } - - // notify control - ItemDeleted(parent, item); - return ret_item; -} - -void PrusaObjectDataViewModel::DeleteAll() -{ - while (!m_objects.empty()) - { - auto object = m_objects.back(); -// object->RemoveAllChildren(); - Delete(wxDataViewItem(object)); - } -} - -void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent) -{ - PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID(); - if (!root) // happens if item.IsOk()==false - return; - - // first remove the node from the parent's array of children; - // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ - // thus removing the node from it doesn't result in freeing it - auto& children = root->GetChildren(); - for (int id = root->GetChildCount() - 1; id >= 0; --id) - { - auto node = children[id]; - auto item = wxDataViewItem(node); - children.RemoveAt(id); - - // free the node - delete node; - - // notify control - ItemDeleted(parent, item); - } - - // set m_containet to FALSE if parent has no child -#ifndef __WXGTK__ - root->m_container = false; -#endif //__WXGTK__ -} - -wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx) -{ - if (obj_idx >= m_objects.size()) - { - printf("Error! Out of objects range.\n"); - return wxDataViewItem(0); - } - return wxDataViewItem(m_objects[obj_idx]); -} - - -wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) -{ - if (obj_idx >= m_objects.size()) { - printf("Error! Out of objects range.\n"); - return wxDataViewItem(0); - } - - auto parent = m_objects[obj_idx]; - if (parent->GetChildCount() == 0) { - printf("Error! Object has no one volume.\n"); - return wxDataViewItem(0); - } - - for (size_t i = 0; i < parent->GetChildCount(); i++) - if (parent->GetNthChild(i)->m_volume_id == volume_idx) - return wxDataViewItem(parent->GetNthChild(i)); - - return wxDataViewItem(0); -} - -int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item) -{ - wxASSERT(item.IsOk()); - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - auto it = find(m_objects.begin(), m_objects.end(), node); - if (it == m_objects.end()) - return -1; - - return it - m_objects.begin(); -} - -int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item) -{ - wxASSERT(item.IsOk()); - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return -1; - return node->GetVolumeId(); -} - -wxString PrusaObjectDataViewModel::GetName(const wxDataViewItem &item) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return wxEmptyString; - - return node->m_name; -} - -wxString PrusaObjectDataViewModel::GetCopy(const wxDataViewItem &item) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return wxEmptyString; - - return node->m_copy; -} - -wxIcon& PrusaObjectDataViewModel::GetIcon(const wxDataViewItem &item) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->m_icon; -} - -wxBitmap& PrusaObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->m_bmp; -} - -void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const -{ - wxASSERT(item.IsOk()); - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - switch (col) - { - case 0:{ - const PrusaDataViewBitmapText data(node->m_name, node->m_bmp); - variant << data; - break;} - case 1: - variant = node->m_copy; - break; - case 2: - variant = node->m_extruder; - break; - case 3: - variant << node->m_action_icon; - break; - default: - ; - } -} - -bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewItem &item, unsigned int col) -{ - wxASSERT(item.IsOk()); - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->SetValue(variant, col); -} - -bool PrusaObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col) -{ - if (item_idx < 0 || item_idx >= m_objects.size()) - return false; - - return m_objects[item_idx]->SetValue(variant, col); -} - -wxDataViewItem PrusaObjectDataViewModel::MoveChildUp(const wxDataViewItem &item) -{ - auto ret_item = wxDataViewItem(0); - wxASSERT(item.IsOk()); - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return ret_item; - - auto node_parent = node->GetParent(); - if (!node_parent) // If isn't part, but object - return ret_item; - - auto volume_id = node->GetVolumeId(); - if (0 < volume_id && volume_id < node_parent->GetChildCount()){ - node_parent->SwapChildrens(volume_id - 1, volume_id); - ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id - 1)); - ItemChanged(item); - ItemChanged(ret_item); - } - else - ret_item = wxDataViewItem(node_parent->GetNthChild(0)); - return ret_item; -} - -wxDataViewItem PrusaObjectDataViewModel::MoveChildDown(const wxDataViewItem &item) -{ - auto ret_item = wxDataViewItem(0); - wxASSERT(item.IsOk()); - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node) // happens if item.IsOk()==false - return ret_item; - - auto node_parent = node->GetParent(); - if (!node_parent) // If isn't part, but object - return ret_item; - - auto volume_id = node->GetVolumeId(); - if (0 <= volume_id && volume_id+1 < node_parent->GetChildCount()){ - node_parent->SwapChildrens(volume_id + 1, volume_id); - ret_item = wxDataViewItem(node_parent->GetNthChild(volume_id + 1)); - ItemChanged(item); - ItemChanged(ret_item); - } - else - ret_item = wxDataViewItem(node_parent->GetNthChild(node_parent->GetChildCount()-1)); - return ret_item; -} - -wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_id, int new_volume_id, const wxDataViewItem &parent) -{ - auto ret_item = wxDataViewItem(0); - if (current_volume_id == new_volume_id) - return ret_item; - wxASSERT(parent.IsOk()); - PrusaObjectDataViewModelNode *node_parent = (PrusaObjectDataViewModelNode*)parent.GetID(); - if (!node_parent) // happens if item.IsOk()==false - return ret_item; - - const size_t shift = node_parent->GetChildren().Item(0)->m_type == "settings" ? 1 : 0; - - PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift); - node_parent->GetChildren().Remove(deleted_node); - ItemDeleted(parent, wxDataViewItem(deleted_node)); - node_parent->Insert(deleted_node, new_volume_id+shift); - ItemAdded(parent, wxDataViewItem(deleted_node)); - const auto settings_item = HasSettings(wxDataViewItem(deleted_node)); - if (settings_item) - ItemAdded(wxDataViewItem(deleted_node), settings_item); - - //update volume_id value for child-nodes - auto children = node_parent->GetChildren(); - int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id; - int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id; - for (int id = id_frst; id <= id_last; ++id) - children[id+shift]->SetVolumeId(id); - - return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift)); -} - -bool PrusaObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const -{ - wxASSERT(item.IsOk()); - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - - // disable extruder selection for the "Settings" item - return !(col == 2 && node->m_extruder.IsEmpty()); -} - -wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const -{ - // the invisible root node has no parent - if (!item.IsOk()) - return wxDataViewItem(0); - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - - // objects nodes has no parent too - if (find(m_objects.begin(), m_objects.end(),node) != m_objects.end()) - return wxDataViewItem(0); - - return wxDataViewItem((void*)node->GetParent()); -} - -bool PrusaObjectDataViewModel::IsContainer(const wxDataViewItem &item) const -{ - // the invisible root node can have children - if (!item.IsOk()) - return true; - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->IsContainer(); -} - -unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent, wxDataViewItemArray &array) const -{ - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent.GetID(); - if (!node) - { - for (auto object : m_objects) - array.Add(wxDataViewItem((void*)object)); - return m_objects.size(); - } - - if (node->GetChildCount() == 0) - { - return 0; - } - - unsigned int count = node->GetChildren().GetCount(); - for (unsigned int pos = 0; pos < count; pos++) - { - PrusaObjectDataViewModelNode *child = node->GetChildren().Item(pos); - array.Add(wxDataViewItem((void*)child)); - } - - return count; -} - -wxDataViewItem PrusaObjectDataViewModel::HasSettings(const wxDataViewItem &item) const -{ - if (!item.IsOk()) - return wxDataViewItem(0); - - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (node->GetChildCount() == 0) - return wxDataViewItem(0); - - auto& children = node->GetChildren(); - if (children[0]->m_type == "settings") - return wxDataViewItem((void*)children[0]);; - - return wxDataViewItem(0); -} - -bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const -{ - if (!item.IsOk()) - return false; - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - return node->m_type == "settings"; -} - - - -void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item, - const std::vector<std::string>& categories) -{ - if (!item.IsOk()) return; - PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID(); - if (!node->update_settings_digest(categories)) - return; - ItemChanged(item); -} - -IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText) -// --------------------------------------------------------- -// PrusaIconTextRenderer -// --------------------------------------------------------- - -bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value) -{ - m_value << value; - return true; -} - -bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const -{ - return false; -} - -bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state) -{ - int xoffset = 0; - - const wxBitmap& icon = m_value.GetBitmap(); - if (icon.IsOk()) - { - dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2); - xoffset = icon.GetWidth() + 4; - } - - RenderText(m_value.GetText(), xoffset, rect, dc, state); - - return true; -} - -wxSize PrusaBitmapTextRenderer::GetSize() const -{ - if (!m_value.GetText().empty()) - { - wxSize size = GetTextExtent(m_value.GetText()); - - if (m_value.GetBitmap().IsOk()) - size.x += m_value.GetBitmap().GetWidth() + 4; - return size; - } - return wxSize(80, 20); -} - - -// ---------------------------------------------------------------------------- -// PrusaDoubleSlider -// ---------------------------------------------------------------------------- - -PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos, - const wxSize& size, - long style, - const wxValidator& val, - const wxString& name) : - wxControl(parent, id, pos, size, wxWANTS_CHARS | wxBORDER_NONE), - m_lower_value(lowerValue), m_higher_value (higherValue), - m_min_value(minValue), m_max_value(maxValue), - m_style(style == wxSL_HORIZONTAL || style == wxSL_VERTICAL ? style: wxSL_HORIZONTAL) -{ -#ifndef __WXOSX__ // SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX - SetDoubleBuffered(true); -#endif //__WXOSX__ - - m_bmp_thumb_higher = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("right_half_circle.png")) : - Slic3r::GUI::from_u8(Slic3r::var("up_half_circle.png")), wxBITMAP_TYPE_PNG); - m_bmp_thumb_lower = wxBitmap(style == wxSL_HORIZONTAL ? Slic3r::GUI::from_u8(Slic3r::var("left_half_circle.png")) : - Slic3r::GUI::from_u8(Slic3r::var("down_half_circle.png")), wxBITMAP_TYPE_PNG); - m_thumb_size = m_bmp_thumb_lower.GetSize(); - - m_bmp_add_tick_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_on.png")), wxBITMAP_TYPE_PNG); - m_bmp_add_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_add_off.png")), wxBITMAP_TYPE_PNG); - m_bmp_del_tick_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG); - m_bmp_del_tick_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("colorchange_delete_off.png")), wxBITMAP_TYPE_PNG); - m_tick_icon_dim = m_bmp_add_tick_on.GetSize().x; - - m_bmp_one_layer_lock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG); - m_bmp_one_layer_lock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG); - m_bmp_one_layer_unlock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG); - m_bmp_one_layer_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG); - m_lock_icon_dim = m_bmp_one_layer_lock_on.GetSize().x; - - m_selection = ssUndef; - - // slider events - Bind(wxEVT_PAINT, &PrusaDoubleSlider::OnPaint, this); - Bind(wxEVT_LEFT_DOWN, &PrusaDoubleSlider::OnLeftDown, this); - Bind(wxEVT_MOTION, &PrusaDoubleSlider::OnMotion, this); - Bind(wxEVT_LEFT_UP, &PrusaDoubleSlider::OnLeftUp, this); - Bind(wxEVT_MOUSEWHEEL, &PrusaDoubleSlider::OnWheel, this); - Bind(wxEVT_ENTER_WINDOW,&PrusaDoubleSlider::OnEnterWin, this); - Bind(wxEVT_LEAVE_WINDOW,&PrusaDoubleSlider::OnLeaveWin, this); - Bind(wxEVT_KEY_DOWN, &PrusaDoubleSlider::OnKeyDown, this); - Bind(wxEVT_KEY_UP, &PrusaDoubleSlider::OnKeyUp, this); - Bind(wxEVT_RIGHT_DOWN, &PrusaDoubleSlider::OnRightDown,this); - Bind(wxEVT_RIGHT_UP, &PrusaDoubleSlider::OnRightUp, this); - - // control's view variables - SLIDER_MARGIN = 4 + (style == wxSL_HORIZONTAL ? m_bmp_thumb_higher.GetWidth() : m_bmp_thumb_higher.GetHeight()); - - DARK_ORANGE_PEN = wxPen(wxColour(253, 84, 2)); - ORANGE_PEN = wxPen(wxColour(253, 126, 66)); - LIGHT_ORANGE_PEN = wxPen(wxColour(254, 177, 139)); - - DARK_GREY_PEN = wxPen(wxColour(128, 128, 128)); - GREY_PEN = wxPen(wxColour(164, 164, 164)); - LIGHT_GREY_PEN = wxPen(wxColour(204, 204, 204)); - - line_pens = { &DARK_GREY_PEN, &GREY_PEN, &LIGHT_GREY_PEN }; - segm_pens = { &DARK_ORANGE_PEN, &ORANGE_PEN, &LIGHT_ORANGE_PEN }; -} - -int PrusaDoubleSlider::GetActiveValue() const -{ - return m_selection == ssLower ? - m_lower_value : m_selection == ssHigher ? - m_higher_value : -1; -} - -wxSize PrusaDoubleSlider::DoGetBestSize() const -{ - const wxSize size = wxControl::DoGetBestSize(); - if (size.x > 1 && size.y > 1) - return size; - const int new_size = is_horizontal() ? 80 : 120; - return wxSize(new_size, new_size); -} - -void PrusaDoubleSlider::SetLowerValue(const int lower_val) -{ - m_selection = ssLower; - m_lower_value = lower_val; - correct_lower_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void PrusaDoubleSlider::SetHigherValue(const int higher_val) -{ - m_selection = ssHigher; - m_higher_value = higher_val; - correct_higher_value(); - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void PrusaDoubleSlider::SetMaxValue(const int max_value) -{ - m_max_value = max_value; - Refresh(); - Update(); -} - -void PrusaDoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos) -{ - int width; - int height; - get_size(&width, &height); - - wxCoord line_beg_x = is_horizontal() ? SLIDER_MARGIN : width*0.5 - 1; - wxCoord line_beg_y = is_horizontal() ? height*0.5 - 1 : SLIDER_MARGIN; - wxCoord line_end_x = is_horizontal() ? width - SLIDER_MARGIN + 1 : width*0.5 - 1; - wxCoord line_end_y = is_horizontal() ? height*0.5 - 1 : height - SLIDER_MARGIN + 1; - - wxCoord segm_beg_x = is_horizontal() ? lower_pos : width*0.5 - 1; - wxCoord segm_beg_y = is_horizontal() ? height*0.5 - 1 : lower_pos-1; - wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; - wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; - - for (int id = 0; id < line_pens.size(); id++) - { - dc.SetPen(*line_pens[id]); - dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); - dc.SetPen(*segm_pens[id]); - dc.DrawLine(segm_beg_x, segm_beg_y, segm_end_x, segm_end_y); - if (is_horizontal()) - line_beg_y = line_end_y = segm_beg_y = segm_end_y += 1; - else - line_beg_x = line_end_x = segm_beg_x = segm_end_x += 1; - } -} - -double PrusaDoubleSlider::get_scroll_step() -{ - const wxSize sz = get_size(); - const int& slider_len = m_style == wxSL_HORIZONTAL ? sz.x : sz.y; - return double(slider_len - SLIDER_MARGIN * 2) / (m_max_value - m_min_value); -} - -// get position on the slider line from entered value -wxCoord PrusaDoubleSlider::get_position_from_value(const int value) -{ - const double step = get_scroll_step(); - const int val = is_horizontal() ? value : m_max_value - value; - return wxCoord(SLIDER_MARGIN + int(val*step + 0.5)); -} - -wxSize PrusaDoubleSlider::get_size() -{ - int w, h; - get_size(&w, &h); - return wxSize(w, h); -} - -void PrusaDoubleSlider::get_size(int *w, int *h) -{ - GetSize(w, h); - is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim; -} - -double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) const -{ - if (m_values.empty()) - return 0.0; - return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; -} - -void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos) -{ - const double step = get_scroll_step(); - if (is_horizontal()) { - lower_pos = SLIDER_MARGIN + int(m_lower_value*step + 0.5); - higher_pos = SLIDER_MARGIN + int(m_higher_value*step + 0.5); - } - else { - lower_pos = SLIDER_MARGIN + int((m_max_value - m_lower_value)*step + 0.5); - higher_pos = SLIDER_MARGIN + int((m_max_value - m_higher_value)*step + 0.5); - } -} - -void PrusaDoubleSlider::draw_focus_rect() -{ - if (!m_is_focused) - return; - const wxSize sz = GetSize(); - wxPaintDC dc(this); - const wxPen pen = wxPen(wxColour(128, 128, 10), 1, wxPENSTYLE_DOT); - dc.SetPen(pen); - dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT)); - dc.DrawRectangle(1, 1, sz.x - 2, sz.y - 2); -} - -void PrusaDoubleSlider::render() -{ - SetBackgroundColour(GetParent()->GetBackgroundColour()); - draw_focus_rect(); - - wxPaintDC dc(this); - wxFont font = dc.GetFont(); - const wxFont smaller_font = font.Smaller(); - dc.SetFont(smaller_font); - - const wxCoord lower_pos = get_position_from_value(m_lower_value); - const wxCoord higher_pos = get_position_from_value(m_higher_value); - - // draw line - draw_scroll_line(dc, lower_pos, higher_pos); - -// //lower slider: -// draw_thumb(dc, lower_pos, ssLower); -// //higher slider: -// draw_thumb(dc, higher_pos, ssHigher); - - // draw both sliders - draw_thumbs(dc, lower_pos, higher_pos); - - //draw color print ticks - draw_ticks(dc); - - //draw color print ticks - draw_one_layer_icon(dc); -} - -void PrusaDoubleSlider::draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end) -{ - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - wxBitmap* icon = m_is_action_icon_focesed ? &m_bmp_add_tick_off : &m_bmp_add_tick_on; - if (m_ticks.find(tick) != m_ticks.end()) - icon = m_is_action_icon_focesed ? &m_bmp_del_tick_off : &m_bmp_del_tick_on; - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = pt_beg.x - 0.5*m_tick_icon_dim : y_draw = pt_beg.y - 0.5*m_tick_icon_dim; - if (m_selection == ssLower) - is_horizontal() ? y_draw = pt_end.y + 3 : x_draw = pt_beg.x - m_tick_icon_dim-2; - else - is_horizontal() ? y_draw = pt_beg.y - m_tick_icon_dim-2 : x_draw = pt_end.x + 3; - - dc.DrawBitmap(*icon, x_draw, y_draw); - - //update rect of the tick action icon - m_rect_tick_action = wxRect(x_draw, y_draw, m_tick_icon_dim, m_tick_icon_dim); -} - -void PrusaDoubleSlider::draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, const SelectedSlider selection) -{ - if (m_selection == selection) { - //draw info line - dc.SetPen(DARK_ORANGE_PEN); - const wxPoint pt_beg = is_horizontal() ? wxPoint(pos.x, pos.y - m_thumb_size.y) : wxPoint(pos.x - m_thumb_size.x, pos.y - 1); - const wxPoint pt_end = is_horizontal() ? wxPoint(pos.x, pos.y + m_thumb_size.y) : wxPoint(pos.x + m_thumb_size.x, pos.y - 1); - dc.DrawLine(pt_beg, pt_end); - - //draw action icon - draw_action_icon(dc, pt_beg, pt_end); - } -} - -wxString PrusaDoubleSlider::get_label(const SelectedSlider& selection) const -{ - const int value = selection == ssLower ? m_lower_value : m_higher_value; - - if (m_label_koef == 1.0 && m_values.empty()) - return wxString::Format("%d", value); - - const wxString str = m_values.empty() ? - wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : - wxNumberFormatter::ToString(m_values[value].second, 2, wxNumberFormatter::Style_None); - return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : m_values[value].first); -} - -void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const -{ - if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection) - return; - wxCoord text_width, text_height; - const wxString label = get_label(selection); - dc.GetMultiLineTextExtent(label, &text_width, &text_height); - wxPoint text_pos; - if (selection ==ssLower) - text_pos = is_horizontal() ? wxPoint(pos.x + 1, pos.y + m_thumb_size.x) : - wxPoint(pos.x + m_thumb_size.x+1, pos.y - 0.5*text_height - 1); - else - text_pos = is_horizontal() ? wxPoint(pos.x - text_width - 1, pos.y - m_thumb_size.x - text_height) : - wxPoint(pos.x - text_width - 1 - m_thumb_size.x, pos.y - 0.5*text_height + 1); - dc.DrawText(label, text_pos); -} - -void PrusaDoubleSlider::draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) -{ - wxCoord x_draw, y_draw; - if (selection == ssLower) { - if (is_horizontal()) { - x_draw = pos.x - m_thumb_size.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y; - } - } - else{ - if (is_horizontal()) { - x_draw = pos.x; - y_draw = pos.y - int(0.5*m_thumb_size.y); - } - else { - x_draw = pos.x - int(0.5*m_thumb_size.x); - y_draw = pos.y - m_thumb_size.y; - } - } - dc.DrawBitmap(selection == ssLower ? m_bmp_thumb_lower : m_bmp_thumb_higher, x_draw, y_draw); - - // Update thumb rect - update_thumb_rect(x_draw, y_draw, selection); -} - -void PrusaDoubleSlider::draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos = is_horizontal() ? wxPoint(pos_coord, height*0.5) : wxPoint(0.5*width, pos_coord); - - // Draw thumb - draw_thumb_item(dc, pos, selection); - - // Draw info_line - draw_info_line_with_icon(dc, pos, selection); - - // Draw thumb text - draw_thumb_text(dc, pos, selection); -} - -void PrusaDoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos) -{ - //calculate thumb position on slider line - int width, height; - get_size(&width, &height); - const wxPoint pos_l = is_horizontal() ? wxPoint(lower_pos, height*0.5) : wxPoint(0.5*width, lower_pos); - const wxPoint pos_h = is_horizontal() ? wxPoint(higher_pos, height*0.5) : wxPoint(0.5*width, higher_pos); - - // Draw lower thumb - draw_thumb_item(dc, pos_l, ssLower); - // Draw lower info_line - draw_info_line_with_icon(dc, pos_l, ssLower); - - // Draw higher thumb - draw_thumb_item(dc, pos_h, ssHigher); - // Draw higher info_line - draw_info_line_with_icon(dc, pos_h, ssHigher); - // Draw higher thumb text - draw_thumb_text(dc, pos_h, ssHigher); - - // Draw lower thumb text - draw_thumb_text(dc, pos_l, ssLower); -} - -void PrusaDoubleSlider::draw_ticks(wxDC& dc) -{ - dc.SetPen(DARK_GREY_PEN); - int height, width; - get_size(&width, &height); - const wxCoord mid = is_horizontal() ? 0.5*height : 0.5*width; - for (auto tick : m_ticks) - { - const wxCoord pos = get_position_from_value(tick); - - is_horizontal() ? dc.DrawLine(pos, mid-14, pos, mid-9) : - dc.DrawLine(mid - 14, pos - 1, mid - 9, pos - 1); - is_horizontal() ? dc.DrawLine(pos, mid+14, pos, mid+9) : - dc.DrawLine(mid + 14, pos - 1, mid + 9, pos - 1); - } -} - -void PrusaDoubleSlider::draw_one_layer_icon(wxDC& dc) -{ - wxBitmap* icon = m_is_one_layer ? - m_is_one_layer_icon_focesed ? &m_bmp_one_layer_lock_off : &m_bmp_one_layer_lock_on : - m_is_one_layer_icon_focesed ? &m_bmp_one_layer_unlock_off : &m_bmp_one_layer_unlock_on; - - int width, height; - get_size(&width, &height); - - wxCoord x_draw, y_draw; - is_horizontal() ? x_draw = width-2 : x_draw = 0.5*width - 0.5*m_lock_icon_dim; - is_horizontal() ? y_draw = 0.5*height - 0.5*m_lock_icon_dim : y_draw = height-2; - - dc.DrawBitmap(*icon, x_draw, y_draw); - - //update rect of the lock/unlock icon - m_rect_one_layer_icon = wxRect(x_draw, y_draw, m_lock_icon_dim, m_lock_icon_dim); -} - -void PrusaDoubleSlider::update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection) -{ - const wxRect& rect = wxRect(begin_x, begin_y, m_thumb_size.x, m_thumb_size.y); - if (selection == ssLower) - m_rect_lower_thumb = rect; - else - m_rect_higher_thumb = rect; -} - -int PrusaDoubleSlider::get_value_from_position(const wxCoord x, const wxCoord y) -{ - const int height = get_size().y; - const double step = get_scroll_step(); - - if (is_horizontal()) - return int(double(x - SLIDER_MARGIN) / step + 0.5); - else - return int(m_min_value + double(height - SLIDER_MARGIN - y) / step + 0.5); -} - -void PrusaDoubleSlider::detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel /*= false*/) -{ - if (is_mouse_wheel) - { - if (is_horizontal()) { - m_selection = pt.x <= m_rect_lower_thumb.GetRight() ? ssLower : - pt.x >= m_rect_higher_thumb.GetLeft() ? ssHigher : ssUndef; - } - else { - m_selection = pt.y >= m_rect_lower_thumb.GetTop() ? ssLower : - pt.y <= m_rect_higher_thumb.GetBottom() ? ssHigher : ssUndef; - } - return; - } - - m_selection = is_point_in_rect(pt, m_rect_lower_thumb) ? ssLower : - is_point_in_rect(pt, m_rect_higher_thumb) ? ssHigher : ssUndef; -} - -bool PrusaDoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect) -{ - if (rect.GetLeft() <= pt.x && pt.x <= rect.GetRight() && - rect.GetTop() <= pt.y && pt.y <= rect.GetBottom()) - return true; - return false; -} - -void PrusaDoubleSlider::ChangeOneLayerLock() -{ - m_is_one_layer = !m_is_one_layer; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event) -{ - this->CaptureMouse(); - wxClientDC dc(this); - wxPoint pos = event.GetLogicalPosition(dc); - if (is_point_in_rect(pos, m_rect_tick_action)) { - action_tick(taOnIcon); - return; - } - - m_is_left_down = true; - if (is_point_in_rect(pos, m_rect_one_layer_icon)){ - m_is_one_layer = !m_is_one_layer; - m_selection == ssLower ? correct_lower_value() : correct_higher_value(); - if (!m_selection) m_selection = ssHigher; - } - else - detect_selected_slider(pos); - - Refresh(); - Update(); - event.Skip(); -} - -void PrusaDoubleSlider::correct_lower_value() -{ - if (m_lower_value < m_min_value) - m_lower_value = m_min_value; - else if (m_lower_value > m_max_value) - m_lower_value = m_max_value; - - if (m_lower_value >= m_higher_value && m_lower_value <= m_max_value || m_is_one_layer) - m_higher_value = m_lower_value; -} - -void PrusaDoubleSlider::correct_higher_value() -{ - if (m_higher_value > m_max_value) - m_higher_value = m_max_value; - else if (m_higher_value < m_min_value) - m_higher_value = m_min_value; - - if (m_higher_value <= m_lower_value && m_higher_value >= m_min_value || m_is_one_layer) - m_lower_value = m_higher_value; -} - -void PrusaDoubleSlider::OnMotion(wxMouseEvent& event) -{ - const wxClientDC dc(this); - const wxPoint pos = event.GetLogicalPosition(dc); - m_is_one_layer_icon_focesed = is_point_in_rect(pos, m_rect_one_layer_icon); - if (!m_is_left_down && !m_is_one_layer){ - m_is_action_icon_focesed = is_point_in_rect(pos, m_rect_tick_action); - } - else if (m_is_left_down || m_is_right_down){ - if (m_selection == ssLower) { - m_lower_value = get_value_from_position(pos.x, pos.y); - correct_lower_value(); - } - else if (m_selection == ssHigher) { - m_higher_value = get_value_from_position(pos.x, pos.y); - correct_higher_value(); - } - } - Refresh(); - Update(); - event.Skip(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event) -{ - this->ReleaseMouse(); - m_is_left_down = false; - Refresh(); - Update(); - event.Skip(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter) -{ - m_is_focused = enter; - Refresh(); - Update(); - event.Skip(); -} - -// "condition" have to be true for: -// - value increase (if wxSL_VERTICAL) -// - value decrease (if wxSL_HORIZONTAL) -void PrusaDoubleSlider::move_current_thumb(const bool condition) -{ - m_is_one_layer = wxGetKeyState(WXK_CONTROL); - int delta = condition ? -1 : 1; - if (is_horizontal()) - delta *= -1; - - if (m_selection == ssLower) { - m_lower_value -= delta; - correct_lower_value(); - } - else if (m_selection == ssHigher) { - m_higher_value -= delta; - correct_higher_value(); - } - Refresh(); - Update(); - - wxCommandEvent e(wxEVT_SCROLL_CHANGED); - e.SetEventObject(this); - ProcessWindowEvent(e); -} - -void PrusaDoubleSlider::action_tick(const TicksAction action) -{ - if (m_selection == ssUndef) - return; - - const int tick = m_selection == ssLower ? m_lower_value : m_higher_value; - - if (action == taOnIcon && !m_ticks.insert(tick).second) - m_ticks.erase(tick); - else { - const auto it = m_ticks.find(tick); - if (it == m_ticks.end() && action == taAdd) - m_ticks.insert(tick); - else if (it != m_ticks.end() && action == taDel) - m_ticks.erase(tick); - else - return; - } - - Refresh(); - Update(); -} - -void PrusaDoubleSlider::OnWheel(wxMouseEvent& event) -{ - wxClientDC dc(this); - wxPoint pos = event.GetLogicalPosition(dc); - detect_selected_slider(pos, true); - - if (m_selection == ssUndef) - return; - - move_current_thumb(event.GetWheelRotation() > 0); -} - -void PrusaDoubleSlider::OnKeyDown(wxKeyEvent &event) -{ - const int key = event.GetKeyCode(); - if (key == '+' || key == WXK_NUMPAD_ADD) - action_tick(taAdd); - else if (key == '-' || key == 390 || key == WXK_DELETE || key == WXK_BACK) - action_tick(taDel); - else if (is_horizontal()) - { - if (key == WXK_LEFT || key == WXK_RIGHT) - move_current_thumb(key == WXK_LEFT); - else if (key == WXK_UP || key == WXK_DOWN){ - m_selection = key == WXK_UP ? ssHigher : ssLower; - Refresh(); - } - } - else { - if (key == WXK_LEFT || key == WXK_RIGHT) { - m_selection = key == WXK_LEFT ? ssHigher : ssLower; - Refresh(); - } - else if (key == WXK_UP || key == WXK_DOWN) - move_current_thumb(key == WXK_UP); - } -} - -void PrusaDoubleSlider::OnKeyUp(wxKeyEvent &event) -{ - if (event.GetKeyCode() == WXK_CONTROL) - m_is_one_layer = false; - Refresh(); - Update(); - event.Skip(); -} - -void PrusaDoubleSlider::OnRightDown(wxMouseEvent& event) -{ - this->CaptureMouse(); - const wxClientDC dc(this); - detect_selected_slider(event.GetLogicalPosition(dc)); - if (!m_selection) - return; - - if (m_selection == ssLower) - m_higher_value = m_lower_value; - else - m_lower_value = m_higher_value; - - m_is_right_down = m_is_one_layer = true; - - Refresh(); - Update(); - event.Skip(); -} - -void PrusaDoubleSlider::OnRightUp(wxMouseEvent& event) -{ - this->ReleaseMouse(); - m_is_right_down = m_is_one_layer = false; - - Refresh(); - Update(); - event.Skip(); -} - - -// ---------------------------------------------------------------------------- -// PrusaLockButton -// ---------------------------------------------------------------------------- - -PrusaLockButton::PrusaLockButton( wxWindow *parent, - wxWindowID id, - const wxPoint& pos /*= wxDefaultPosition*/, - const wxSize& size /*= wxDefaultSize*/): - wxButton(parent, id, wxEmptyString, pos, size, wxBU_EXACTFIT | wxNO_BORDER) -{ - m_bmp_lock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_on.png")), wxBITMAP_TYPE_PNG); - m_bmp_lock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_lock_off.png")), wxBITMAP_TYPE_PNG); - m_bmp_unlock_on = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_on.png")), wxBITMAP_TYPE_PNG); - m_bmp_unlock_off = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("one_layer_unlock_off.png")), wxBITMAP_TYPE_PNG); - m_lock_icon_dim = m_bmp_lock_on.GetSize().x; - -#ifdef __WXMSW__ - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); -#endif // __WXMSW__ - SetBitmap(m_bmp_unlock_on); - - //button events - Bind(wxEVT_BUTTON, &PrusaLockButton::OnButton, this); - Bind(wxEVT_ENTER_WINDOW, &PrusaLockButton::OnEnterBtn, this); - Bind(wxEVT_LEAVE_WINDOW, &PrusaLockButton::OnLeaveBtn, this); -} - -void PrusaLockButton::OnButton(wxCommandEvent& event) -{ - m_is_pushed = !m_is_pushed; - enter_button(true); - - event.Skip(); -} - -void PrusaLockButton::enter_button(const bool enter) -{ - wxBitmap* icon = m_is_pushed ? - enter ? &m_bmp_lock_off : &m_bmp_lock_on : - enter ? &m_bmp_unlock_off : &m_bmp_unlock_on; - SetBitmap(*icon); - - Refresh(); - Update(); -} - -// ************************************** EXPERIMENTS *************************************** - -// ***************************************************************************** - - - diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp deleted file mode 100644 index 51c02035c..000000000 --- a/xs/src/slic3r/GUI/wxExtensions.hpp +++ /dev/null @@ -1,773 +0,0 @@ -#ifndef slic3r_GUI_wxExtensions_hpp_ -#define slic3r_GUI_wxExtensions_hpp_ - -#include <wx/checklst.h> -#include <wx/combo.h> -#include <wx/dataview.h> -#include <wx/dc.h> -#include <wx/collpane.h> -#include <wx/wupdlock.h> -#include <wx/button.h> -#include <wx/slider.h> - -#include <vector> -#include <set> - -class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup -{ - static const unsigned int DefaultWidth; - static const unsigned int DefaultHeight; - static const unsigned int DefaultItemHeight; - - wxString m_text; - - // Events sent on mouseclick are quite complex. Function OnListBoxSelection is supposed to pass the event to the checkbox, which works fine on - // Win. On OSX and Linux the events are generated differently - clicking on the checkbox square generates the event twice (and the square - // therefore seems not to respond). - // This enum is meant to save current state of affairs, i.e., if the event forwarding is ok to do or not. It is only used on Linux - // and OSX by some #ifdefs. It also stores information whether OnListBoxSelection is supposed to change the checkbox status, - // or if it changed status on its own already (which happens when the square is clicked). More comments in OnCheckListBox(...) - // There indeed is a better solution, maybe making a custom event used for the event passing to distinguish the original and passed message - // and blocking one of them on OSX and Linux. Feel free to refactor, but carefully test on all platforms. - enum class OnCheckListBoxFunction{ - FreeToProceed, - RefuseToProceed, - WasRefusedLastTime - } m_check_box_events_status = OnCheckListBoxFunction::FreeToProceed; - - -public: - virtual bool Create(wxWindow* parent); - virtual wxWindow* GetControl(); - virtual void SetStringValue(const wxString& value); - virtual wxString GetStringValue() const; - virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); - - virtual void OnKeyEvent(wxKeyEvent& evt); - - void OnCheckListBox(wxCommandEvent& evt); - void OnListBoxSelection(wxCommandEvent& evt); -}; - - -// *** wxDataViewTreeCtrlComboBox *** - -class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup -{ - static const unsigned int DefaultWidth; - static const unsigned int DefaultHeight; - static const unsigned int DefaultItemHeight; - - wxString m_text; - int m_cnt_open_items{0}; - -public: - virtual bool Create(wxWindow* parent); - virtual wxWindow* GetControl() { return this; } - virtual void SetStringValue(const wxString& value) { m_text = value; } - virtual wxString GetStringValue() const { return m_text; } -// virtual wxSize GetAdjustedSize(int minWidth, int prefHeight, int maxHeight); - - virtual void OnKeyEvent(wxKeyEvent& evt); - void OnDataViewTreeCtrlSelection(wxCommandEvent& evt); - void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; } -}; - - - -// *** PrusaCollapsiblePane *** -// ---------------------------------------------------------------------------- -class PrusaCollapsiblePane : public wxCollapsiblePane -{ -public: - PrusaCollapsiblePane() {} - PrusaCollapsiblePane(wxWindow *parent, - wxWindowID winid, - const wxString& label, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxCP_DEFAULT_STYLE, - const wxValidator& val = wxDefaultValidator, - const wxString& name = wxCollapsiblePaneNameStr) - { - Create(parent, winid, label, pos, size, style, val, name); - } - ~PrusaCollapsiblePane() {} - - void OnStateChange(const wxSize& sz); //override/hide of OnStateChange from wxCollapsiblePane - virtual bool Show(bool show = true) override { - wxCollapsiblePane::Show(show); - OnStateChange(GetBestSize()); - return true; - } -}; - - -// *** PrusaCollapsiblePaneMSW *** used only #ifdef __WXMSW__ -// ---------------------------------------------------------------------------- -#ifdef __WXMSW__ -class PrusaCollapsiblePaneMSW : public PrusaCollapsiblePane//wxCollapsiblePane -{ - wxButton* m_pDisclosureTriangleButton = nullptr; - wxBitmap m_bmp_close; - wxBitmap m_bmp_open; -public: - PrusaCollapsiblePaneMSW() {} - PrusaCollapsiblePaneMSW( wxWindow *parent, - wxWindowID winid, - const wxString& label, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxCP_DEFAULT_STYLE, - const wxValidator& val = wxDefaultValidator, - const wxString& name = wxCollapsiblePaneNameStr) - { - Create(parent, winid, label, pos, size, style, val, name); - } - - ~PrusaCollapsiblePaneMSW() {} - - bool Create(wxWindow *parent, - wxWindowID id, - const wxString& label, - const wxPoint& pos, - const wxSize& size, - long style, - const wxValidator& val, - const wxString& name); - - void UpdateBtnBmp(); - void SetLabel(const wxString &label) override; - bool Layout() override; - void Collapse(bool collapse) override; -}; -#endif //__WXMSW__ - -// ***************************************************************************** - -// ---------------------------------------------------------------------------- -// PrusaDataViewBitmapText: helper class used by PrusaBitmapTextRenderer -// ---------------------------------------------------------------------------- - -class PrusaDataViewBitmapText : public wxObject -{ -public: - PrusaDataViewBitmapText(const wxString &text = wxEmptyString, - const wxBitmap& bmp = wxNullBitmap) : - m_text(text), m_bmp(bmp) - { } - - PrusaDataViewBitmapText(const PrusaDataViewBitmapText &other) - : wxObject(), - m_text(other.m_text), - m_bmp(other.m_bmp) - { } - - void SetText(const wxString &text) { m_text = text; } - wxString GetText() const { return m_text; } - void SetBitmap(const wxIcon &icon) { m_bmp = icon; } - const wxBitmap &GetBitmap() const { return m_bmp; } - - bool IsSameAs(const PrusaDataViewBitmapText& other) const { - return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp); - } - - bool operator==(const PrusaDataViewBitmapText& other) const { - return IsSameAs(other); - } - - bool operator!=(const PrusaDataViewBitmapText& other) const { - return !IsSameAs(other); - } - -private: - wxString m_text; - wxBitmap m_bmp; -}; -DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText) - - -// ---------------------------------------------------------------------------- -// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel -// ---------------------------------------------------------------------------- - -class PrusaObjectDataViewModelNode; -WX_DEFINE_ARRAY_PTR(PrusaObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray); - -class PrusaObjectDataViewModelNode -{ - PrusaObjectDataViewModelNode* m_parent; - MyObjectTreeModelNodePtrArray m_children; - wxIcon m_empty_icon; - wxBitmap m_empty_bmp; - std::vector< std::string > m_opt_categories; -public: - PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) { - m_parent = NULL; - m_name = name; - m_copy = wxString::Format("%d", instances_count); - m_type = "object"; - m_volume_id = -1; -#ifdef __WXGTK__ - // it's necessary on GTK because of control have to know if this item will be container - // in another case you couldn't to add subitem for this item - // it will be produce "segmentation fault" - m_container = true; -#endif //__WXGTK__ - set_object_action_icon(); - } - - PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent, - const wxString& sub_obj_name, - const wxBitmap& bmp, - const wxString& extruder, - const int volume_id=-1) { - m_parent = parent; - m_name = sub_obj_name; - m_copy = wxEmptyString; - m_bmp = bmp; - m_type = "volume"; - m_volume_id = volume_id; - m_extruder = extruder; -#ifdef __WXGTK__ - // it's necessary on GTK because of control have to know if this item will be container - // in another case you couldn't to add subitem for this item - // it will be produce "segmentation fault" - m_container = true; -#endif //__WXGTK__ - set_part_action_icon(); - } - - PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent) : - m_parent(parent), - m_name("Settings to modified"), - m_copy(wxEmptyString), - m_type("settings"), - m_extruder(wxEmptyString) {} - - ~PrusaObjectDataViewModelNode() - { - // free all our children nodes - size_t count = m_children.GetCount(); - for (size_t i = 0; i < count; i++) - { - PrusaObjectDataViewModelNode *child = m_children[i]; - delete child; - } - } - - wxString m_name; - wxIcon& m_icon = m_empty_icon; - wxBitmap& m_bmp = m_empty_bmp; - wxString m_copy; - std::string m_type; - int m_volume_id = -2; - bool m_container = false; - wxString m_extruder = "default"; - wxBitmap m_action_icon; - - bool IsContainer() const - { - return m_container; - } - - PrusaObjectDataViewModelNode* GetParent() - { - return m_parent; - } - MyObjectTreeModelNodePtrArray& GetChildren() - { - return m_children; - } - PrusaObjectDataViewModelNode* GetNthChild(unsigned int n) - { - return m_children.Item(n); - } - void Insert(PrusaObjectDataViewModelNode* child, unsigned int n) - { - if (!m_container) - m_container = true; - m_children.Insert(child, n); - } - void Append(PrusaObjectDataViewModelNode* child) - { - if (!m_container) - m_container = true; - m_children.Add(child); - } - void RemoveAllChildren() - { - if (GetChildCount() == 0) - return; - for (size_t id = GetChildCount() - 1; id >= 0; --id) - { - if (m_children.Item(id)->GetChildCount() > 0) - m_children[id]->RemoveAllChildren(); - auto node = m_children[id]; - m_children.RemoveAt(id); - delete node; - } - } - - size_t GetChildCount() const - { - return m_children.GetCount(); - } - - bool SetValue(const wxVariant &variant, unsigned int col) - { - switch (col) - { - case 0:{ - PrusaDataViewBitmapText data; - data << variant; - m_bmp = data.GetBitmap(); - m_name = data.GetText(); - return true;} - case 1: - m_copy = variant.GetString(); - return true; - case 2: - m_extruder = variant.GetString(); - return true; - case 3: - m_action_icon << variant; - return true; - default: - printf("MyObjectTreeModel::SetValue: wrong column"); - } - return false; - } - void SetIcon(const wxIcon &icon) - { - m_icon = icon; - } - - void SetBitmap(const wxBitmap &icon) - { - m_bmp = icon; - } - - void SetType(const std::string& type){ - m_type = type; - } - const std::string& GetType(){ - return m_type; - } - - void SetVolumeId(const int& volume_id){ - m_volume_id = volume_id; - } - const int& GetVolumeId(){ - return m_volume_id; - } - - // use this function only for childrens - void AssignAllVal(PrusaObjectDataViewModelNode& from_node) - { - // ! Don't overwrite other values because of equality of this values for all children -- - m_name = from_node.m_name; - m_icon = from_node.m_icon; - m_volume_id = from_node.m_volume_id; - m_extruder = from_node.m_extruder; - } - - bool SwapChildrens(int frst_id, int scnd_id) { - if (GetChildCount() < 2 || - frst_id < 0 || frst_id >= GetChildCount() || - scnd_id < 0 || scnd_id >= GetChildCount()) - return false; - - PrusaObjectDataViewModelNode new_scnd = *GetNthChild(frst_id); - PrusaObjectDataViewModelNode new_frst = *GetNthChild(scnd_id); - - new_scnd.m_volume_id = m_children.Item(scnd_id)->m_volume_id; - new_frst.m_volume_id = m_children.Item(frst_id)->m_volume_id; - - m_children.Item(frst_id)->AssignAllVal(new_frst); - m_children.Item(scnd_id)->AssignAllVal(new_scnd); - return true; - } - - // Set action icons for node - void set_object_action_icon(); - void set_part_action_icon(); - bool update_settings_digest(const std::vector<std::string>& categories); -}; - -// ---------------------------------------------------------------------------- -// PrusaObjectDataViewModel -// ---------------------------------------------------------------------------- - -class PrusaObjectDataViewModel :public wxDataViewModel -{ - std::vector<PrusaObjectDataViewModelNode*> m_objects; -public: - PrusaObjectDataViewModel(); - ~PrusaObjectDataViewModel(); - - wxDataViewItem Add(const wxString &name); - wxDataViewItem Add(const wxString &name, const int instances_count); - wxDataViewItem AddChild(const wxDataViewItem &parent_item, - const wxString &name, - const wxBitmap& icon, - const int extruder = 0, - const bool create_frst_child = true); - wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); - wxDataViewItem Delete(const wxDataViewItem &item); - void DeleteAll(); - void DeleteChildren(wxDataViewItem& parent); - wxDataViewItem GetItemById(int obj_idx); - wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); - int GetIdByItem(wxDataViewItem& item); - int GetVolumeIdByItem(const wxDataViewItem& item); - bool IsEmpty() { return m_objects.empty(); } - - // helper method for wxLog - - wxString GetName(const wxDataViewItem &item) const; - wxString GetCopy(const wxDataViewItem &item) const; - wxIcon& GetIcon(const wxDataViewItem &item) const; - wxBitmap& GetBitmap(const wxDataViewItem &item) const; - - // helper methods to change the model - - virtual unsigned int GetColumnCount() const override { return 3;} - virtual wxString GetColumnType(unsigned int col) const override{ return wxT("string"); } - - virtual void GetValue(wxVariant &variant, - const wxDataViewItem &item, unsigned int col) const override; - virtual bool SetValue(const wxVariant &variant, - const wxDataViewItem &item, unsigned int col) override; - bool SetValue(const wxVariant &variant, const int item_idx, unsigned int col); - - wxDataViewItem MoveChildUp(const wxDataViewItem &item); - wxDataViewItem MoveChildDown(const wxDataViewItem &item); - // For parent move child from cur_volume_id place to new_volume_id - // Remaining items will moved up/down accordingly - wxDataViewItem ReorganizeChildren(int cur_volume_id, - int new_volume_id, - const wxDataViewItem &parent); - - virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override; - - virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override; - virtual bool IsContainer(const wxDataViewItem &item) const override; - virtual unsigned int GetChildren(const wxDataViewItem &parent, - wxDataViewItemArray &array) const override; - - // Is the container just a header or an item with all columns - // In our case it is an item with all columns - virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; } - - wxDataViewItem HasSettings(const wxDataViewItem &item) const; - bool IsSettingsItem(const wxDataViewItem &item) const; - void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories); -}; - -// ---------------------------------------------------------------------------- -// PrusaBitmapTextRenderer -// ---------------------------------------------------------------------------- - -class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer -{ -public: - PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT, - int align = wxDVR_DEFAULT_ALIGNMENT): - wxDataViewCustomRenderer(wxT("wxObject"), mode, align) {} - - bool SetValue(const wxVariant &value); - bool GetValue(wxVariant &value) const; - - virtual bool Render(wxRect cell, wxDC *dc, int state); - virtual wxSize GetSize() const; - - virtual bool HasEditorCtrl() const { return false; } - -private: -// wxDataViewIconText m_value; - PrusaDataViewBitmapText m_value; -}; - - -// ---------------------------------------------------------------------------- -// MyCustomRenderer -// ---------------------------------------------------------------------------- - -class MyCustomRenderer : public wxDataViewCustomRenderer -{ -public: - // This renderer can be either activatable or editable, for demonstration - // purposes. In real programs, you should select whether the user should be - // able to activate or edit the cell and it doesn't make sense to switch - // between the two -- but this is just an example, so it doesn't stop us. - explicit MyCustomRenderer(wxDataViewCellMode mode) - : wxDataViewCustomRenderer("string", mode, wxALIGN_CENTER) - { } - - virtual bool Render(wxRect rect, wxDC *dc, int state) override/*wxOVERRIDE*/ - { - dc->SetBrush(*wxLIGHT_GREY_BRUSH); - dc->SetPen(*wxTRANSPARENT_PEN); - - rect.Deflate(2); - dc->DrawRoundedRectangle(rect, 5); - - RenderText(m_value, - 0, // no offset - wxRect(dc->GetTextExtent(m_value)).CentreIn(rect), - dc, - state); - return true; - } - - virtual bool ActivateCell(const wxRect& WXUNUSED(cell), - wxDataViewModel *WXUNUSED(model), - const wxDataViewItem &WXUNUSED(item), - unsigned int WXUNUSED(col), - const wxMouseEvent *mouseEvent) override/*wxOVERRIDE*/ - { - wxString position; - if (mouseEvent) - position = wxString::Format("via mouse at %d, %d", mouseEvent->m_x, mouseEvent->m_y); - else - position = "from keyboard"; -// wxLogMessage("MyCustomRenderer ActivateCell() %s", position); - return false; - } - - virtual wxSize GetSize() const override/*wxOVERRIDE*/ - { - return wxSize(60, 20); - } - - virtual bool SetValue(const wxVariant &value) override/*wxOVERRIDE*/ - { - m_value = value.GetString(); - return true; - } - - virtual bool GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; } - - virtual bool HasEditorCtrl() const override/*wxOVERRIDE*/{ return true; } - - virtual wxWindow* - CreateEditorCtrl(wxWindow* parent, - wxRect labelRect, - const wxVariant& value) override/*wxOVERRIDE*/ - { - wxTextCtrl* text = new wxTextCtrl(parent, wxID_ANY, value, - labelRect.GetPosition(), - labelRect.GetSize(), - wxTE_PROCESS_ENTER); - text->SetInsertionPointEnd(); - - return text; - } - - virtual bool - GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/ - { - wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl); - if (!text) - return false; - - value = text->GetValue(); - - return true; - } - -private: - wxString m_value; -}; - - -// ---------------------------------------------------------------------------- -// PrusaDoubleSlider -// ---------------------------------------------------------------------------- - -enum SelectedSlider { - ssUndef, - ssLower, - ssHigher -}; -enum TicksAction{ - taOnIcon, - taAdd, - taDel -}; -class PrusaDoubleSlider : public wxControl -{ -public: - PrusaDoubleSlider( - wxWindow *parent, - wxWindowID id, - int lowerValue, - int higherValue, - int minValue, - int maxValue, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize, - long style = wxSL_VERTICAL, - const wxValidator& val = wxDefaultValidator, - const wxString& name = wxEmptyString); - ~PrusaDoubleSlider(){} - - int GetLowerValue() const { - return m_lower_value; - } - int GetHigherValue() const { - return m_higher_value; - } - int GetActiveValue() const; - double GetLowerValueD() const { return get_double_value(ssLower); } - double GetHigherValueD() const { return get_double_value(ssHigher); } - wxSize DoGetBestSize() const override; - void SetLowerValue(const int lower_val); - void SetHigherValue(const int higher_val); - void SetMaxValue(const int max_value); - void SetKoefForLabels(const double koef) { - m_label_koef = koef; - } - void SetSliderValues(const std::vector<std::pair<int, double>>& values) { - m_values = values; - } - void ChangeOneLayerLock(); - - void OnPaint(wxPaintEvent& ){ render();} - void OnLeftDown(wxMouseEvent& event); - void OnMotion(wxMouseEvent& event); - void OnLeftUp(wxMouseEvent& event); - void OnEnterWin(wxMouseEvent& event){ enter_window(event, true); } - void OnLeaveWin(wxMouseEvent& event){ enter_window(event, false); } - void OnWheel(wxMouseEvent& event); - void OnKeyDown(wxKeyEvent &event); - void OnKeyUp(wxKeyEvent &event); - void OnRightDown(wxMouseEvent& event); - void OnRightUp(wxMouseEvent& event); - -protected: - - void render(); - void draw_focus_rect(); - void draw_action_icon(wxDC& dc, const wxPoint pt_beg, const wxPoint pt_end); - void draw_scroll_line(wxDC& dc, const int lower_pos, const int higher_pos); - void draw_thumb(wxDC& dc, const wxCoord& pos_coord, const SelectedSlider& selection); - void draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord& higher_pos); - void draw_ticks(wxDC& dc); - void draw_one_layer_icon(wxDC& dc); - void draw_thumb_item(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection); - void draw_info_line_with_icon(wxDC& dc, const wxPoint& pos, SelectedSlider selection); - void draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const; - - void update_thumb_rect(const wxCoord& begin_x, const wxCoord& begin_y, const SelectedSlider& selection); - void detect_selected_slider(const wxPoint& pt, const bool is_mouse_wheel = false); - void correct_lower_value(); - void correct_higher_value(); - void move_current_thumb(const bool condition); - void action_tick(const TicksAction action); - void enter_window(wxMouseEvent& event, const bool enter); - - bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } - - double get_scroll_step(); - wxString get_label(const SelectedSlider& selection) const; - void get_lower_and_higher_position(int& lower_pos, int& higher_pos); - int get_value_from_position(const wxCoord x, const wxCoord y); - wxCoord get_position_from_value(const int value); - wxSize get_size(); - void get_size(int *w, int *h); - double get_double_value(const SelectedSlider& selection) const; - -private: - int m_min_value; - int m_max_value; - int m_lower_value; - int m_higher_value; - wxBitmap m_bmp_thumb_higher; - wxBitmap m_bmp_thumb_lower; - wxBitmap m_bmp_add_tick_on; - wxBitmap m_bmp_add_tick_off; - wxBitmap m_bmp_del_tick_on; - wxBitmap m_bmp_del_tick_off; - wxBitmap m_bmp_one_layer_lock_on; - wxBitmap m_bmp_one_layer_lock_off; - wxBitmap m_bmp_one_layer_unlock_on; - wxBitmap m_bmp_one_layer_unlock_off; - SelectedSlider m_selection; - bool m_is_left_down = false; - bool m_is_right_down = false; - bool m_is_one_layer = false; - bool m_is_focused = false; - bool m_is_action_icon_focesed = false; - bool m_is_one_layer_icon_focesed = false; - - wxRect m_rect_lower_thumb; - wxRect m_rect_higher_thumb; - wxRect m_rect_tick_action; - wxRect m_rect_one_layer_icon; - wxSize m_thumb_size; - int m_tick_icon_dim; - int m_lock_icon_dim; - long m_style; - float m_label_koef = 1.0; - -// control's view variables - wxCoord SLIDER_MARGIN; // margin around slider - - wxPen DARK_ORANGE_PEN; - wxPen ORANGE_PEN; - wxPen LIGHT_ORANGE_PEN; - - wxPen DARK_GREY_PEN; - wxPen GREY_PEN; - wxPen LIGHT_GREY_PEN; - - std::vector<wxPen*> line_pens; - std::vector<wxPen*> segm_pens; - std::set<int> m_ticks; - std::vector<std::pair<int,double>> m_values; -}; - - -// ---------------------------------------------------------------------------- -// PrusaLockButton -// ---------------------------------------------------------------------------- - -class PrusaLockButton : public wxButton -{ -public: - PrusaLockButton( - wxWindow *parent, - wxWindowID id, - const wxPoint& pos = wxDefaultPosition, - const wxSize& size = wxDefaultSize); - ~PrusaLockButton(){} - - void OnButton(wxCommandEvent& event); - void OnEnterBtn(wxMouseEvent& event){ enter_button(true); event.Skip(); } - void OnLeaveBtn(wxMouseEvent& event){ enter_button(false); event.Skip(); } - - bool IsLocked() const { return m_is_pushed; } - -protected: - void enter_button(const bool enter); - -private: - bool m_is_pushed = false; - - wxBitmap m_bmp_lock_on; - wxBitmap m_bmp_lock_off; - wxBitmap m_bmp_unlock_on; - wxBitmap m_bmp_unlock_off; - - int m_lock_icon_dim; -}; - - -// ******************************* EXPERIMENTS ********************************************** -// ****************************************************************************************** - - -#endif // slic3r_GUI_wxExtensions_hpp_ diff --git a/xs/src/slic3r/GUI/wxPerlIface.cpp b/xs/src/slic3r/GUI/wxPerlIface.cpp deleted file mode 100644 index 216ca4b3b..000000000 --- a/xs/src/slic3r/GUI/wxPerlIface.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Derived from the following: - -///////////////////////////////////////////////////////////////////////////// -// Name: cpp/helpers.cpp -// Purpose: implementation for helpers.h -// Author: Mattia Barbon -// Modified by: -// Created: 29/10/2000 -// RCS-ID: $Id: helpers.cpp 3397 2012-09-30 02:26:07Z mdootson $ -// Copyright: (c) 2000-2011 Mattia Barbon -// Licence: This program is free software; you can redistribute it and/or -// modify it under the same terms as Perl itself -///////////////////////////////////////////////////////////////////////////// - -#ifdef __cplusplus -extern "C" { -#endif -#include "EXTERN.h" -#include "perl.h" -#include "XSUB.h" -#include "ppport.h" -#undef do_open -#undef do_close -#ifdef __cplusplus -} -#endif - -//#include <xsinit.h> - -// ---------------------------------------------------------------------------- -// Utility functions for working with MAGIC -// ---------------------------------------------------------------------------- - -struct my_magic -{ - my_magic() : object( NULL ), deleteable( true ) { } - - void* object; - bool deleteable; -}; - -//STATIC MGVTBL my_vtbl = { 0, 0, 0, 0, 0, 0, 0, 0 }; - -my_magic* wxPli_get_magic( pTHX_ SV* rv ) -{ - // check for reference - if( !SvROK( rv ) ) - return NULL; - SV* ref = SvRV( rv ); - - // if it isn't a SvPVMG, then it can't have MAGIC - // so it is deleteable - if( !ref || SvTYPE( ref ) < SVt_PVMG ) - return NULL; - - // search for '~' / PERL_MAGIC_ext magic, and check the value -// MAGIC* magic = mg_findext( ref, PERL_MAGIC_ext, &my_vtbl ); - MAGIC* magic = mg_find( ref, '~' ); - if( !magic ) - return NULL; - - return (my_magic*)magic->mg_ptr; -} - -// gets 'this' pointer from a blessed scalar/hash reference -void* wxPli_sv_2_object( pTHX_ SV* scalar, const char* classname ) -{ - // is it correct to use undef as 'NULL'? - if( !SvOK( scalar ) ) - { - return NULL; - } - - if( !SvROK( scalar ) ) - croak( "variable is not an object: it must have type %s", classname ); - - if( !classname || sv_derived_from( scalar, (char*) classname ) ) - { - SV* ref = SvRV( scalar ); - - my_magic* mg = wxPli_get_magic( aTHX_ scalar ); - - // rationale: if this is an hash-ish object, it always - // has both mg and mg->object; if however this is a - // scalar-ish object that has been marked/unmarked deletable - // it has mg, but not mg->object - if( !mg || !mg->object ) - return INT2PTR( void*, SvOK( ref ) ? SvIV( ref ) : 0 ); - - return mg->object; - } - else - { - croak( "variable is not of type %s", classname ); - return NULL; // dummy, for compiler - } -} diff --git a/xs/src/slic3r/GUI/wxinit.h b/xs/src/slic3r/GUI/wxinit.h deleted file mode 100644 index b55681b92..000000000 --- a/xs/src/slic3r/GUI/wxinit.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef slic3r_wxinit_hpp_ -#define slic3r_wxinit_hpp_ - -#include <wx/wx.h> -#include <wx/intl.h> -#include <wx/html/htmlwin.h> - -// Perl redefines a _ macro, so we undef this one -#undef _ - -// We do want to use translation however, so define it as __ so we can do a find/replace -// later when we no longer need to undef _ -#define __(s) wxGetTranslation((s)) - -// legacy macros -// https://wiki.wxwidgets.org/EventTypes_and_Event-Table_Macros -#ifndef wxEVT_BUTTON -#define wxEVT_BUTTON wxEVT_COMMAND_BUTTON_CLICKED -#endif - -#ifndef wxEVT_HTML_LINK_CLICKED -#define wxEVT_HTML_LINK_CLICKED wxEVT_COMMAND_HTML_LINK_CLICKED -#endif - -#endif |