Merge remote-tracking branch 'origin/master' into lm_wiping_dialog_colors

This commit is contained in:
YuSanka 2019-08-23 14:27:25 +02:00
commit 6b4f6f43dd
1421 changed files with 33462 additions and 21845 deletions

View file

@ -18,12 +18,9 @@ wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(25 * wxGetApp().em_unit(), -
#ifdef __APPLE__
m_user_drawn_background = false;
#endif /*__APPLE__*/
Bind(wxEVT_PAINT, ([this](wxPaintEvent &/* e */) { repaint(); }));
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(); }));
}
void Bed_2D::repaint()
void Bed_2D::repaint(const std::vector<Vec2d>& shape)
{
wxAutoBufferedPaintDC dc(this);
auto cw = GetSize().GetWidth();
@ -43,29 +40,20 @@ void Bed_2D::repaint()
dc.DrawRectangle(rect.GetLeft(), rect.GetTop(), rect.GetWidth(), rect.GetHeight());
}
// turn cw and ch from sizes to max coordinates
cw--;
ch--;
if (shape.empty())
return;
// reduce size to have some space around the drawn shape
cw -= (2 * Border);
ch -= (2 * Border);
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 bed_polygon = Polygon::new_scale(shape);
auto bb = BoundingBoxf(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();
@ -76,17 +64,17 @@ void Bed_2D::repaint()
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()));
m_shift = Vec2d(shift(0) + cbb.min(0), shift(1) - (cbb.max(1) - ch));
// draw bed fill
dc.SetBrush(wxBrush(wxColour(255, 255, 255), wxBRUSHSTYLE_SOLID));
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)));
for (auto pt : shape)
{
Point pt_pix = to_pixels(pt, ch);
pt_list.push_back(new wxPoint(pt_pix(0), pt_pix(1)));
}
dc.DrawPolygon(&pt_list, 0, 0);
@ -105,9 +93,9 @@ void Bed_2D::repaint()
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));
Point pt1 = to_pixels(unscale(pl.points[i]), ch);
Point pt2 = to_pixels(unscale(pl.points[i + 1]), ch);
dc.DrawLine(pt1(0), pt1(1), pt2(0), pt2(1));
}
}
@ -116,7 +104,7 @@ void Bed_2D::repaint()
dc.SetBrush(wxBrush(wxColour(0, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
dc.DrawPolygon(&pt_list, 0, 0);
auto origin_px = to_pixels(Vec2d(0, 0));
auto origin_px = to_pixels(Vec2d(0, 0), ch);
// draw axes
auto axes_len = 50;
@ -153,7 +141,7 @@ void Bed_2D::repaint()
// draw current position
if (m_pos!= Vec2d(0, 0)) {
auto pos_px = to_pixels(m_pos);
auto pos_px = to_pixels(m_pos, ch);
dc.SetPen(wxPen(wxColour(200, 0, 0), 2, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(wxColour(200, 0, 0), wxBRUSHSTYLE_TRANSPARENT));
dc.DrawCircle(pos_px(0), pos_px(1), 5);
@ -161,38 +149,17 @@ void Bed_2D::repaint()
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)
Point Bed_2D::to_pixels(const Vec2d& point, int height)
{
auto p = point * m_scale_factor + m_shift;
return Point(p(0), GetSize().GetHeight() - p(1));
return Point(p(0) + Border, height - p(1) + Border);
}
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)
void Bed_2D::set_pos(const Vec2d& pos)
{
m_pos = pos;
Refresh();

View file

@ -9,27 +9,21 @@ namespace GUI {
class Bed_2D : public wxPanel
{
static const int Border = 10;
bool m_user_drawn_background = true;
bool m_painted = false;
bool m_interactive = false;
double m_scale_factor;
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);
Point to_pixels(const Vec2d& point, int height);
void set_pos(const Vec2d& pos);
public:
Bed_2D(wxWindow* parent);
~Bed_2D() {}
explicit Bed_2D(wxWindow* parent);
std::vector<Vec2d> m_bed_shape;
void repaint(const std::vector<Vec2d>& shape);
};

View file

@ -9,10 +9,12 @@
#include "GUI_App.hpp"
#include "PresetBundle.hpp"
#include "Gizmos/GLGizmoBase.hpp"
#include "GLCanvas3D.hpp"
#include <GL/glew.h>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem/operations.hpp>
static const float GROUND_Z = -0.02f;
@ -21,7 +23,6 @@ namespace GUI {
bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords)
{
#if ENABLE_TEXTURES_FROM_SVG
m_vertices.clear();
unsigned int v_size = 3 * (unsigned int)triangles.size();
@ -81,75 +82,12 @@ bool GeometryBuffer::set_from_triangles(const Polygons& triangles, float z, bool
}
}
}
#else
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] = (m_tex_coords[i] - min_x) * inv_size_x;
m_tex_coords[i + 1] = (m_tex_coords[i + 1] - min_y) * inv_size_y;
}
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
return true;
}
bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
{
#if ENABLE_TEXTURES_FROM_SVG
m_vertices.clear();
unsigned int v_size = 2 * (unsigned int)lines.size();
@ -173,37 +111,14 @@ bool GeometryBuffer::set_from_lines(const Lines& lines, float z)
v2.position[2] = z;
++v_count;
}
#else
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;
}
#endif // ENABLE_TEXTURES_FROM_SVG
return true;
}
#if ENABLE_TEXTURES_FROM_SVG
const float* GeometryBuffer::get_vertices_data() const
{
return (m_vertices.size() > 0) ? (const float*)m_vertices.data() : nullptr;
}
#endif // ENABLE_TEXTURES_FROM_SVG
const double Bed3D::Axes::Radius = 0.5;
const double Bed3D::Axes::ArrowBaseRadius = 2.5 * Bed3D::Axes::Radius;
@ -211,7 +126,7 @@ const double Bed3D::Axes::ArrowLength = 5.0;
Bed3D::Axes::Axes()
: origin(Vec3d::Zero())
, length(Vec3d::Zero())
, length(25.0 * Vec3d::Ones())
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
@ -273,24 +188,44 @@ void Bed3D::Axes::render_axis(double length) const
Bed3D::Bed3D()
: m_type(Custom)
#if ENABLE_TEXTURES_FROM_SVG
, m_custom_texture("")
, m_custom_model("")
, m_requires_canvas_update(false)
, m_vbo_id(0)
#endif // ENABLE_TEXTURES_FROM_SVG
, m_scale_factor(1.0f)
{
}
bool Bed3D::set_shape(const Pointfs& shape)
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
EType new_type = detect_type(shape);
if (m_shape == shape && m_type == new_type)
// check that the passed custom texture filename is valid
std::string cst_texture(custom_texture);
if (!cst_texture.empty())
{
if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture))
cst_texture = "";
}
// check that the passed custom texture filename is valid
std::string cst_model(custom_model);
if (!cst_model.empty())
{
if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model))
cst_model = "";
}
if ((m_shape == shape) && (m_type == new_type) && (m_custom_texture == cst_texture) && (m_custom_model == cst_model))
// No change, no need to update the UI.
return false;
m_shape = shape;
m_custom_texture = cst_texture;
m_custom_model = cst_model;
m_type = new_type;
calc_bounding_box();
calc_bounding_boxes();
ExPolygon poly;
for (const Vec2d& p : m_shape)
@ -305,13 +240,13 @@ bool Bed3D::set_shape(const Pointfs& shape)
m_polygon = offset_ex(poly.contour, (float)bed_bbox.radius() * 1.7f, jtRound, scale_(0.5))[0].contour;
#if ENABLE_TEXTURES_FROM_SVG
reset();
#endif // ENABLE_TEXTURES_FROM_SVG
m_texture.reset();
m_model.reset();
// Set the origin and size for painting of the coordinate system axes.
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
m_axes.length = 0.1 * get_bounding_box().max_size() * Vec3d::Ones();
m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones();
// Let the calee to update the UI.
return true;
@ -327,86 +262,54 @@ Point Bed3D::point_projection(const Point& point) const
return m_polygon.point_projection(point);
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor) const
{
m_scale_factor = scale_factor;
EType type = useVBOs ? m_type : Custom;
switch (type)
{
case MK2:
{
render_prusa("mk2", theta > 90.0f);
break;
}
case MK3:
{
render_prusa("mk3", theta > 90.0f);
break;
}
case SL1:
{
render_prusa("sl1", theta > 90.0f);
break;
}
default:
case Custom:
{
render_custom();
break;
}
}
}
#else
void Bed3D::render(float theta, bool useVBOs, float scale_factor) const
{
m_scale_factor = scale_factor;
if (m_shape.empty())
return;
render_axes();
switch (m_type)
{
case MK2:
{
render_prusa("mk2", theta, useVBOs);
render_prusa(canvas, "mk2", theta > 90.0f);
break;
}
case MK3:
{
render_prusa("mk3", theta, useVBOs);
render_prusa(canvas, "mk3", theta > 90.0f);
break;
}
case SL1:
{
render_prusa("sl1", theta, useVBOs);
render_prusa(canvas, "sl1", theta > 90.0f);
break;
}
default:
case Custom:
{
render_custom();
render_custom(canvas, theta > 90.0f);
break;
}
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_axes() const
{
if (!m_shape.empty())
m_axes.render();
}
void Bed3D::calc_bounding_box()
void Bed3D::calc_bounding_boxes() const
{
m_bounding_box = BoundingBoxf3();
for (const Vec2d& p : m_shape)
{
m_bounding_box.merge(Vec3d(p(0), p(1), 0.0));
}
m_extended_bounding_box = m_bounding_box;
// extend to contain axes
m_extended_bounding_box.merge(m_axes.length + Axes::ArrowLength * Vec3d::Ones());
// extend to contain model, if any
if (!m_model.get_filename().empty())
m_extended_bounding_box.merge(m_model.get_transformed_bounding_box());
}
void Bed3D::calc_triangles(const ExPolygon& poly)
@ -414,7 +317,7 @@ void Bed3D::calc_triangles(const ExPolygon& poly)
Polygons triangles;
poly.triangulate(&triangles);
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, m_type != Custom))
if (!m_triangles.set_from_triangles(triangles, GROUND_Z, true))
printf("Unable to create bed triangles\n");
}
@ -486,315 +389,249 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
return type;
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_prusa(const std::string &key, bool bottom) const
void Bed3D::render_axes() const
{
std::string tex_path = resources_dir() + "/icons/bed/" + key;
if (!m_shape.empty())
m_axes.render();
}
std::string model_path = resources_dir() + "/models/" + key;
void Bed3D::render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const
{
if (!bottom)
render_model(m_custom_model.empty() ? resources_dir() + "/models/" + key + "_bed.stl" : m_custom_model);
// use anisotropic filter if graphic card allows
GLfloat max_anisotropy = 0.0f;
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy));
render_texture(m_custom_texture.empty() ? resources_dir() + "/icons/bed/" + key + ".svg" : m_custom_texture, bottom, canvas);
}
// use higher resolution images if graphic card allows
GLint max_tex_size;
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
// clamp or the texture generation becomes too slow
max_tex_size = std::min(max_tex_size, 8192);
std::string filename = tex_path + ".svg";
void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const
{
if (filename.empty())
{
m_texture.reset();
render_default(bottom);
return;
}
if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename))
{
if (!m_texture.load_from_svg_file(filename, true, max_tex_size))
m_texture.reset();
if (boost::algorithm::iends_with(filename, ".svg"))
{
render_custom();
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
{
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8))
{
render_default(bottom);
return;
}
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
{
render_default(bottom);
return;
}
}
else if (boost::algorithm::iends_with(filename, ".png"))
{
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
{
if (!m_temp_texture.load_from_file(filename, false, GLTexture::None, false))
{
render_default(bottom);
return;
}
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(filename, true, GLTexture::MultiThreaded, true))
{
render_default(bottom);
return;
}
}
else
{
render_default(bottom);
return;
}
}
else if (m_texture.unsent_compressed_data_available())
{
// sends to gpu the already available compressed levels of the main texture
m_texture.send_compressed_data_to_gpu();
if (max_anisotropy > 0.0f)
{
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
// the temporary texture is not needed anymore, reset it
if (m_temp_texture.get_id() != 0)
m_temp_texture.reset();
m_requires_canvas_update = true;
}
else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu())
{
canvas.stop_keeping_dirty();
m_requires_canvas_update = false;
}
if (!bottom)
if (m_triangles.get_vertices_count() > 0)
{
filename = model_path + "_bed.stl";
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, true)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 7.5, -0.03);
else if (key == "mk3")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 5.5, 2.43);
else if (key == "sl1")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 0.0, -0.03);
if (m_shader.get_shader_program_id() == 0)
m_shader.init("printbed.vs", "printbed.fs");
m_model.center_around(offset);
}
if (!m_model.get_filename().empty())
if (m_shader.is_initialized())
{
glsafe(::glEnable(GL_LIGHTING));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
}
}
m_shader.start_using();
m_shader.set_uniform("transparent_background", bottom);
m_shader.set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
if (m_vbo_id == 0)
{
glsafe(::glGenBuffers(1, &m_vbo_id));
if (m_vbo_id == 0)
{
glsafe(::glGenBuffers(1, &m_vbo_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
if (bottom)
glsafe(::glFrontFace(GL_CW));
unsigned int stride = m_triangles.get_vertex_data_size();
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
// show the temporary texture while no compressed data is available
GLuint tex_id = (GLuint)m_temp_texture.get_id();
if (tex_id == 0)
tex_id = (GLuint)m_texture.get_id();
glsafe(::glBindTexture(GL_TEXTURE_2D, tex_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
glsafe(::glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)m_triangles.get_vertices_data_size(), (const GLvoid*)m_triangles.get_vertices_data(), GL_STATIC_DRAW));
if (position_id != -1)
{
glsafe(::glEnableVertexAttribArray(position_id));
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
glsafe(::glEnableVertexAttribArray(tex_coords_id));
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
}
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
if (tex_coords_id != -1)
glsafe(::glDisableVertexAttribArray(tex_coords_id));
if (position_id != -1)
glsafe(::glDisableVertexAttribArray(position_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
if (bottom)
glsafe(::glFrontFace(GL_CW));
render_prusa_shader(bottom);
if (bottom)
glsafe(::glFrontFace(GL_CCW));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
}
}
void Bed3D::render_prusa_shader(bool transparent) const
{
if (m_shader.get_shader_program_id() == 0)
m_shader.init("printbed.vs", "printbed.fs");
if (m_shader.is_initialized())
{
m_shader.start_using();
m_shader.set_uniform("transparent_background", transparent);
unsigned int stride = m_triangles.get_vertex_data_size();
GLint position_id = m_shader.get_attrib_location("v_position");
GLint tex_coords_id = m_shader.get_attrib_location("v_tex_coords");
glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)m_texture.get_id()));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, m_vbo_id));
if (position_id != -1)
{
glsafe(::glEnableVertexAttribArray(position_id));
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_position_offset()));
}
if (tex_coords_id != -1)
{
glsafe(::glEnableVertexAttribArray(tex_coords_id));
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_tex_coords_offset()));
}
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
if (tex_coords_id != -1)
glsafe(::glDisableVertexAttribArray(tex_coords_id));
if (position_id != -1)
glsafe(::glDisableVertexAttribArray(position_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_shader.stop_using();
}
}
#else
void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const
{
std::string tex_path = resources_dir() + "/icons/bed/" + key;
// use higher resolution images if graphic card allows
GLint max_tex_size;
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size));
// temporary set to lowest resolution
max_tex_size = 2048;
if (max_tex_size >= 8192)
tex_path += "_8192";
else if (max_tex_size >= 4096)
tex_path += "_4096";
std::string model_path = resources_dir() + "/models/" + key;
// use anisotropic filter if graphic card allows
GLfloat max_anisotropy = 0.0f;
if (glewIsSupported("GL_EXT_texture_filter_anisotropic"))
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy));
std::string filename = tex_path + "_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;
}
if (max_anisotropy > 0.0f)
{
glsafe(::glBindTexture(GL_TEXTURE_2D, m_top_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
if (bottom)
glsafe(::glFrontFace(GL_CCW));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
m_shader.stop_using();
}
}
filename = tex_path + "_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;
}
if (max_anisotropy > 0.0f)
{
glsafe(::glBindTexture(GL_TEXTURE_2D, m_bottom_texture.get_id()));
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
}
}
if (theta <= 90.0f)
{
filename = model_path + "_bed.stl";
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 7.5, -0.03);
else if (key == "mk3")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 5.5, 2.43);
else if (key == "sl1")
// hardcoded value to match the stl model
offset += Vec3d(0.0, 0.0, -0.03);
m_model.center_around(offset);
}
if (!m_model.get_filename().empty())
{
glsafe(::glEnable(GL_LIGHTING));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
}
}
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnable(GL_TEXTURE_2D));
glsafe(::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_TEXTURE_COORD_ARRAY));
if (theta > 90.0f)
glsafe(::glFrontFace(GL_CW));
glsafe(::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id()));
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
glsafe(::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
if (theta > 90.0f)
glsafe(::glFrontFace(GL_CCW));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glDisableClientState(GL_TEXTURE_COORD_ARRAY));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_TEXTURE_2D));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDepthMask(GL_TRUE));
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_custom() const
void Bed3D::render_model(const std::string& filename) const
{
#if ENABLE_TEXTURES_FROM_SVG
m_texture.reset();
#else
m_top_texture.reset();
m_bottom_texture.reset();
#endif // ENABLE_TEXTURES_FROM_SVG
if (filename.empty())
return;
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename))
{
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
Vec3d shift = m_bounding_box.center();
shift(2) = -0.03;
m_model.set_offset(shift);
// update extended bounding box
calc_bounding_boxes();
}
if (!m_model.get_filename().empty())
{
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
#if ENABLE_TEXTURES_FROM_SVG
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
#else
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices()));
#endif // ENABLE_TEXTURES_FROM_SVG
glsafe(::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
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glLineWidth(3.0f * m_scale_factor));
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
#if ENABLE_TEXTURES_FROM_SVG
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
#else
glsafe(::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_gridlines.get_vertices()));
#endif // ENABLE_TEXTURES_FROM_SVG
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)gridlines_vcount));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_BLEND));
m_model.render();
glsafe(::glDisable(GL_LIGHTING));
}
}
#if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
{
if (m_custom_texture.empty() && m_custom_model.empty())
{
render_default(bottom);
return;
}
if (!bottom)
render_model(m_custom_model);
render_texture(m_custom_texture, bottom, canvas);
}
void Bed3D::render_default(bool bottom) const
{
m_texture.reset();
unsigned int triangles_vcount = m_triangles.get_vertices_count();
if (triangles_vcount > 0)
{
bool has_model = !m_model.get_filename().empty();
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
if (!has_model && !bottom)
{
// draw background
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glColor4f(0.35f, 0.35f, 0.35f, 0.4f));
glsafe(::glNormal3d(0.0f, 0.0f, 1.0f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_triangles.get_vertices_data()));
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)triangles_vcount));
glsafe(::glDepthMask(GL_TRUE));
}
// draw grid
glsafe(::glLineWidth(3.0f * m_scale_factor));
if (has_model && !bottom)
glsafe(::glColor4f(0.75f, 0.75f, 0.75f, 1.0f));
else
glsafe(::glColor4f(0.2f, 0.2f, 0.2f, 0.4f));
glsafe(::glVertexPointer(3, GL_FLOAT, m_triangles.get_vertex_data_size(), (GLvoid*)m_gridlines.get_vertices_data()));
glsafe(::glDrawArrays(GL_LINES, 0, (GLsizei)m_gridlines.get_vertices_count()));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisable(GL_BLEND));
}
}
void Bed3D::reset()
{
if (m_vbo_id > 0)
@ -803,7 +640,6 @@ void Bed3D::reset()
m_vbo_id = 0;
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
} // GUI
} // Slic3r

View file

@ -3,9 +3,7 @@
#include "GLTexture.hpp"
#include "3DScene.hpp"
#if ENABLE_TEXTURES_FROM_SVG
#include "GLShader.hpp"
#endif // ENABLE_TEXTURES_FROM_SVG
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
@ -13,9 +11,10 @@ typedef class GLUquadric GLUquadricObj;
namespace Slic3r {
namespace GUI {
class GLCanvas3D;
class GeometryBuffer
{
#if ENABLE_TEXTURES_FROM_SVG
struct Vertex
{
float position[3];
@ -29,27 +28,17 @@ class GeometryBuffer
};
std::vector<Vertex> m_vertices;
#else
std::vector<float> m_vertices;
std::vector<float> m_tex_coords;
#endif // ENABLE_TEXTURES_FROM_SVG
public:
bool set_from_triangles(const Polygons& triangles, float z, bool generate_tex_coords);
bool set_from_lines(const Lines& lines, float z);
#if ENABLE_TEXTURES_FROM_SVG
const float* get_vertices_data() const;
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
unsigned int get_position_offset() const { return 0; }
unsigned int get_tex_coords_offset() const { return (unsigned int)(3 * sizeof(float)); }
size_t get_position_offset() const { return 0; }
size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
#else
const float* get_vertices() const { return m_vertices.data(); }
const float* get_tex_coords() const { return m_tex_coords.data(); }
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size() / 3; }
#endif // ENABLE_TEXTURES_FROM_SVG
};
class Bed3D
@ -85,18 +74,20 @@ public:
private:
EType m_type;
Pointfs m_shape;
BoundingBoxf3 m_bounding_box;
std::string m_custom_texture;
std::string m_custom_model;
mutable BoundingBoxf3 m_bounding_box;
mutable BoundingBoxf3 m_extended_bounding_box;
Polygon m_polygon;
GeometryBuffer m_triangles;
GeometryBuffer m_gridlines;
#if ENABLE_TEXTURES_FROM_SVG
mutable GLTexture m_texture;
// temporary texture shown until the main texture has still no levels compressed
mutable GLTexture m_temp_texture;
// used to trigger 3D scene update once all compressed textures have been sent to GPU
mutable bool m_requires_canvas_update;
mutable Shader m_shader;
mutable unsigned int m_vbo_id;
#else
mutable GLTexture m_top_texture;
mutable GLTexture m_bottom_texture;
#endif // ENABLE_TEXTURES_FROM_SVG
mutable GLBed m_model;
Axes m_axes;
@ -104,9 +95,7 @@ private:
public:
Bed3D();
#if ENABLE_TEXTURES_FROM_SVG
~Bed3D() { reset(); }
#endif // ENABLE_TEXTURES_FROM_SVG
EType get_type() const { return m_type; }
@ -115,30 +104,26 @@ public:
const Pointfs& get_shape() const { return m_shape; }
// Return true if the bed shape changed, so the calee will update the UI.
bool set_shape(const Pointfs& shape);
bool set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model);
const BoundingBoxf3& get_bounding_box() const { return m_bounding_box; }
const BoundingBoxf3& get_bounding_box(bool extended) const { return extended ? m_extended_bounding_box : m_bounding_box; }
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
void render(float theta, bool useVBOs, float scale_factor) const;
void render_axes() const;
void render(GLCanvas3D& canvas, float theta, float scale_factor) const;
private:
void calc_bounding_box();
void calc_bounding_boxes() const;
void calc_triangles(const ExPolygon& poly);
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
EType detect_type(const Pointfs& shape) const;
#if ENABLE_TEXTURES_FROM_SVG
void render_prusa(const std::string& key, bool bottom) const;
void render_prusa_shader(bool transparent) const;
#else
void render_prusa(const std::string &key, float theta, bool useVBOs) const;
#endif // ENABLE_TEXTURES_FROM_SVG
void render_custom() const;
#if ENABLE_TEXTURES_FROM_SVG
void render_axes() const;
void render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const;
void render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const;
void render_model(const std::string& filename) const;
void render_custom(GLCanvas3D& canvas, bool bottom) const;
void render_default(bool bottom) const;
void reset();
#endif // ENABLE_TEXTURES_FROM_SVG
};
} // GUI

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,7 @@
#include "slic3r/GUI/GLCanvas3DManager.hpp"
#include <functional>
#include <memory>
#ifndef NDEBUG
#define HAS_GLSAFE
@ -55,7 +56,7 @@ public:
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),
@ -63,7 +64,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
{ this->setup_sizes(); }
{ assert(! rhs.has_VBOs()); }
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
triangle_indices(std::move(rhs.triangle_indices)),
@ -71,17 +72,25 @@ public:
vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0),
quad_indices_VBO_id(0)
{ this->setup_sizes(); }
{ assert(! rhs.has_VBOs()); }
~GLIndexedVertexArray() { release_geometry(); }
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();
assert(quad_indices_VBO_id == 0);
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
assert(rhs.triangle_indices_VBO_id == 0);
assert(rhs.quad_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->m_bounding_box = rhs.m_bounding_box;
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
this->triangle_indices_size = rhs.triangle_indices_size;
this->quad_indices_size = rhs.quad_indices_size;
return *this;
}
@ -89,11 +98,17 @@ public:
{
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();
assert(quad_indices_VBO_id == 0);
assert(rhs.vertices_and_normals_interleaved_VBO_id == 0);
assert(rhs.triangle_indices_VBO_id == 0);
assert(rhs.quad_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->m_bounding_box = std::move(rhs.m_bounding_box);
this->vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
this->triangle_indices_size = rhs.triangle_indices_size;
this->quad_indices_size = rhs.quad_indices_size;
return *this;
}
@ -104,19 +119,18 @@ public:
// 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;
size_t vertices_and_normals_interleaved_size{ 0 };
size_t triangle_indices_size{ 0 };
size_t quad_indices_size{ 0 };
// 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;
// Zero if the VBOs are not sent to GPU yet.
unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
unsigned int triangle_indices_VBO_id{ 0 };
unsigned int quad_indices_VBO_id{ 0 };
void load_mesh_flat_shading(const TriangleMesh &mesh);
void load_mesh_full_shading(const TriangleMesh &mesh);
void load_mesh(const TriangleMesh &mesh, bool use_VBOs) { use_VBOs ? this->load_mesh_full_shading(mesh) : this->load_mesh_flat_shading(mesh); }
void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
@ -127,14 +141,21 @@ public:
}
inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
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);
this->vertices_and_normals_interleaved.emplace_back(nx);
this->vertices_and_normals_interleaved.emplace_back(ny);
this->vertices_and_normals_interleaved.emplace_back(nz);
this->vertices_and_normals_interleaved.emplace_back(x);
this->vertices_and_normals_interleaved.emplace_back(y);
this->vertices_and_normals_interleaved.emplace_back(z);
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
m_bounding_box.merge(Vec3f(x, y, z).cast<double>());
};
inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
@ -146,80 +167,82 @@ public:
}
inline void push_triangle(int idx1, int idx2, int idx3) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
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);
this->triangle_indices.emplace_back(idx1);
this->triangle_indices.emplace_back(idx2);
this->triangle_indices.emplace_back(idx3);
this->triangle_indices_size = this->triangle_indices.size();
};
inline void push_quad(int idx1, int idx2, int idx3, int idx4) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
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);
this->quad_indices.emplace_back(idx1);
this->quad_indices.emplace_back(idx2);
this->quad_indices.emplace_back(idx3);
this->quad_indices.emplace_back(idx4);
this->quad_indices_size = this->quad_indices.size();
};
// 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);
void finalize_geometry(bool opengl_initialized);
// 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;
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();
this->m_bounding_box.reset();
vertices_and_normals_interleaved_size = 0;
triangle_indices_size = 0;
quad_indices_size = 0;
}
// Shrink the internal storage to tighly fit the data stored.
void shrink_to_fit() {
if (! this->has_VBOs())
this->setup_sizes();
void shrink_to_fit() {
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;
const BoundingBoxf3& bounding_box() const { return m_bounding_box; }
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const { return sizeof(*this) + vertices_and_normals_interleaved.capacity() * sizeof(float) + triangle_indices.capacity() * sizeof(int) + quad_indices.capacity() * sizeof(int); }
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const
{
size_t memsize = 0;
if (this->vertices_and_normals_interleaved_VBO_id != 0)
memsize += this->vertices_and_normals_interleaved_size * 4;
if (this->triangle_indices_VBO_id != 0)
memsize += this->triangle_indices_size * 4;
if (this->quad_indices_VBO_id != 0)
memsize += this->quad_indices_size * 4;
return memsize;
}
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
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();
}
BoundingBoxf3 m_bounding_box;
};
class GLVolume {
@ -243,30 +266,25 @@ public:
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]) {}
~GLVolume();
private:
Geometry::Transformation m_instance_transformation;
Geometry::Transformation m_volume_transformation;
// Shift in z required by sla supports+pad
double m_sla_shift_z;
double m_sla_shift_z;
// 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.
// This object may or may not own the convex hull instance based on m_convex_hull_owned
const TriangleMesh* m_convex_hull;
bool m_convex_hull_owned;
// Convex hull of the volume, if any.
std::shared_ptr<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.
@ -298,6 +316,8 @@ public:
bool selected;
// Is this object disabled from selection?
bool disabled;
// Is this object printable?
bool printable;
// Whether or not this volume is active for rendering
bool is_active;
// Whether or not to use this volume when applying zoom_to_volumes()
@ -331,6 +351,9 @@ public:
// Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer.
std::vector<size_t> offsets;
// Bounding box of this volume, in unscaled coordinates.
const BoundingBoxf3& bounding_box() const { return this->indexed_vertex_array.bounding_box(); }
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
@ -395,7 +418,9 @@ public:
double get_sla_shift_z() const { return m_sla_shift_z; }
void set_sla_shift_z(double z) { m_sla_shift_z = z; }
void set_convex_hull(const TriangleMesh *convex_hull, bool owned);
void set_convex_hull(std::shared_ptr<const TriangleMesh> convex_hull) { m_convex_hull = std::move(convex_hull); }
void set_convex_hull(const TriangleMesh &convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(convex_hull); }
void set_convex_hull(TriangleMesh &&convex_hull) { m_convex_hull = std::make_shared<const TriangleMesh>(std::move(convex_hull)); }
int object_idx() const { return this->composite_id.object_id; }
int volume_idx() const { return this->composite_id.volume_id; }
@ -409,22 +434,32 @@ public:
BoundingBoxf3 transformed_convex_hull_bounding_box(const Transform3d &trafo) const;
// caching variant
const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
// convex hull
const TriangleMesh* convex_hull() const { return m_convex_hull.get(); }
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 set_range(double low, double high);
void render() const;
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
void render_legacy() const;
void render(int color_id, int detection_id, int worldmatrix_id) const;
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
void finalize_geometry(bool opengl_initialized) { this->indexed_vertex_array.finalize_geometry(opengl_initialized); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
bool is_sla_support() const;
bool is_sla_pad() const;
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {
//FIXME what to do wih m_convex_hull?
return sizeof(*this) - sizeof(this->indexed_vertex_array) + this->indexed_vertex_array.cpu_memory_used() + this->print_zs.capacity() * sizeof(coordf_t) + this->offsets.capacity() * sizeof(size_t);
}
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const { return this->indexed_vertex_array.gpu_memory_used(); }
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
};
typedef std::vector<GLVolume*> GLVolumePtrs;
@ -459,42 +494,41 @@ public:
~GLVolumeCollection() { clear(); };
std::vector<int> load_object(
const ModelObject *model_object,
const ModelObject *model_object,
int obj_idx,
const std::vector<int> &instance_idxs,
const std::string &color_by,
bool use_VBOs);
const std::vector<int> &instance_idxs,
const std::string &color_by,
bool opengl_initialized);
int load_object_volume(
const ModelObject *model_object,
int obj_idx,
int volume_idx,
int instance_idx,
const std::string &color_by,
bool use_VBOs);
const ModelObject *model_object,
int obj_idx,
int volume_idx,
int instance_idx,
const std::string &color_by,
bool opengl_initialized);
// Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary(
const SLAPrintObject *print_object,
int obj_idx,
// pairs of <instance_idx, print_instance_idx>
const std::vector<std::pair<size_t, size_t>> &instances,
const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone
size_t timestamp,
bool use_VBOs);
bool opengl_initialized);
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);
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width, bool opengl_initialized);
// Render the volumes by OpenGL.
void render_VBOs(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
void render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) 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); }
void finalize_geometry(bool opengl_initialized) { for (auto* v : volumes) v->finalize_geometry(opengl_initialized); }
// 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(); }
@ -522,6 +556,18 @@ public:
// 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;
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const;
// Return an estimate of the memory held by GPU vertex buffers.
size_t gpu_memory_used() const;
size_t total_memory_used() const { return this->cpu_memory_used() + this->gpu_memory_used(); }
// Return CPU, GPU and total memory log line.
std::string log_memory_info() const;
bool has_toolpaths_to_export() const;
// Export the geometry of the GLVolumes toolpaths of this collection into the file with the given path, in obj format
void export_toolpaths_to_obj(const char* filename) const;
private:
GLVolumeCollection(const GLVolumeCollection &other);
GLVolumeCollection& operator=(const GLVolumeCollection &);
@ -533,17 +579,17 @@ class GLModel
{
protected:
GLVolume m_volume;
bool m_useVBOs;
std::string m_filename;
public:
GLModel();
virtual ~GLModel();
bool init(bool useVBOs) { return on_init(useVBOs); }
bool init_from_file(const std::string& filename, bool useVBOs) { return on_init_from_file(filename, useVBOs); }
// init() / init_from_file() shall be called with the OpenGL context active!
bool init() { return on_init(); }
bool init_from_file(const std::string& filename) { return on_init_from_file(filename); }
void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box.center()); }
void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box().center()); }
void set_color(const float* color, unsigned int size);
const Vec3d& get_offset() const;
@ -554,25 +600,22 @@ public:
void set_scale(const Vec3d& scale);
const std::string& get_filename() const { return m_filename; }
const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box; }
const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box(); }
const BoundingBoxf3& get_transformed_bounding_box() const { return m_volume.transformed_bounding_box(); }
void reset();
void render() const;
protected:
virtual bool on_init(bool useVBOs) { return false; }
virtual bool on_init_from_file(const std::string& filename, bool useVBOs) { return false; }
private:
void render_VBOs() const;
void render_legacy() const;
virtual bool on_init() { return false; }
virtual bool on_init_from_file(const std::string& filename) { return false; }
};
class GLArrow : public GLModel
{
protected:
virtual bool on_init(bool useVBOs);
bool on_init() override;
};
class GLCurvedArrow : public GLModel
@ -583,13 +626,13 @@ public:
explicit GLCurvedArrow(unsigned int resolution);
protected:
virtual bool on_init(bool useVBOs);
bool on_init() override;
};
class GLBed : public GLModel
{
protected:
virtual bool on_init_from_file(const std::string& filename, bool useVBOs);
bool on_init_from_file(const std::string& filename) override;
};
class _3DScene
@ -604,6 +647,7 @@ public:
static void remove_all_canvases();
static bool init(wxGLCanvas* canvas);
static void destroy();
static GUI::GLCanvas3D* get_canvas(wxGLCanvas* canvas);

View file

@ -76,9 +76,9 @@ void CopyrightsDialog::fill_entries()
{
m_entries = {
{ "wxWidgets" , "2019 wxWidgets" , "https://www.wxwidgets.org/" },
{ "OpenGL" , "1997-2019 The Khronos™ Group Inc" , "https://www.opengl.org/" },
{ "OpenGL" , "1997-2019 The Khronos™ Group Inc" , "https://www.opengl.org/" },
{ "GNU gettext" , "1998, 2019 Free Software Foundation, Inc." , "https://www.gnu.org/software/gettext/" },
{ "PoEdit" , "2019 Václav Slavík" , "https://poedit.net/" },
{ "PoEdit" , "2019 Václav Slavík" , "https://poedit.net/" },
{ "ImGUI" , "2014-2019 Omar Cornut" , "https://github.com/ocornut/imgui" },
{ "Eigen" , "" , "http://eigen.tuxfamily.org" },
{ "ADMesh" , "1995, 1996 Anthony D. Martin; "
@ -106,8 +106,13 @@ void CopyrightsDialog::fill_entries()
"2001-2016 Expat maintainers" , "http://www.libexpat.org/" },
{ "AVRDUDE" , "2018 Free Software Foundation, Inc." , "http://savannah.nongnu.org/projects/avrdude" },
{ "Shinyprofiler" , "2007-2010 Aidin Abedi" , "http://code.google.com/p/shinyprofiler/" },
{ "Real-Time DXT1/DXT5 C compression library"
, "Based on original by fabian \"ryg\" giesen v1.04. "
"Custom version, modified by Yann Collet" , "https://github.com/Cyan4973/RygsDXTc" },
{ "Icons for STL and GCODE files."
, "Akira Yasuda" , "http://3dp0.com/icons-for-stl-and-gcode/" }
, "Akira Yasuda" , "http://3dp0.com/icons-for-stl-and-gcode/" },
{ "AppImage packaging for Linux using AppImageKit"
, "2004-2019 Simon Peter and contributors" , "https://appimage.org/" }
};
}

View file

@ -15,9 +15,13 @@
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/exceptions.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/format.hpp>
#include <wx/string.h>
#include "I18N.hpp"
namespace Slic3r {
static const std::string VENDOR_PREFIX = "vendor:";
@ -54,12 +58,11 @@ void AppConfig::set_defaults()
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.
// github.com/prusa3d/PrusaSlicer/issues/233
if (get("use_legacy_opengl").empty())
set("use_legacy_opengl", "0");
// remove old 'use_legacy_opengl' parameter from this config, if present
if (!get("use_legacy_opengl").empty())
erase("", "use_legacy_opengl");
#if __APPLE__
#ifdef __APPLE__
if (get("use_retina_opengl").empty())
set("use_retina_opengl", "1");
#endif
@ -73,6 +76,9 @@ void AppConfig::set_defaults()
if (get("custom_toolbar_size").empty())
set("custom_toolbar_size", "100");
if (get("use_perspective_camera").empty())
set("use_perspective_camera", "1");
// Remove legacy window positions/sizes
erase("", "main_frame_maximized");
erase("", "main_frame_pos");
@ -88,7 +94,15 @@ void AppConfig::load()
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(AppConfig::config_path());
pt::read_ini(ifs, tree);
try {
pt::read_ini(ifs, tree);
} catch (pt::ptree_error& ex) {
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
throw std::runtime_error(
_utf8(L("Error parsing PrusaSlicer config file, it is probably corrupted. "
"Try to manualy delete the file to recover from the error. Your user profiles will not be affected.")) +
"\n\n" + AppConfig::config_path() + "\n\n" + ex.what());
}
// 2) Parse the property_tree, extract the sections and key / value pairs.
for (const auto &section : tree) {
@ -227,6 +241,33 @@ std::string AppConfig::get_last_dir() const
return std::string();
}
std::vector<std::string> AppConfig::get_recent_projects() const
{
std::vector<std::string> ret;
const auto it = m_storage.find("recent_projects");
if (it != m_storage.end())
{
for (const std::map<std::string, std::string>::value_type& item : it->second)
{
ret.push_back(item.second);
}
}
return ret;
}
void AppConfig::set_recent_projects(const std::vector<std::string>& recent_projects)
{
auto it = m_storage.find("recent_projects");
if (it == m_storage.end())
it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type("recent_projects", std::map<std::string, std::string>())).first;
it->second.clear();
for (unsigned int i = 0; i < (unsigned int)recent_projects.size(); ++i)
{
it->second[std::to_string(i + 1)] = recent_projects[i];
}
}
void AppConfig::update_config_dir(const std::string &dir)
{
this->set("recent", "config_directory", dir);

View file

@ -6,7 +6,7 @@
#include <string>
#include "libslic3r/Config.hpp"
#include "slic3r/Utils/Semver.hpp"
#include "libslic3r/Semver.hpp"
namespace Slic3r {
@ -122,6 +122,9 @@ public:
// Does the config file exist?
static bool exists();
std::vector<std::string> get_recent_projects() const;
void set_recent_projects(const std::vector<std::string>& recent_projects);
private:
// Map of section, name -> value
std::map<std::string, std::map<std::string, std::string>> m_storage;

View file

@ -89,7 +89,7 @@ void BackgroundSlicingProcess::process_fff()
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
if (copy_file(m_temp_output_path, export_path) != 0)
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed")));
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());
@ -151,7 +151,12 @@ void BackgroundSlicingProcess::thread_proc()
} catch (CanceledException & /* ex */) {
// Canceled, this is all right.
assert(m_print->canceled());
} catch (std::exception &ex) {
} catch (const std::bad_alloc& ex) {
wxString errmsg = wxString::Format(_(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it.")), SLIC3R_APP_NAME);
error = errmsg.ToStdString() + "\n\n" + std::string(ex.what());
} catch (std::exception &ex) {
error = ex.what();
} catch (...) {
error = "Unknown C++ exception.";

View file

@ -4,23 +4,25 @@
#include <wx/numformatter.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
#include <wx/tooltip.h>
#include "libslic3r/BoundingBox.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/Polygon.hpp"
#include "boost/nowide/iostream.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <algorithm>
namespace Slic3r {
namespace GUI {
void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
void BedShapeDialog::build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
{
SetFont(wxGetApp().normal_font());
m_panel = new BedShapePanel(this);
m_panel->build_panel(default_pt);
m_panel->build_panel(default_pt, custom_texture, custom_model);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_panel, 1, wxEXPAND);
@ -30,11 +32,9 @@ void BedShapeDialog::build_dialog(ConfigOptionPoints* default_pt)
SetMinSize(GetSize());
main_sizer->SetSizeHints(this);
// needed to actually free memory
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent e) {
EndModal(wxID_OK);
Destroy();
}));
this->Bind(wxEVT_CLOSE_WINDOW, ([this](wxCloseEvent& evt) {
EndModal(wxID_CANCEL);
}));
}
void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
@ -53,75 +53,88 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
Refresh();
}
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
{
// on_change(nullptr);
const std::string BedShapePanel::NONE = "None";
const std::string BedShapePanel::EMPTY_STRING = "";
auto box = new wxStaticBox(this, wxID_ANY, _(L("Shape")));
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model)
{
m_shape = default_pt.values;
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
// shape options
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
sbsizer->Add(m_shape_options_book);
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
sbsizer->Add(m_shape_options_book);
auto optgroup = init_shape_options_page(_(L("Rectangular")));
ConfigOptionDef def;
def.type = coPoints;
def.set_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);
ConfigOptionDef def;
def.type = coPoints;
def.set_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.set_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);
def.type = coPoints;
def.set_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.set_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("Circular")));
def.type = coFloat;
def.set_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);
optgroup = init_shape_options_page(_(L("Custom")));
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
shape_sizer->Add(shape_btn, 1, wxEXPAND);
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
{
load_stl();
}));
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(shape_sizer, 1, wxEXPAND);
return sizer;
};
optgroup->append_line(line);
shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_stl();
}));
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
{
return sizer;
};
optgroup->append_line(line);
wxPanel* texture_panel = init_texture_panel();
wxPanel* model_panel = init_model_panel();
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;
m_canvas->Bind(wxEVT_PAINT, [this](wxPaintEvent& e) { m_canvas->repaint(m_shape); });
m_canvas->Bind(wxEVT_SIZE, [this](wxSizeEvent& e) { m_canvas->Refresh(); });
// 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) ;
wxSizer* left_sizer = new wxBoxSizer(wxVERTICAL);
left_sizer->Add(sbsizer, 0, wxEXPAND);
left_sizer->Add(texture_panel, 1, wxEXPAND);
left_sizer->Add(model_panel, 1, wxEXPAND);
wxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(left_sizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10);
SetSizerAndFit(top_sizer);
@ -135,23 +148,157 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// Called from the constructor.
// Create a panel for a rectangular / circular / custom bed shape.
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title)
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
{
auto panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup;
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
wxPanel* panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
optgroup->label_width = 10;
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);
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;
return optgroup;
}
wxPanel* BedShapePanel::init_texture_panel()
{
wxPanel* panel = new wxPanel(this);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Texture")));
optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND);
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(filename_sizer, 1, wxEXPAND);
sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_texture();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
m_custom_texture = NONE;
update_shape();
}));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.SetText(_(boost::filesystem::path(m_custom_texture).filename().string()));
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_texture == NONE) ? "" : _(m_custom_texture);
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
}
}));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.Enable(m_custom_texture != NONE);
}));
return sizer;
};
optgroup->append_line(line);
panel->SetSizerAndFit(optgroup->sizer);
return panel;
}
wxPanel* BedShapePanel::init_model_panel()
{
wxPanel* panel = new wxPanel(this);
ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Model")));
optgroup->label_width = 10;
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
update_shape();
};
Line line{ "", "" };
line.full_width = 1;
line.widget = [this](wxWindow* parent) {
wxButton* load_btn = new wxButton(parent, wxID_ANY, _(L("Load...")));
wxSizer* load_sizer = new wxBoxSizer(wxHORIZONTAL);
load_sizer->Add(load_btn, 1, wxEXPAND);
wxStaticText* filename_lbl = new wxStaticText(parent, wxID_ANY, _(NONE));
wxSizer* filename_sizer = new wxBoxSizer(wxHORIZONTAL);
filename_sizer->Add(filename_lbl, 1, wxEXPAND);
wxButton* remove_btn = new wxButton(parent, wxID_ANY, _(L("Remove")));
wxSizer* remove_sizer = new wxBoxSizer(wxHORIZONTAL);
remove_sizer->Add(remove_btn, 1, wxEXPAND);
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(filename_sizer, 1, wxEXPAND);
sizer->Add(load_sizer, 1, wxEXPAND);
sizer->Add(remove_sizer, 1, wxEXPAND);
load_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
load_model();
}));
remove_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{
m_custom_model = NONE;
update_shape();
}));
filename_lbl->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.SetText(_(boost::filesystem::path(m_custom_model).filename().string()));
wxStaticText* lbl = dynamic_cast<wxStaticText*>(e.GetEventObject());
if (lbl != nullptr)
{
wxString tooltip_text = (m_custom_model == NONE) ? "" : _(m_custom_model);
wxToolTip* tooltip = lbl->GetToolTip();
if ((tooltip == nullptr) || (tooltip->GetTip() != tooltip_text))
lbl->SetToolTip(tooltip_text);
}
}));
remove_btn->Bind(wxEVT_UPDATE_UI, ([this](wxUpdateUIEvent& e)
{
e.Enable(m_custom_model != NONE);
}));
return sizer;
};
optgroup->append_line(line);
panel->SetSizerAndFit(optgroup->sizer);
return panel;
}
// Called from the constructor.
@ -159,20 +306,20 @@ ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(wxString title)
// 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)
void BedShapePanel::set_shape(const ConfigOptionPoints& points)
{
auto polygon = Polygon::new_scale(points->values);
auto polygon = Polygon::new_scale(points.values);
// is this a rectangle ?
if (points->size() == 4) {
auto lines = polygon.lines();
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_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));
@ -223,8 +370,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
}
}
if (points->size() < 3) {
// Invalid polygon.Revert to default bed dimensions.
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) });
@ -236,8 +383,8 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
// 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();
m_loaded_shape = points.values;
update_shape();
}
void BedShapePanel::update_preview()
@ -281,11 +428,11 @@ void BedShapePanel::update_shape()
x1 -= dx;
y0 -= dy;
y1 -= dy;
m_canvas->m_bed_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1)};
}
m_shape = { Vec2d(x0, y0),
Vec2d(x1, y0),
Vec2d(x1, y1),
Vec2d(x0, y1) };
}
else if(page_idx == SHAPE_CIRCULAR) {
double diameter;
try{
@ -297,43 +444,44 @@ void BedShapePanel::update_shape()
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;
auto edges = 72;
std::vector<Vec2d> points;
for (size_t i = 1; i <= edges; ++i) {
auto angle = i * twopi / edges;
points.push_back(Vec2d(r*cos(angle), r*sin(angle)));
}
m_canvas->m_bed_shape = points;
}
m_shape = points;
}
else if (page_idx == SHAPE_CUSTOM)
m_shape = m_loaded_shape;
// $self->{on_change}->();
update_preview();
}
// Loads an stl file, projects it to the XY plane and calculates a polygon.
void BedShapePanel::load_stl()
{
auto dialog = new wxFileDialog(this, _(L("Choose a file to import bed shape from (STL/OBJ/AMF/3MF/PRUSA):")), "", "",
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog->ShowModal() != wxID_OK) {
dialog->Destroy();
return;
}
wxArrayString input_file;
dialog->GetPaths(input_file);
dialog->Destroy();
wxFileDialog dialog(this, _(L("Choose an STL file to import bed shape from:")), "", "", file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
std::string file_name = input_file[0].ToUTF8().data();
std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".stl"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
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);
model = Model::read_from_file(file_name);
}
catch (std::exception &) {
show_error(this, _(L("Error! Invalid model")));
return;
}
auto mesh = model.mesh();
auto expolygons = mesh.horizontal_projection();
@ -351,8 +499,55 @@ void BedShapePanel::load_stl()
std::vector<Vec2d> points;
for (auto pt : polygon.points)
points.push_back(unscale(pt));
m_canvas->m_bed_shape = points;
update_preview();
m_loaded_shape = points;
update_shape();
}
void BedShapePanel::load_texture()
{
wxFileDialog dialog(this, _(L("Choose a file to import bed texture from (PNG/SVG):")), "", "",
file_wildcards(FT_TEX), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
m_custom_texture = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".png") && !boost::algorithm::iends_with(file_name, ".svg"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
m_custom_texture = file_name;
update_shape();
}
void BedShapePanel::load_model()
{
wxFileDialog dialog(this, _(L("Choose an STL file to import bed model from:")), "", "",
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
m_custom_model = NONE;
std::string file_name = dialog.GetPath().ToUTF8().data();
if (!boost::algorithm::iends_with(file_name, ".stl"))
{
show_error(this, _(L("Invalid file format.")));
return;
}
wxBusyCursor wait;
m_custom_model = file_name;
update_shape();
}
} // GUI

View file

@ -16,26 +16,40 @@ namespace GUI {
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
class BedShapePanel : public wxPanel
{
Bed_2D* m_canvas;
static const std::string NONE;
static const std::string EMPTY_STRING;
Bed_2D* m_canvas;
std::vector<Vec2d> m_shape;
std::vector<Vec2d> m_loaded_shape;
std::string m_custom_texture;
std::string m_custom_model;
public:
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {}
~BedShapePanel() {}
BedShapePanel(wxWindow* parent) : wxPanel(parent, wxID_ANY), m_custom_texture(NONE), m_custom_model(NONE) {}
void build_panel(ConfigOptionPoints* default_pt);
ConfigOptionsGroupShp init_shape_options_page(wxString title);
void set_shape(ConfigOptionPoints* points);
void update_preview();
void build_panel(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
// Returns the resulting bed shape polygon. This value will be stored to the ini file.
const std::vector<Vec2d>& get_shape() const { return m_shape; }
const std::string& get_custom_texture() const { return (m_custom_texture != NONE) ? m_custom_texture : EMPTY_STRING; }
const std::string& get_custom_model() const { return (m_custom_model != NONE) ? m_custom_model : EMPTY_STRING; }
private:
ConfigOptionsGroupShp init_shape_options_page(const wxString& title);
wxPanel* init_texture_panel();
wxPanel* init_model_panel();
void set_shape(const 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; }
void load_texture();
void load_model();
wxChoicebook* m_shape_options_book;
std::vector <ConfigOptionsGroupShp> m_optgroups;
friend class BedShapeDialog;
};
class BedShapeDialog : public DPIDialog
@ -44,10 +58,12 @@ class BedShapeDialog : public DPIDialog
public:
BedShapeDialog(wxWindow* parent) : DPIDialog(parent, wxID_ANY, _(L("Bed Shape")),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {}
~BedShapeDialog() {}
void build_dialog(ConfigOptionPoints* default_pt);
std::vector<Vec2d> GetValue() { return m_panel->GetValue(); }
void build_dialog(const ConfigOptionPoints& default_pt, const ConfigOptionString& custom_texture, const ConfigOptionString& custom_model);
const std::vector<Vec2d>& get_shape() const { return m_panel->get_shape(); }
const std::string& get_custom_texture() const { return m_panel->get_custom_texture(); }
const std::string& get_custom_model() const { return m_panel->get_custom_model(); }
protected:
void on_dpi_changed(const wxRect &suggested_rect) override;

View file

@ -52,7 +52,7 @@ struct LifetimeGuard
};
BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxRESIZE_BORDER)
: wxDialog(parent, wxID_ANY, _(L("Network lookup")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
, list(new wxListView(this, wxID_ANY))
, replies(new ReplySet)
, label(new wxStaticText(this, wxID_ANY, ""))
@ -171,7 +171,7 @@ void BonjourDialog::on_reply(BonjourReplyEvent &e)
// Filter replies based on selected technology
const auto model = e.reply.txt_data.find("model");
const bool sl1 = model != e.reply.txt_data.end() && model->second == "SL1";
if (tech == ptFFF && sl1 || tech == ptSLA && !sl1) {
if ((tech == ptFFF && sl1) || (tech == ptSLA && !sl1)) {
return;
}

View file

@ -2,6 +2,8 @@
#include "Camera.hpp"
#include "3DScene.hpp"
#include "GUI_App.hpp"
#include "AppConfig.hpp"
#include <GL/glew.h>
@ -19,32 +21,68 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
namespace Slic3r {
namespace GUI {
const double Camera::DefaultDistance = 1000.0;
double Camera::FrustrumMinZRange = 50.0;
double Camera::FrustrumMinNearZ = 100.0;
double Camera::FrustrumZMargin = 10.0;
double Camera::MaxFovDeg = 60.0;
Camera::Camera()
: type(Ortho)
, zoom(1.0f)
, phi(45.0f)
// , distance(0.0f)
: phi(45.0f)
, requires_zoom_to_bed(false)
, inverted_phi(false)
, m_theta(45.0f)
, m_type(Perspective)
, m_target(Vec3d::Zero())
, m_theta(45.0f)
, m_zoom(1.0)
, m_distance(DefaultDistance)
, m_gui_scale(1.0)
, m_view_matrix(Transform3d::Identity())
, m_projection_matrix(Transform3d::Identity())
{
}
std::string Camera::get_type_as_string() const
{
switch (type)
switch (m_type)
{
default:
case Unknown:
return "unknown";
// case Perspective:
// return "perspective";
case Perspective:
return "perspective";
default:
case Ortho:
return "ortho";
return "orthographic";
};
}
void Camera::set_type(EType type)
{
if (m_type != type)
{
m_type = type;
wxGetApp().app_config->set("use_perspective_camera", (m_type == Perspective) ? "1" : "0");
wxGetApp().app_config->save();
}
}
void Camera::set_type(const std::string& type)
{
if (type == "1")
set_type(Perspective);
else
set_type(Ortho);
}
void Camera::select_next_type()
{
unsigned char next = (unsigned char)m_type + 1;
if (next == (unsigned char)Num_types)
next = 1;
set_type((EType)next);
}
void Camera::set_target(const Vec3d& target)
{
m_target = target;
@ -65,9 +103,20 @@ void Camera::set_theta(float theta, bool apply_limit)
}
}
void Camera::set_scene_box(const BoundingBoxf3& box)
void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h)
{
m_scene_box = box;
zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0;
zoom = m_zoom / (1.0 - zoom);
// Don't allow to zoom too far outside the scene.
double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h);
if (zoom_min > 0.0)
zoom = std::max(zoom, zoom_min * 0.7);
// Don't allow to zoom too close to the scene.
zoom = std::min(zoom, 100.0);
m_zoom = zoom;
}
bool Camera::select_view(const std::string& direction)
@ -99,6 +148,18 @@ bool Camera::select_view(const std::string& direction)
return false;
}
double Camera::get_fov() const
{
switch (m_type)
{
case Perspective:
return 2.0 * Geometry::rad2deg(std::atan(1.0 / m_projection_matrix.matrix()(1, 1)));
default:
case Ortho:
return 0.0;
};
}
void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
{
glsafe(::glViewport(0, 0, w, h));
@ -107,27 +168,275 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
void Camera::apply_view_matrix() const
{
double theta_rad = Geometry::deg2rad(-(double)m_theta);
double phi_rad = Geometry::deg2rad((double)phi);
double sin_theta = ::sin(theta_rad);
Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
glsafe(::glMatrixMode(GL_MODELVIEW));
glsafe(::glLoadIdentity());
glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch
glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
glsafe(::glTranslated(-m_target(0), -m_target(1), -m_target(2)));
glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
glsafe(::glTranslated(-camera_pos(0), -camera_pos(1), -camera_pos(2)));
glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data()));
}
void Camera::apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const
void Camera::apply_projection(const BoundingBoxf3& box) const
{
set_distance(DefaultDistance);
double w = 0.0;
double h = 0.0;
while (true)
{
m_frustrum_zs = calc_tight_frustrum_zs_around(box);
w = 0.5 * (double)m_viewport[2];
h = 0.5 * (double)m_viewport[3];
if (m_zoom != 0.0)
{
double inv_zoom = 1.0 / m_zoom;
w *= inv_zoom;
h *= inv_zoom;
}
switch (m_type)
{
default:
case Ortho:
{
m_gui_scale = 1.0;
break;
}
case Perspective:
{
// scale near plane to keep w and h constant on the plane at z = m_distance
double scale = m_frustrum_zs.first / m_distance;
w *= scale;
h *= scale;
m_gui_scale = scale;
break;
}
}
if (m_type == Perspective)
{
double fov_deg = Geometry::rad2deg(2.0 * std::atan(h / m_frustrum_zs.first));
// adjust camera distance to keep fov in a limited range
if (fov_deg > MaxFovDeg)
{
double delta_z = h / ::tan(0.5 * Geometry::deg2rad(MaxFovDeg)) - m_frustrum_zs.first;
if (delta_z > 0.001)
set_distance(m_distance + delta_z);
else
break;
}
else
break;
}
else
break;
}
glsafe(::glMatrixMode(GL_PROJECTION));
glsafe(::glLoadIdentity());
glsafe(::glOrtho(x_min, x_max, y_min, y_max, z_min, z_max));
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
switch (m_type)
{
default:
case Ortho:
{
glsafe(::glOrtho(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
break;
}
case Perspective:
{
glsafe(::glFrustum(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second));
break;
}
}
glsafe(::glGetDoublev(GL_PROJECTION_MATRIX, m_projection_matrix.data()));
glsafe(::glMatrixMode(GL_MODELVIEW));
}
void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
{
// Calculate the zoom factor needed to adjust the view around the given box.
double zoom = calc_zoom_to_bounding_box_factor(box, canvas_w, canvas_h);
if (zoom > 0.0)
{
m_zoom = zoom;
// center view around box center
m_target = box.center();
}
}
#if ENABLE_CAMERA_STATISTICS
void Camera::debug_render() const
{
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_bg_alpha(0.5f);
imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
std::string type = get_type_as_string();
Vec3f position = get_position().cast<float>();
Vec3f target = m_target.cast<float>();
float distance = (float)get_distance();
Vec3f forward = get_dir_forward().cast<float>();
Vec3f right = get_dir_right().cast<float>();
Vec3f up = get_dir_up().cast<float>();
float nearZ = (float)m_frustrum_zs.first;
float farZ = (float)m_frustrum_zs.second;
float deltaZ = farZ - nearZ;
float zoom = (float)m_zoom;
float fov = (float)get_fov();
float gui_scale = (float)get_gui_scale();
ImGui::InputText("Type", const_cast<char*>(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Distance", &distance, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat3("Forward", forward.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Right", right.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Up", up.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat("Near Z", &nearZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Far Z", &farZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Delta Z", &deltaZ, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat("Zoom", &zoom, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat("Fov", &fov, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat("GUI scale", &gui_scale, 0.0f, 0.0f, "%.6f", ImGuiInputTextFlags_ReadOnly);
imgui.end();
}
#endif // ENABLE_CAMERA_STATISTICS
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const
{
std::pair<double, double> ret;
while (true)
{
ret = std::make_pair(DBL_MAX, -DBL_MAX);
// box vertices in world space
std::vector<Vec3d> vertices;
vertices.reserve(8);
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
// set the Z range in eye coordinates (negative Zs are in front of the camera)
for (const Vec3d& v : vertices)
{
double z = -(m_view_matrix * v)(2);
ret.first = std::min(ret.first, z);
ret.second = std::max(ret.second, z);
}
// apply margin
ret.first -= FrustrumZMargin;
ret.second += FrustrumZMargin;
// ensure min size
if (ret.second - ret.first < FrustrumMinZRange)
{
double mid_z = 0.5 * (ret.first + ret.second);
double half_size = 0.5 * FrustrumMinZRange;
ret.first = mid_z - half_size;
ret.second = mid_z + half_size;
}
if (ret.first >= FrustrumMinNearZ)
break;
// ensure min Near Z
set_distance(m_distance + FrustrumMinNearZ - ret.first);
}
return ret;
}
double Camera::calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const
{
double max_bb_size = box.max_size();
if (max_bb_size == 0.0)
return -1.0;
// project the box vertices on a plane perpendicular to the camera forward axis
// then calculates the vertices coordinate on this plane along the camera xy axes
// ensure that the view matrix is updated
apply_view_matrix();
Vec3d right = get_dir_right();
Vec3d up = get_dir_up();
Vec3d forward = get_dir_forward();
Vec3d bb_center = box.center();
// box vertices in world space
std::vector<Vec3d> vertices;
vertices.reserve(8);
vertices.push_back(box.min);
vertices.emplace_back(box.max(0), box.min(1), box.min(2));
vertices.emplace_back(box.max(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.max(1), box.min(2));
vertices.emplace_back(box.min(0), box.min(1), box.max(2));
vertices.emplace_back(box.max(0), box.min(1), box.max(2));
vertices.push_back(box.max);
vertices.emplace_back(box.min(0), box.max(1), box.max(2));
double max_x = 0.0;
double max_y = 0.0;
// margin factor to give some empty space around the box
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, std::abs(x_on_plane));
max_y = std::max(max_y, std::abs(y_on_plane));
}
if ((max_x == 0.0) || (max_y == 0.0))
return -1.0f;
max_x *= margin_factor;
max_y *= margin_factor;
return std::min((double)canvas_w / (2.0 * max_x), (double)canvas_h / (2.0 * max_y));
}
void Camera::set_distance(double distance) const
{
m_distance = distance;
apply_view_matrix();
}
} // GUI
} // Slic3r

View file

@ -9,44 +9,65 @@ namespace GUI {
struct Camera
{
static const double DefaultDistance;
static double FrustrumMinZRange;
static double FrustrumMinNearZ;
static double FrustrumZMargin;
static double MaxFovDeg;
enum EType : unsigned char
{
Unknown,
// Perspective,
Ortho,
Perspective,
Num_types
};
EType type;
float zoom;
float phi;
// float distance;
bool requires_zoom_to_bed;
bool inverted_phi;
private:
EType m_type;
Vec3d m_target;
float m_theta;
double m_zoom;
// Distance between camera position and camera target measured along the camera Z axis
mutable double m_distance;
mutable double m_gui_scale;
mutable std::array<int, 4> m_viewport;
mutable Transform3d m_view_matrix;
mutable Transform3d m_projection_matrix;
mutable std::pair<double, double> m_frustrum_zs;
BoundingBoxf3 m_scene_box;
public:
Camera();
EType get_type() const { return m_type; }
std::string get_type_as_string() const;
void set_type(EType type);
// valid values for type: "0" -> ortho, "1" -> perspective
void set_type(const std::string& type);
void select_next_type();
const Vec3d& get_target() const { return m_target; }
void set_target(const Vec3d& target);
double get_distance() const { return m_distance; }
double get_gui_scale() const { return m_gui_scale; }
float get_theta() const { return m_theta; }
void set_theta(float theta, bool apply_limit);
double get_zoom() const { return m_zoom; }
void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
void set_zoom(double zoom) { m_zoom = zoom; }
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
void set_scene_box(const BoundingBoxf3& box);
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
bool select_view(const std::string& direction);
@ -56,13 +77,31 @@ public:
Vec3d get_dir_right() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(0); }
Vec3d get_dir_up() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(1); }
Vec3d get_dir_forward() const { return m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
Vec3d get_dir_forward() const { return -m_view_matrix.matrix().block(0, 0, 3, 3).row(2); }
Vec3d get_position() const { return m_view_matrix.matrix().block(0, 3, 3, 1); }
Vec3d get_position() const { return m_view_matrix.matrix().inverse().block(0, 3, 3, 1); }
double get_near_z() const { return m_frustrum_zs.first; }
double get_far_z() const { return m_frustrum_zs.second; }
double get_fov() const;
void apply_viewport(int x, int y, unsigned int w, unsigned int h) const;
void apply_view_matrix() const;
void apply_ortho_projection(float x_min, float x_max, float y_min, float y_max, float z_min, float z_max) const;
void apply_projection(const BoundingBoxf3& box) const;
void zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h);
#if ENABLE_CAMERA_STATISTICS
void debug_render() const;
#endif // ENABLE_CAMERA_STATISTICS
private:
// returns tight values for nearZ and farZ plane around the given bounding box
// the camera MUST be outside of the bounding box in eye coordinate of the given box
std::pair<double, double> calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const;
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const;
void set_distance(double distance) const;
};
} // GUI

View file

@ -25,6 +25,7 @@
#include "PresetBundle.hpp"
#include "GUI.hpp"
#include "GUI_Utils.hpp"
#include "slic3r/Config/Snapshot.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
@ -32,6 +33,10 @@ namespace Slic3r {
namespace GUI {
using Config::Snapshot;
using Config::SnapshotDB;
// Printer model picker GUI control
struct PrinterPickerEvent : public wxEvent
@ -330,8 +335,8 @@ PagePrinters::PagePrinters(ConfigWizard *parent, wxString title, wxString shortn
const auto families = vendor.families();
for (const auto &family : families) {
const auto filter = [&](const VendorProfile::PrinterModel &model) {
return (model.technology == ptFFF && technology & T_FFF
|| model.technology == ptSLA && technology & T_SLA)
return ((model.technology == ptFFF && technology & T_FFF)
|| (model.technology == ptSLA && technology & T_SLA))
&& model.family == family;
};
@ -532,15 +537,21 @@ PageBedShape::PageBedShape(ConfigWizard *parent)
{
append_text(_(L("Set the shape of your printer's bed.")));
shape_panel->build_panel(wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"));
shape_panel->build_panel(*wizard_p()->custom_config->option<ConfigOptionPoints>("bed_shape"),
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_texture"),
*wizard_p()->custom_config->option<ConfigOptionString>("bed_custom_model"));
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);
const std::vector<Vec2d>& points = shape_panel->get_shape();
const std::string& custom_texture = shape_panel->get_custom_texture();
const std::string& custom_model = shape_panel->get_custom_model();
config.set_key_value("bed_shape", new ConfigOptionPoints(points));
config.set_key_value("bed_custom_texture", new ConfigOptionString(custom_texture));
config.set_key_value("bed_custom_model", new ConfigOptionString(custom_model));
}
PageDiameters::PageDiameters(ConfigWizard *parent)
@ -810,7 +821,7 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
const Item& item = items[i];
unsigned x = em_w/2 + item.indent * em_w;
if (i == item_active || item_hover >= 0 && i == (size_t)item_hover) {
if (i == item_active || (item_hover >= 0 && i == (size_t)item_hover)) {
dc.DrawBitmap(bullet_blue.bmp(), x, y + yoff_icon, false);
}
else if (i < item_active) { dc.DrawBitmap(bullet_black.bmp(), x, y + yoff_icon, false); }
@ -1019,15 +1030,33 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
// Decide whether to create snapshot based on run_reason and the reset profile checkbox
bool snapshot = true;
Snapshot::Reason snapshot_reason = Snapshot::SNAPSHOT_UPGRADE;
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;
case ConfigWizard::RR_DATA_EMPTY:
snapshot = false;
break;
case ConfigWizard::RR_DATA_LEGACY:
snapshot = true;
break;
case ConfigWizard::RR_DATA_INCOMPAT:
// In this case snapshot has already been taken by
// PresetUpdater with the appropriate reason
snapshot = false;
break;
case ConfigWizard::RR_USER:
snapshot = page_welcome->reset_user_profile();
snapshot_reason = Snapshot::SNAPSHOT_USER;
break;
}
if (snapshot) {
SnapshotDB::singleton().take_snapshot(*app_config, snapshot_reason);
}
if (install_bundles.size() > 0) {
// Install bundles from resources.
updater->install_bundles_rsrc(std::move(install_bundles), snapshot);
// Don't create snapshot - we've already done that above if applicable.
updater->install_bundles_rsrc(std::move(install_bundles), false);
} else {
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
}
@ -1086,7 +1115,7 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason)
p->load_vendors();
p->custom_config.reset(DynamicPrintConfig::new_from_defaults_keys({
"gcode_flavor", "bed_shape", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
"gcode_flavor", "bed_shape", "bed_custom_texture", "bed_custom_model", "nozzle_diameter", "filament_diameter", "temperature", "bed_temperature",
}));
p->index = new ConfigWizardIndex(this);

View file

@ -113,11 +113,19 @@ wxString Field::get_tooltip_text(const wxString& default_string)
wxString tooltip_text("");
wxString tooltip = _(m_opt.tooltip);
edit_tooltip(tooltip);
std::string opt_id = m_opt_id;
auto hash_pos = opt_id.find("#");
if (hash_pos != std::string::npos) {
opt_id.replace(hash_pos, 1,"[");
opt_id += "]";
}
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;
(boost::iends_with(opt_id, "_gcode") ? "\n" : "") + default_string +
(boost::iends_with(opt_id, "_gcode") ? "" : "\n") +
_(L("parameter name")) + "\t: " + opt_id;
return tooltip_text;
}
@ -128,6 +136,8 @@ bool Field::is_matched(const std::string& string, const std::string& pattern)
return std::regex_match(string, regex_pattern);
}
static wxString na_value() { return _(L("N/A")); }
void Field::get_value_by_opt_type(wxString& str)
{
switch (m_opt.type) {
@ -157,7 +167,9 @@ void Field::get_value_by_opt_type(wxString& str)
val = 0.0;
else
{
if (!str.ToCDouble(&val))
if (m_opt.nullable && str == na_value())
val = ConfigOptionFloatsNullable::nil_value();
else if (!str.ToCDouble(&val))
{
show_error(m_parent, _(L("Invalid numeric input.")));
set_value(double_to_string(val), true);
@ -185,17 +197,18 @@ void Field::get_value_by_opt_type(wxString& str)
show_error(m_parent, _(L("Invalid numeric input.")));
set_value(double_to_string(val), true);
}
else if (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max ||
m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)
else if ((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max ||
m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1) &&
(m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast<std::string>(m_value)))
{
std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm";
const int nVal = int(val);
wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %d %s?\n"
"Select YES if you want to change this value to %d%%, \n"
"or NO if you are sure that %d %s is a correct value.")), nVal, nVal, sidetext, nVal, nVal, sidetext);
auto dialog = new wxMessageDialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO);
if (dialog->ShowModal() == wxID_YES) {
set_value(wxString::Format("%s%%", str), true);
const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm";
const wxString stVal = double_to_string(val, 2);
const wxString msg_text = wxString::Format(_(L("Do you mean %s%% instead of %s %s?\n"
"Select YES if you want to change this value to %s%%, \n"
"or NO if you are sure that %s %s is a correct value.")), stVal, stVal, sidetext, stVal, stVal, sidetext);
wxMessageDialog dialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO);
if (dialog.ShowModal() == wxID_YES) {
set_value(wxString::Format("%s%%", stVal), false/*true*/);
str += "%%";
}
}
@ -247,6 +260,7 @@ void TextCtrl::BUILD() {
m_opt.default_value->getFloat() :
m_opt.get_default_value<ConfigOptionPercents>()->get_at(m_opt_idx);
text_value = double_to_string(val);
m_last_meaningful_value = text_value;
break;
}
case coString:
@ -316,24 +330,7 @@ void TextCtrl::BUILD() {
}
propagate_value();
}), temp->GetId());
/*
temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt)
{
#ifdef __WXGTK__
if (bChangedValueEvent)
#endif //__WXGTK__
if(is_defined_input_value())
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)
{
@ -346,18 +343,80 @@ void TextCtrl::BUILD() {
window = dynamic_cast<wxWindow*>(temp);
}
bool TextCtrl::value_was_changed()
{
if (m_value.empty())
return true;
boost::any val = m_value;
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
// update m_value!
get_value_by_opt_type(ret_str);
switch (m_opt.type) {
case coInt:
return boost::any_cast<int>(m_value) != boost::any_cast<int>(val);
case coPercent:
case coPercents:
case coFloats:
case coFloat: {
if (m_opt.nullable && std::isnan(boost::any_cast<double>(m_value)) &&
std::isnan(boost::any_cast<double>(val)))
return false;
return boost::any_cast<double>(m_value) != boost::any_cast<double>(val);
}
case coString:
case coStrings:
case coFloatOrPercent:
return boost::any_cast<std::string>(m_value) != boost::any_cast<std::string>(val);
default:
return true;
}
}
void TextCtrl::propagate_value()
{
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type))
if (is_defined_input_value<wxTextCtrl>(window, m_opt.type) && value_was_changed())
on_change_field();
else
on_kill_focus();
}
void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/) {
m_disable_change_event = !change_event;
if (m_opt.nullable) {
const bool m_is_na_val = boost::any_cast<wxString>(value) == na_value();
if (!m_is_na_val)
m_last_meaningful_value = value;
dynamic_cast<wxTextCtrl*>(window)->SetValue(m_is_na_val ? na_value() : boost::any_cast<wxString>(value));
}
else
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(value));
m_disable_change_event = false;
if (!change_event) {
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
// update m_value to correct work of next value_was_changed()
get_value_by_opt_type(ret_str);
}
}
void TextCtrl::set_last_meaningful_value()
{
dynamic_cast<wxTextCtrl*>(window)->SetValue(boost::any_cast<wxString>(m_last_meaningful_value));
propagate_value();
}
void TextCtrl::set_na_value()
{
dynamic_cast<wxTextCtrl*>(window)->SetValue(na_value());
propagate_value();
}
boost::any& TextCtrl::get_value()
{
wxString ret_str = static_cast<wxTextCtrl*>(window)->GetValue();
// modifies ret_string!
// update m_value
get_value_by_opt_type(ret_str);
return m_value;
@ -400,6 +459,8 @@ void CheckBox::BUILD() {
m_opt.get_default_value<ConfigOptionBools>()->get_at(m_opt_idx) :
false;
m_last_meaningful_value = static_cast<unsigned char>(check_value);
// Set Label as a string of at least one space simbol to correct system scaling of a CheckBox
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(" "), wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@ -407,7 +468,10 @@ void CheckBox::BUILD() {
temp->SetValue(check_value);
if (m_opt.readonly) temp->Disable();
temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId());
temp->Bind(wxEVT_CHECKBOX, ([this](wxCommandEvent e) {
m_is_na_val = false;
on_change_field();
}), temp->GetId());
temp->SetToolTip(get_tooltip_text(check_value ? "true" : "false"));
@ -415,6 +479,38 @@ void CheckBox::BUILD() {
window = dynamic_cast<wxWindow*>(temp);
}
void CheckBox::set_value(const boost::any& value, bool change_event)
{
m_disable_change_event = !change_event;
if (m_opt.nullable) {
m_is_na_val = boost::any_cast<unsigned char>(value) == ConfigOptionBoolsNullable::nil_value();
if (!m_is_na_val)
m_last_meaningful_value = value;
dynamic_cast<wxCheckBox*>(window)->SetValue(m_is_na_val ? false : boost::any_cast<unsigned char>(value) != 0);
}
else
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<bool>(value));
m_disable_change_event = false;
}
void CheckBox::set_last_meaningful_value()
{
if (m_opt.nullable) {
m_is_na_val = false;
dynamic_cast<wxCheckBox*>(window)->SetValue(boost::any_cast<unsigned char>(m_last_meaningful_value) != 0);
on_change_field();
}
}
void CheckBox::set_na_value()
{
if (m_opt.nullable) {
m_is_na_val = true;
dynamic_cast<wxCheckBox*>(window)->SetValue(false);
on_change_field();
}
}
boost::any& CheckBox::get_value()
{
// boost::any m_value;
@ -422,7 +518,7 @@ boost::any& CheckBox::get_value()
if (m_opt.type == coBool)
m_value = static_cast<bool>(value);
else
m_value = static_cast<unsigned char>(value);
m_value = m_is_na_val ? ConfigOptionBoolsNullable::nil_value() : static_cast<unsigned char>(value);
return m_value;
}
@ -463,7 +559,16 @@ void SpinCtrl::BUILD() {
break;
}
const int min_val = m_opt.min == INT_MIN ? 0: m_opt.min;
const int min_val = m_opt.min == INT_MIN
#ifdef __WXOSX__
// We will forcibly set the input value for SpinControl, since the value
// inserted from the keyboard is not updated under OSX.
// So, we can't set min control value bigger then 0.
// Otherwise, it couldn't be possible to input from keyboard value
// less then min_val.
|| m_opt.min > 0
#endif
? 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,
@ -535,6 +640,14 @@ void SpinCtrl::propagate_value()
if (tmp_value == UNDEF_VALUE) {
on_kill_focus();
} else {
#ifdef __WXOSX__
// check input value for minimum
if (m_opt.min > 0 && tmp_value < m_opt.min) {
wxSpinCtrl* spin = static_cast<wxSpinCtrl*>(window);
spin->SetValue(m_opt.min);
spin->GetText()->SetInsertionPointEnd();
}
#endif
on_change_field();
}
}
@ -815,7 +928,7 @@ boost::any& Choice::get_value()
wxString ret_str = field->GetValue();
// options from right panel
std::vector <std::string> right_panel_options{ "support", "scale_unit" };
std::vector <std::string> right_panel_options{ "support", "pad", "scale_unit" };
for (auto rp_option: right_panel_options)
if (m_opt_id == rp_option)
return m_value = boost::any(ret_str);
@ -920,11 +1033,12 @@ void ColourPicker::BUILD()
// Validate the color
wxString clr_str(m_opt.get_default_value<ConfigOptionStrings>()->get_at(m_opt_idx));
wxColour clr(clr_str);
if (! clr.IsOk()) {
if (clr_str.IsEmpty() || !clr.IsOk()) {
clr = wxTransparentColour;
}
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
// // recast as a wxWindow to fit the calling convention
@ -935,17 +1049,59 @@ void ColourPicker::BUILD()
temp->SetToolTip(get_tooltip_text(clr_str));
}
void ColourPicker::set_undef_value(wxColourPickerCtrl* field)
{
field->SetColour(wxTransparentColour);
wxButton* btn = dynamic_cast<wxButton*>(field->GetPickerCtrl());
wxBitmap bmp = btn->GetBitmap();
wxMemoryDC dc(bmp);
dc.SetTextForeground(*wxWHITE);
dc.SetFont(wxGetApp().normal_font());
const wxRect rect = wxRect(0, 0, bmp.GetWidth(), bmp.GetHeight());
dc.DrawLabel("undef", rect, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL);
dc.SelectObject(wxNullBitmap);
btn->SetBitmapLabel(bmp);
}
void ColourPicker::set_value(const boost::any& value, bool change_event)
{
m_disable_change_event = !change_event;
const wxString clr_str(boost::any_cast<wxString>(value));
auto field = dynamic_cast<wxColourPickerCtrl*>(window);
wxColour clr(clr_str);
if (clr_str.IsEmpty() || !clr.IsOk())
set_undef_value(field);
else
field->SetColour(clr);
m_disable_change_event = false;
}
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();
if (colour == wxTransparentColour)
m_value = std::string("");
else {
auto clr_str = wxString::Format(wxT("#%02X%02X%02X"), colour.Red(), colour.Green(), colour.Blue());
m_value = clr_str.ToStdString();
}
return m_value;
}
void ColourPicker::msw_rescale()
{
Field::msw_rescale();
wxColourPickerCtrl* field = dynamic_cast<wxColourPickerCtrl*>(window);
if (field->GetColour() == wxTransparentColour)
set_undef_value(field);
}
void PointCtrl::BUILD()
{
auto temp = new wxBoxSizer(wxHORIZONTAL);

View file

@ -123,6 +123,8 @@ public:
/// 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;
virtual void set_last_meaningful_value() {}
virtual void set_na_value() {}
/// Gets a boost::any representing this control.
/// subclasses should overload with a specific version
@ -247,6 +249,8 @@ protected:
// current value
boost::any m_value;
// last maeningful value
boost::any m_last_meaningful_value;
int m_em_unit;
@ -277,6 +281,7 @@ public:
~TextCtrl() {}
void BUILD();
bool value_was_changed();
// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER
void propagate_value();
wxWindow* window {nullptr};
@ -286,11 +291,9 @@ public:
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;
}
virtual void set_value(const boost::any& value, bool change_event = false) override;
virtual void set_last_meaningful_value() override;
virtual void set_na_value() override;
boost::any& get_value() override;
@ -303,6 +306,7 @@ public:
class CheckBox : public Field {
using Field::Field;
bool m_is_na_val {false};
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) {}
@ -316,11 +320,9 @@ public:
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;
}
void set_value(const boost::any& value, bool change_event = false) override;
void set_last_meaningful_value() override;
void set_na_value() override;
boost::any& get_value() override;
void msw_rescale() override;
@ -402,6 +404,8 @@ public:
class ColourPicker : public Field {
using Field::Field;
void set_undef_value(wxColourPickerCtrl* 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) {}
@ -415,13 +419,9 @@ public:
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;
}
void set_value(const boost::any& value, bool change_event = false) override;
boost::any& get_value() override;
void msw_rescale() override;
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };

View file

@ -5,11 +5,14 @@
#include <stdexcept>
#include <boost/format.hpp>
#include <boost/asio.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/optional.hpp>
#if _WIN32
#include <regex>
#endif
#include "libslic3r/Utils.hpp"
#include "avrdude/avrdude-slic3r.hpp"
#include "GUI.hpp"
@ -104,7 +107,7 @@ struct FirmwareDialog::priv
// GUI elements
wxComboBox *port_picker;
wxStaticText *port_autodetect;
wxStaticText *txt_port_autodetect;
wxFilePickerCtrl *hex_picker;
wxStaticText *txt_status;
wxGauge *progressbar;
@ -131,6 +134,7 @@ struct FirmwareDialog::priv
// Data
std::vector<SerialPortInfo> ports;
optional<SerialPortInfo> port;
bool port_autodetect;
HexFile hex_file;
// This is a shared pointer holding the background AvrDude task
@ -147,6 +151,7 @@ struct FirmwareDialog::priv
btn_flash_label_flashing(_(L("Cancel"))),
label_status_flashing(_(L("Flashing in progress. Please do not disconnect the printer!"))),
timer_pulse(q),
port_autodetect(false),
progress_tasks_done(0),
progress_tasks_bar(0),
user_cancelled(false),
@ -158,7 +163,8 @@ struct FirmwareDialog::priv
void set_txt_status(const wxString &label);
void flashing_start(unsigned tasks);
void flashing_done(AvrDudeComplete complete);
void enable_port_picker(bool enable);
void set_autodetect(bool autodetect);
void update_flash_enabled();
void load_hex_file(const wxString &path);
void queue_event(AvrdudeEvent aevt, wxString message);
@ -171,6 +177,7 @@ struct FirmwareDialog::priv
void prepare_mk2();
void prepare_mk3();
void prepare_avr109(Avr109Pid usb_pid);
bool get_serial_port();
void perform_upload();
void user_cancel();
@ -211,8 +218,10 @@ void FirmwareDialog::priv::find_serial_ports()
idx = i;
break;
}
if (idx != -1)
if (idx != -1) {
port_picker->SetSelection(idx);
update_flash_enabled();
}
}
}
}
@ -277,20 +286,30 @@ void FirmwareDialog::priv::flashing_done(AvrDudeComplete complete)
}
}
void FirmwareDialog::priv::enable_port_picker(bool enable)
void FirmwareDialog::priv::set_autodetect(bool autodetect)
{
port_picker->Show(enable);
btn_rescan->Show(enable);
port_autodetect->Show(! enable);
port_autodetect = autodetect;
port_picker->Show(!autodetect);
btn_rescan->Show(!autodetect);
txt_port_autodetect->Show(autodetect);
q->Layout();
fit_no_shrink();
}
void FirmwareDialog::priv::update_flash_enabled()
{
const bool hex_exists = wxFileExists(hex_picker->GetPath());
const bool port_valid = port_autodetect || get_serial_port();
btn_flash->Enable(hex_exists && port_valid);
}
void FirmwareDialog::priv::load_hex_file(const wxString &path)
{
hex_file = HexFile(path.wx_str());
const bool auto_lookup = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1;
enable_port_picker(! auto_lookup);
const bool autodetect = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1;
set_autodetect(autodetect);
}
void FirmwareDialog::priv::queue_event(AvrdudeEvent aevt, wxString message)
@ -335,7 +354,7 @@ bool FirmwareDialog::priv::check_model_id()
// 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;
@ -423,8 +442,7 @@ void FirmwareDialog::priv::avr109_lookup_port(Avr109Pid usb_pid)
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.boot &&
port.id_product != usb_pid.app;
(port.id_product != usb_pid.boot && port.id_product != usb_pid.app);
}), ports.end());
if (ports.size() == 0) {
@ -553,6 +571,31 @@ void FirmwareDialog::priv::prepare_avr109(Avr109Pid usb_pid)
}
bool FirmwareDialog::priv::get_serial_port()
{
const int selection = port_picker->GetSelection();
if (selection != wxNOT_FOUND) {
port = this->ports[selection];
} else {
// User has supplied a custom filename
std::string path_u8 = GUI::into_u8(port_picker->GetValue());
#ifdef _WIN32
static const std::regex com_pattern("COM[0-9]+", std::regex::icase);
std::smatch matches;
if (std::regex_match(path_u8, matches, com_pattern)) {
#else
if (fs::is_other(fs::path(path_u8))) {
#endif
port = SerialPortInfo(std::move(path_u8));
} else {
port = boost::none;
}
}
return !!port;
}
void FirmwareDialog::priv::perform_upload()
{
auto filename = hex_picker->GetPath();
@ -560,14 +603,8 @@ void FirmwareDialog::priv::perform_upload()
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;
}
if (!port_autodetect && !get_serial_port()) {
return;
}
const bool extra_verbose = false; // For debugging
@ -769,13 +806,13 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
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->txt_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);
port_sizer->Add(p->txt_port_autodetect, 1, wxEXPAND);
p->set_autodetect(false);
auto *label_progress = new wxStaticText(panel, wxID_ANY, _(L("Progress:")));
p->progressbar = new wxGauge(panel, wxID_ANY, 1, wxDefaultPosition, wxDefaultSize, wxGA_HORIZONTAL | wxGA_SMOOTH);
@ -836,10 +873,13 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
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->update_flash_enabled();
});
p->port_picker->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &) { p->update_flash_enabled(); });
p->port_picker->Bind(wxEVT_TEXT, [this](wxCommandEvent &) { p->update_flash_enabled(); });
p->spoiler->Bind(wxEVT_COLLAPSIBLEPANE_CHANGED, [=](wxCollapsiblePaneEvent &evt) {
if (evt.GetCollapsed()) {
this->SetMinSize(wxSize(p->min_width, p->min_height));

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,6 @@
#include <stddef.h>
#include <memory>
#include "libslic3r/ModelArrange.hpp"
#include "3DScene.hpp"
#include "GLToolbar.hpp"
#include "Event.hpp"
@ -12,6 +11,7 @@
#include "Camera.hpp"
#include "Selection.hpp"
#include "Gizmos/GLGizmosManager.hpp"
#include "GUI_ObjectLayers.hpp"
#include <float.h>
@ -124,6 +124,10 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_BED_SHAPE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
class GLCanvas3D
{
@ -154,32 +158,6 @@ class GLCanvas3D
void reset() { first_volumes.clear(); }
};
#if !ENABLE_TEXTURES_FROM_SVG
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();
};
#endif // !ENABLE_TEXTURES_FROM_SVG
class LayersEditing
{
public:
@ -195,7 +173,6 @@ class GLCanvas3D
static const float THICKNESS_BAR_WIDTH;
static const float THICKNESS_RESET_BUTTON_HEIGHT;
bool m_use_legacy_opengl;
bool m_enabled;
Shader m_shader;
unsigned int m_z_texture_id;
@ -248,7 +225,6 @@ class GLCanvas3D
void select_object(const Model &model, int object_id);
bool is_allowed() const;
void set_use_legacy_opengl(bool use_legacy_opengl);
bool is_enabled() const;
void set_enabled(bool enabled);
@ -374,7 +350,7 @@ class GLCanvas3D
std::vector<Warning> m_warnings;
// Generates the texture with given text.
bool _generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored = false);
bool generate(const std::string& msg, const GLCanvas3D& canvas, bool compress, bool red_colored = false);
};
class LegendTexture : public GUI::GLTexture
@ -397,7 +373,7 @@ class GLCanvas3D
void fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
std::vector<std::pair<double, double>>& cp_legend_values);
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas);
bool generate(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors, const GLCanvas3D& canvas, bool compress);
void render(const GLCanvas3D& canvas) const;
};
@ -435,12 +411,14 @@ private:
Shader m_shader;
Mouse m_mouse;
mutable GLGizmosManager m_gizmos;
mutable GLToolbar m_toolbar;
mutable GLToolbar m_main_toolbar;
mutable GLToolbar m_undoredo_toolbar;
ClippingPlane m_clipping_planes[2];
mutable ClippingPlane m_camera_clipping_plane;
bool m_use_clipping_planes;
mutable SlaCap m_sla_caps[2];
std::string m_sidebar_field;
bool m_keep_dirty;
mutable GLVolumeCollection m_volumes;
Selection m_selection;
@ -451,7 +429,6 @@ private:
// Screen is only refreshed from the OnIdle handler if it is dirty.
bool m_dirty;
bool m_initialized;
bool m_use_VBOs;
bool m_apply_zoom_to_volumes_filter;
mutable std::vector<int> m_hover_volume_idxs;
bool m_warning_texture_enabled;
@ -460,7 +437,6 @@ private:
bool m_moving_enabled;
bool m_dynamic_background_enabled;
bool m_multisample_allowed;
bool m_regenerate_volumes;
bool m_moving;
bool m_tab_down;
ECursorType m_cursor_type;
@ -476,10 +452,16 @@ private:
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
#if ENABLE_RENDER_PICKING_PASS
bool m_show_picking_texture;
#endif // ENABLE_RENDER_PICKING_PASS
#if ENABLE_RENDER_STATISTICS
RenderStats m_render_stats;
#endif // ENABLE_RENDER_STATISTICS
int m_imgui_undo_redo_hovered_pos{ -1 };
public:
GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
~GLCanvas3D();
@ -489,7 +471,7 @@ public:
wxGLCanvas* get_wxglcanvas() { return m_canvas; }
const wxGLCanvas* get_wxglcanvas() const { return m_canvas; }
bool init(bool useVBOs, bool use_legacy_opengl);
bool init();
void post_event(wxEvent &&event);
void set_as_dirty();
@ -500,6 +482,8 @@ public:
void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
void update_instance_printable_state_for_object(size_t obj_idx);
void update_instance_printable_state_for_objects(std::vector<size_t>& object_idxs);
void set_config(const DynamicPrintConfig* config);
void set_process(BackgroundSlicingProcess* process);
@ -508,6 +492,9 @@ public:
const Selection& get_selection() const { return m_selection; }
Selection& get_selection() { return m_selection; }
const GLGizmosManager& get_gizmos_manager() const { return m_gizmos; }
GLGizmosManager& get_gizmos_manager() { return m_gizmos; }
void bed_shape_changed();
void set_clipping_plane(unsigned int id, const ClippingPlane& plane)
@ -539,7 +526,8 @@ public:
void enable_moving(bool enable);
void enable_gizmos(bool enable);
void enable_selection(bool enable);
void enable_toolbar(bool enable);
void enable_main_toolbar(bool enable);
void enable_undoredo_toolbar(bool enable);
void enable_dynamic_background(bool enable);
void allow_multisample(bool allow);
@ -591,18 +579,20 @@ public:
void set_tooltip(const std::string& tooltip) const;
void do_move();
void do_rotate();
void do_scale();
void do_flatten();
void do_mirror();
// the following methods add a snapshot to the undo/redo stack, unless the given string is empty
void do_move(const std::string& snapshot_type);
void do_rotate(const std::string& snapshot_type);
void do_scale(const std::string& snapshot_type);
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
void do_mirror(const std::string& snapshot_type);
void set_camera_zoom(float zoom);
void set_camera_zoom(double zoom);
void update_gizmos_on_off_state();
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
void handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type);
void update_ui_from_settings();
@ -610,15 +600,33 @@ public:
int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; }
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
arr::WipeTowerInfo get_wipe_tower_info() const;
void arrange_wipe_tower(const arr::WipeTowerInfo& wti) const;
class WipeTowerInfo {
protected:
Vec2d m_pos = {std::nan(""), std::nan("")};
Vec2d m_bb_size = {0., 0.};
double m_rotation = 0.;
friend class GLCanvas3D;
public:
inline operator bool() const
{
return !std::isnan(m_pos.x()) && !std::isnan(m_pos.y());
}
inline const Vec2d& pos() const { return m_pos; }
inline double rotation() const { return m_rotation; }
inline const Vec2d bb_size() const { return m_bb_size; }
void apply_wipe_tower() const;
};
WipeTowerInfo get_wipe_tower_info() const;
// Returns the view ray line, in world coordinate, at the given mouse position.
Linef3 mouse_ray(const Point& mouse_pos);
void set_mouse_as_dragging() { m_mouse.dragging = true; }
void disable_regenerate_volumes() { m_regenerate_volumes = false; }
void refresh_camera_scene_box() { m_camera.set_scene_box(scene_bounding_box()); }
bool is_mouse_dragging() const { return m_mouse.dragging; }
@ -627,18 +635,31 @@ public:
void set_cursor(ECursorType type);
void msw_rescale();
void start_keeping_dirty() { m_keep_dirty = true; }
void stop_keeping_dirty() { m_keep_dirty = false; }
unsigned int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
void force_main_toolbar_left_action(unsigned int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
void force_main_toolbar_right_action(unsigned int item_id) { m_main_toolbar.force_right_action(item_id, *this); }
void get_undoredo_toolbar_additional_tooltip(unsigned int item_id, std::string& text) { return m_undoredo_toolbar.get_additional_tooltip(item_id, text); }
void set_undoredo_toolbar_additional_tooltip(unsigned int item_id, const std::string& text) { m_undoredo_toolbar.set_additional_tooltip(item_id, text); }
bool has_toolpaths_to_export() const;
void export_toolpaths_to_obj(const char* filename) const;
private:
bool _is_shown_on_screen() const;
bool _init_toolbar();
bool _init_toolbars();
bool _init_main_toolbar();
bool _init_undoredo_toolbar();
bool _set_current();
void _resize(unsigned int w, unsigned int h);
BoundingBoxf3 _max_bounding_box() const;
BoundingBoxf3 _max_bounding_box(bool include_gizmos, bool include_bed_model) const;
void _zoom_to_bounding_box(const BoundingBoxf3& bbox);
float _get_zoom_to_bounding_box_factor(const BoundingBoxf3& bbox) const;
void _zoom_to_box(const BoundingBoxf3& box);
void _refresh_if_shown_on_screen();
@ -646,24 +667,26 @@ private:
void _rectangular_selection_picking_pass() const;
void _render_background() const;
void _render_bed(float theta) const;
void _render_axes() const;
void _render_objects() const;
void _render_selection() const;
#if ENABLE_RENDER_SELECTION_CENTER
void _render_selection_center() const;
#endif // ENABLE_RENDER_SELECTION_CENTER
void _render_overlays() const;
void _render_warning_texture() const;
void _render_legend_texture() const;
void _render_volumes_for_picking() const;
void _render_current_gizmo() const;
void _render_gizmos_overlay() const;
void _render_toolbar() const;
void _render_main_toolbar() const;
void _render_undoredo_toolbar() const;
void _render_view_toolbar() const;
#if ENABLE_SHOW_CAMERA_TARGET
void _render_camera_target() const;
#endif // ENABLE_SHOW_CAMERA_TARGET
void _render_sla_slices() const;
void _render_selection_sidebar_hints() const;
void _render_undo_redo_stack(const bool is_undo, float pos_x);
void _update_volumes_hover_state() const;
@ -703,8 +726,8 @@ private:
void _load_gcode_unretractions(const GCodePreviewData& preview_data);
// generates objects and wipe tower geometry
void _load_fff_shells();
// generates objects geometry for sla
void _load_sla_shells();
// Load SLA objects and support structures for objects, for which the slaposSliceSupports step has been finished.
void _load_sla_shells();
// sets gcode geometry visibility according to user selection
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
void _update_toolpath_volumes_outside_state();
@ -719,13 +742,11 @@ private:
bool _is_any_volume_outside() const;
#if !ENABLE_SVG_ICONS
void _resize_toolbars() const;
#endif // !ENABLE_SVG_ICONS
// updates the selection from the content of m_hover_volume_idxs
void _update_selection_from_hover();
bool _deactivate_undo_redo_toolbar_items();
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
public:

View file

@ -15,40 +15,112 @@
#include <string>
#include <iostream>
#ifdef __APPLE__
#include "../Utils/MacDarkMode.hpp"
#endif // __APPLE__
namespace Slic3r {
namespace GUI {
GLCanvas3DManager::GLInfo::GLInfo()
: version("")
, glsl_version("")
, vendor("")
, renderer("")
: m_detected(false)
, m_version("")
, m_glsl_version("")
, m_vendor("")
, m_renderer("")
, m_max_tex_size(0)
, m_max_anisotropy(0.0f)
{
}
void GLCanvas3DManager::GLInfo::detect()
const std::string& GLCanvas3DManager::GLInfo::get_version() const
{
if (!m_detected)
detect();
return m_version;
}
const std::string& GLCanvas3DManager::GLInfo::get_glsl_version() const
{
if (!m_detected)
detect();
return m_glsl_version;
}
const std::string& GLCanvas3DManager::GLInfo::get_vendor() const
{
if (!m_detected)
detect();
return m_vendor;
}
const std::string& GLCanvas3DManager::GLInfo::get_renderer() const
{
if (!m_detected)
detect();
return m_renderer;
}
int GLCanvas3DManager::GLInfo::get_max_tex_size() const
{
if (!m_detected)
detect();
// clamp to avoid the texture generation become too slow and use too much GPU memory
#ifdef __APPLE__
// and use smaller texture for non retina systems
return (Slic3r::GUI::mac_max_scaling_factor() > 1.0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
#else
// and use smaller texture for older OpenGL versions
return is_version_greater_or_equal_to(3, 0) ? std::min(m_max_tex_size, 8192) : std::min(m_max_tex_size / 2, 4096);
#endif // __APPLE__
}
float GLCanvas3DManager::GLInfo::get_max_anisotropy() const
{
if (!m_detected)
detect();
return m_max_anisotropy;
}
void GLCanvas3DManager::GLInfo::detect() const
{
const char* data = (const char*)::glGetString(GL_VERSION);
if (data != nullptr)
version = data;
m_version = data;
data = (const char*)::glGetString(GL_SHADING_LANGUAGE_VERSION);
if (data != nullptr)
glsl_version = data;
m_glsl_version = data;
data = (const char*)::glGetString(GL_VENDOR);
if (data != nullptr)
vendor = data;
m_vendor = data;
data = (const char*)::glGetString(GL_RENDERER);
if (data != nullptr)
renderer = data;
m_renderer = data;
glsafe(::glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_max_tex_size));
if (GLEW_EXT_texture_filter_anisotropic)
glsafe(::glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &m_max_anisotropy));
m_detected = true;
}
bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{
if (!m_detected)
detect();
std::vector<std::string> tokens;
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
boost::split(tokens, m_version, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.empty())
return false;
@ -75,6 +147,9 @@ bool GLCanvas3DManager::GLInfo::is_version_greater_or_equal_to(unsigned int majo
std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool extensions) const
{
if (!m_detected)
detect();
std::stringstream out;
std::string h2_start = format_as_html ? "<b>" : "";
@ -84,10 +159,10 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
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;
out << b_start << "GL version: " << b_end << (m_version.empty() ? "N/A" : m_version) << line_end;
out << b_start << "Vendor: " << b_end << (m_vendor.empty() ? "N/A" : m_vendor) << line_end;
out << b_start << "Renderer: " << b_end << (m_renderer.empty() ? "N/A" : m_renderer) << line_end;
out << b_start << "GLSL version: " << b_end << (m_glsl_version.empty() ? "N/A" : m_glsl_version) << line_end;
if (extensions)
{
@ -111,22 +186,18 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
}
GLCanvas3DManager::EMultisampleState GLCanvas3DManager::s_multisample = GLCanvas3DManager::MS_Unknown;
bool GLCanvas3DManager::s_compressed_textures_supported = false;
GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
GLCanvas3DManager::GLCanvas3DManager()
: m_context(nullptr)
, m_gl_initialized(false)
, m_use_legacy_opengl(false)
, m_use_VBOs(false)
{
}
GLCanvas3DManager::~GLCanvas3DManager()
{
if (m_context != nullptr)
{
delete m_context;
m_context = nullptr;
}
this->destroy();
}
bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar)
@ -134,7 +205,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
if (canvas == nullptr)
return false;
if (_get_canvas(canvas) != m_canvases.end())
if (do_get_canvas(canvas) != m_canvases.end())
return false;
GLCanvas3D* canvas3D = new GLCanvas3D(canvas, bed, camera, view_toolbar);
@ -159,7 +230,7 @@ bool GLCanvas3DManager::add(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLTo
bool GLCanvas3DManager::remove(wxGLCanvas* canvas)
{
CanvasesMap::iterator it = _get_canvas(canvas);
CanvasesMap::iterator it = do_get_canvas(canvas);
if (it == m_canvases.end())
return false;
@ -190,77 +261,92 @@ 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;
if (GLEW_EXT_texture_compression_s3tc)
s_compressed_textures_supported = true;
else
s_compressed_textures_supported = false;
}
}
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::init(wxGLCanvas* canvas)
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
CanvasesMap::const_iterator it = do_get_canvas(canvas);
if (it != m_canvases.end())
return (it->second != nullptr) ? _init(*it->second) : false;
return (it->second != nullptr) ? init(*it->second) : false;
else
return false;
}
void GLCanvas3DManager::destroy()
{
if (m_context != nullptr)
{
delete m_context;
m_context = nullptr;
}
}
GLCanvas3D* GLCanvas3DManager::get_canvas(wxGLCanvas* canvas)
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
CanvasesMap::const_iterator it = do_get_canvas(canvas);
return (it != m_canvases.end()) ? it->second : nullptr;
}
wxGLCanvas* GLCanvas3DManager::create_wxglcanvas(wxWindow *parent)
{
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 24, WX_GL_SAMPLE_BUFFERS, GL_TRUE, WX_GL_SAMPLES, 4, 0 };
int attribList[] = {
WX_GL_RGBA,
WX_GL_DOUBLEBUFFER,
// RGB channels each should be allocated with 8 bit depth. One should almost certainly get these bit depths by default.
WX_GL_MIN_RED, 8,
WX_GL_MIN_GREEN, 8,
WX_GL_MIN_BLUE, 8,
// Requesting an 8 bit alpha channel. Interestingly, the NVIDIA drivers would most likely work with some alpha plane, but glReadPixels would not return
// the alpha channel on NVIDIA if not requested when the GL context is created.
WX_GL_MIN_ALPHA, 8,
WX_GL_DEPTH_SIZE, 24,
WX_GL_SAMPLE_BUFFERS, GL_TRUE,
WX_GL_SAMPLES, 4,
0
};
if (s_multisample == MS_Unknown)
{
_detect_multisample(attribList);
// debug output
std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
detect_multisample(attribList);
// // debug output
// std::cout << "Multisample " << (can_multisample() ? "enabled" : "disabled") << std::endl;
}
if (! can_multisample()) {
attribList[4] = 0;
}
if (! can_multisample())
attribList[12] = 0;
return new wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
}
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
}
GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas) const
GLCanvas3DManager::CanvasesMap::const_iterator GLCanvas3DManager::do_get_canvas(wxGLCanvas* canvas) const
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);
}
bool GLCanvas3DManager::_init(GLCanvas3D& canvas)
bool GLCanvas3DManager::init(GLCanvas3D& canvas)
{
if (!m_gl_initialized)
init_gl();
return canvas.init(m_use_VBOs, m_use_legacy_opengl);
return canvas.init();
}
void GLCanvas3DManager::_detect_multisample(int* attribList)
void GLCanvas3DManager::detect_multisample(int* attribList)
{
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
const AppConfig* app_config = GUI::get_app_config();
bool enable_multisample = app_config != nullptr
&& app_config->get("use_legacy_opengl") != "1"
&& wxVersion >= 30003;
bool enable_multisample = wxVersion >= 30003;
s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? MS_Enabled : MS_Disabled;
// Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows

View file

@ -29,21 +29,39 @@ struct Camera;
class GLCanvas3DManager
{
struct GLInfo
public:
class GLInfo
{
std::string version;
std::string glsl_version;
std::string vendor;
std::string renderer;
mutable bool m_detected;
mutable std::string m_version;
mutable std::string m_glsl_version;
mutable std::string m_vendor;
mutable std::string m_renderer;
mutable int m_max_tex_size;
mutable float m_max_anisotropy;
public:
GLInfo();
void detect();
const std::string& get_version() const;
const std::string& get_glsl_version() const;
const std::string& get_vendor() const;
const std::string& get_renderer() const;
int get_max_tex_size() const;
float get_max_anisotropy() const;
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;
private:
void detect() const;
};
private:
enum EMultisampleState : unsigned char
{
MS_Unknown,
@ -55,11 +73,10 @@ class GLCanvas3DManager
CanvasesMap m_canvases;
wxGLContext* m_context;
GLInfo m_gl_info;
static GLInfo s_gl_info;
bool m_gl_initialized;
bool m_use_legacy_opengl;
bool m_use_VBOs;
static EMultisampleState s_multisample;
static bool s_compressed_textures_supported;
public:
GLCanvas3DManager();
@ -72,21 +89,25 @@ public:
unsigned int count() const;
void init_gl();
std::string get_gl_info(bool format_as_html, bool extensions) const;
bool init(wxGLCanvas* canvas);
void destroy();
GLCanvas3D* get_canvas(wxGLCanvas* canvas);
static bool can_multisample() { return s_multisample == MS_Enabled; }
static bool are_compressed_textures_supported() { return s_compressed_textures_supported; }
static wxGLCanvas* create_wxglcanvas(wxWindow *parent);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);
CanvasesMap::const_iterator _get_canvas(wxGLCanvas* canvas) const;
static const GLInfo& get_gl_info() { return s_gl_info; }
bool _init(GLCanvas3D& canvas);
static void _detect_multisample(int* attribList);
private:
CanvasesMap::iterator do_get_canvas(wxGLCanvas* canvas);
CanvasesMap::const_iterator do_get_canvas(wxGLCanvas* canvas) const;
bool init(GLCanvas3D& canvas);
static void detect_multisample(int* attribList);
};
} // namespace GUI

View file

@ -68,7 +68,8 @@ namespace GUI {
if (!is_dragging())
return;
float zoom = canvas.get_camera().zoom;
const Camera& camera = canvas.get_camera();
float zoom = (float)camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = canvas.get_canvas_size();
@ -96,6 +97,11 @@ namespace GUI {
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
// ensure that the rectangle is renderered inside the frustrum
glsafe(::glTranslated(0.0, 0.0, -(camera.get_near_z() + 0.5)));
// ensure that the overlay fits the frustrum near z plane
double gui_scale = camera.get_gui_scale();
glsafe(::glScaled(gui_scale, gui_scale, 1.0));
glsafe(::glPushAttrib(GL_ENABLE_BIT));
glsafe(::glLineStipple(4, 0xAAAA));

View file

@ -268,7 +268,6 @@ sub SetMatrix
}
*/
#if ENABLE_TEXTURES_FROM_SVG
Shader::Shader()
: m_shader(nullptr)
{
@ -363,6 +362,5 @@ void Shader::reset()
m_shader = nullptr;
}
}
#endif // ENABLE_TEXTURES_FROM_SVG
} // namespace Slic3r

View file

@ -37,7 +37,6 @@ public:
std::string last_error;
};
#if ENABLE_TEXTURES_FROM_SVG
class Shader
{
GLShader* m_shader;
@ -66,7 +65,6 @@ public:
private:
void reset();
};
#endif // ENABLE_TEXTURES_FROM_SVG
}

View file

@ -12,6 +12,10 @@
#include <vector>
#include <algorithm>
#include <thread>
#define STB_DXT_IMPLEMENTATION
#include "stb_dxt/stb_dxt.h"
#include "nanosvg/nanosvg.h"
#include "nanosvg/nanosvgrast.h"
@ -21,6 +25,100 @@
namespace Slic3r {
namespace GUI {
void GLTexture::Compressor::reset()
{
if (m_thread.joinable()) {
m_abort_compressing = true;
m_thread.join();
m_levels.clear();
m_num_levels_compressed = 0;
m_abort_compressing = false;
}
assert(m_levels.empty());
assert(m_abort_compressing == false);
assert(m_num_levels_compressed == 0);
}
void GLTexture::Compressor::start_compressing()
{
// The worker thread should be stopped already.
assert(! m_thread.joinable());
assert(! m_levels.empty());
assert(m_abort_compressing == false);
assert(m_num_levels_compressed == 0);
if (! m_levels.empty()) {
std::thread thrd(&GLTexture::Compressor::compress, this);
m_thread = std::move(thrd);
}
}
bool GLTexture::Compressor::unsent_compressed_data_available() const
{
if (m_levels.empty())
return false;
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
unsigned int num_compressed = m_num_levels_compressed;
for (unsigned int i = 0; i < num_compressed; ++ i)
if (! m_levels[i].sent_to_gpu && ! m_levels[i].compressed_data.empty())
return true;
return false;
}
void GLTexture::Compressor::send_compressed_data_to_gpu()
{
// this method should be called inside the main thread of Slicer or a new OpenGL context (sharing resources) would be needed
if (m_levels.empty())
return;
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_texture.m_id));
// Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread.
int num_compressed = (int)m_num_levels_compressed;
for (int i = 0; i < num_compressed; ++ i)
{
Level& level = m_levels[i];
if (! level.sent_to_gpu && ! level.compressed_data.empty())
{
glsafe(::glCompressedTexSubImage2D(GL_TEXTURE_2D, (GLint)i, 0, 0, (GLsizei)level.w, (GLsizei)level.h, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)level.compressed_data.size(), (const GLvoid*)level.compressed_data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (i > 0) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR));
level.sent_to_gpu = true;
// we are done with the compressed data, we can discard it
level.compressed_data.clear();
}
}
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
if (num_compressed == (unsigned int)m_levels.size())
// Finalize the worker thread, close it.
this->reset();
}
void GLTexture::Compressor::compress()
{
// reference: https://github.com/Cyan4973/RygsDXTc
assert(m_num_levels_compressed == 0);
assert(m_abort_compressing == false);
for (Level& level : m_levels)
{
if (m_abort_compressing)
break;
// stb_dxt library, despite claiming that the needed size of the destination buffer is equal to (source buffer size)/4,
// crashes if doing so, so we start with twice the required size
level.compressed_data = std::vector<unsigned char>(level.w * level.h * 2, 0);
int compressed_size = 0;
rygCompress(level.compressed_data.data(), level.src_data.data(), level.w, level.h, 1, compressed_size);
level.compressed_data.resize(compressed_size);
// we are done with the source data, we can discard it
level.src_data.clear();
++ m_num_levels_compressed;
}
}
GLTexture::Quad_UVs GLTexture::FullTextureUVs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } };
GLTexture::GLTexture()
@ -28,6 +126,7 @@ GLTexture::GLTexture()
, m_width(0)
, m_height(0)
, m_source("")
, m_compressor(*this)
{
}
@ -36,7 +135,7 @@ GLTexture::~GLTexture()
reset();
}
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
{
reset();
@ -44,12 +143,12 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps)
return false;
if (boost::algorithm::iends_with(filename, ".png"))
return load_from_png(filename, use_mipmaps);
return load_from_png(filename, use_mipmaps, compression_type, apply_anisotropy);
else
return false;
}
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
{
reset();
@ -57,12 +156,12 @@ bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps
return false;
if (boost::algorithm::iends_with(filename, ".svg"))
return load_from_svg(filename, use_mipmaps, max_size_px);
return load_from_svg(filename, use_mipmaps, compress, apply_anisotropy, max_size_px);
else
return false;
}
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px)
bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress)
{
reset();
@ -178,7 +277,10 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
@ -222,6 +324,7 @@ void GLTexture::reset()
m_width = 0;
m_height = 0;
m_source = "";
m_compressor.reset();
}
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
@ -252,46 +355,10 @@ void GLTexture::render_sub_texture(unsigned int tex_id, float left, float right,
glsafe(::glDisable(GL_BLEND));
}
unsigned int GLTexture::generate_mipmaps(wxImage& image)
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy)
{
int w = image.GetWidth();
int h = image.GetHeight();
GLint level = 0;
std::vector<unsigned char> data(w * h * 4, 0);
bool compression_enabled = (compression_type != None) && GLEW_EXT_texture_compression_s3tc;
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;
}
glsafe(::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;
}
bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
{
// Load a PNG with an alpha channel.
wxImage image;
if (!image.LoadFile(wxString::FromUTF8(filename.c_str()), wxBITMAP_TYPE_PNG))
@ -302,8 +369,32 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
m_width = image.GetWidth();
m_height = image.GetHeight();
int n_pixels = m_width * m_height;
bool requires_rescale = false;
if (compression_enabled && (compression_type == MultiThreaded))
{
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4;
int height_rem = m_height % 4;
if (width_rem != 0)
{
m_width += (4 - width_rem);
requires_rescale = true;
}
if (height_rem != 0)
{
m_height += (4 - height_rem);
requires_rescale = true;
}
}
if (requires_rescale)
image = image.ResampleBicubic(m_width, m_height);
int n_pixels = m_width * m_height;
if (n_pixels <= 0)
{
reset();
@ -335,34 +426,109 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps)
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (apply_anisotropy)
{
GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy();
if (max_anisotropy > 1.0f)
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
}
if (compression_enabled)
{
if (compression_type == SingleThreaded)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
{
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// and send the uncompressed data to the compressor
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data);
}
}
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (use_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
unsigned int levels_count = generate_mipmaps(image);
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
int lod_w = m_width;
int lod_h = m_height;
GLint level = 0;
// we do not need to generate all levels down to 1x1
while ((lod_w > 16) || (lod_h > 16))
{
++level;
lod_w = std::max(lod_w / 2, 1);
lod_h = std::max(lod_h / 2, 1);
n_pixels = lod_w * lod_h;
image = image.ResampleBicubic(lod_w, lod_h);
data.resize(n_pixels * 4);
img_rgb = image.GetData();
img_alpha = image.GetAlpha();
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;
}
if (compression_enabled)
{
if (compression_type == SingleThreaded)
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
else
{
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// and send the uncompressed data to the compressor
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
}
}
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
}
if (!compression_enabled)
{
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
}
else
{
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
}
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_source = filename;
if (compression_enabled && (compression_type == MultiThreaded))
// start asynchronous compression
m_compressor.start_compressing();
return true;
}
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px)
bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px)
{
bool compression_enabled = compress && GLEW_EXT_texture_compression_s3tc;
NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f);
if (image == nullptr)
{
// printf("Could not open SVG image.\n");
reset();
return false;
}
@ -371,6 +537,20 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
m_width = (int)(scale * image->width);
m_height = (int)(scale * image->height);
if (compression_enabled)
{
// the stb_dxt compression library seems to like only texture sizes which are a multiple of 4
int width_rem = m_width % 4;
int height_rem = m_height % 4;
if (width_rem != 0)
m_width += (4 - width_rem);
if (height_rem != 0)
m_height += (4 - height_rem);
}
int n_pixels = m_width * m_height;
if (n_pixels <= 0)
@ -383,7 +563,6 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
NSVGrasterizer* rast = nsvgCreateRasterizer();
if (rast == nullptr)
{
// printf("Could not init rasterizer.\n");
nsvgDelete(image);
reset();
return false;
@ -397,14 +576,32 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
glsafe(::glGenTextures(1, &m_id));
glsafe(::glBindTexture(GL_TEXTURE_2D, m_id));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (apply_anisotropy)
{
GLfloat max_anisotropy = GLCanvas3DManager::get_gl_info().get_max_anisotropy();
if (max_anisotropy > 1.0f)
glsafe(::glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy));
}
if (compression_enabled)
{
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// and send the uncompressed data to the compressor
m_compressor.add_level((unsigned int)m_width, (unsigned int)m_height, data);
}
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (use_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
int lod_w = m_width;
int lod_h = m_height;
GLint level = 0;
while ((lod_w > 1) || (lod_h > 1))
// we do not need to generate all levels down to 1x1
while ((lod_w > 16) || (lod_h > 16))
{
++level;
@ -412,24 +609,42 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns
lod_h = std::max(lod_h / 2, 1);
scale /= 2.0f;
data.resize(lod_w * lod_h * 4);
nsvgRasterize(rast, image, 0, 0, scale, data.data(), lod_w, lod_h, lod_w * 4);
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
if (compression_enabled)
{
// initializes the texture on GPU
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
// and send the uncompressed data to the compressor
m_compressor.add_level((unsigned int)lod_w, (unsigned int)lod_h, data);
}
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()));
}
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
if (!compression_enabled)
{
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
}
}
else
{
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
}
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
m_source = filename;
if (compression_enabled)
// start asynchronous compression
m_compressor.start_compressing();
nsvgDeleteRasterizer(rast);
nsvgDelete(image);

View file

@ -1,8 +1,10 @@
#ifndef slic3r_GLTexture_hpp_
#define slic3r_GLTexture_hpp_
#include <atomic>
#include <string>
#include <vector>
#include <thread>
class wxImage;
@ -11,7 +13,55 @@ namespace GUI {
class GLTexture
{
class Compressor
{
struct Level
{
unsigned int w;
unsigned int h;
std::vector<unsigned char> src_data;
std::vector<unsigned char> compressed_data;
bool sent_to_gpu;
Level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) : w(w), h(h), src_data(data), sent_to_gpu(false) {}
};
GLTexture& m_texture;
std::vector<Level> m_levels;
std::thread m_thread;
// Does the caller want the background thread to stop?
// This atomic also works as a memory barrier for synchronizing the cancel event with the worker thread.
std::atomic<bool> m_abort_compressing;
// How many levels were compressed since the start of the background processing thread?
// This atomic also works as a memory barrier for synchronizing results of the worker thread with the calling thread.
std::atomic<unsigned int> m_num_levels_compressed;
public:
explicit Compressor(GLTexture& texture) : m_texture(texture), m_abort_compressing(false), m_num_levels_compressed(0) {}
~Compressor() { reset(); }
void reset();
void add_level(unsigned int w, unsigned int h, const std::vector<unsigned char>& data) { m_levels.emplace_back(w, h, data); }
void start_compressing();
bool unsent_compressed_data_available() const;
void send_compressed_data_to_gpu();
bool all_compressed_data_sent_to_gpu() const { return m_levels.empty(); }
private:
void compress();
};
public:
enum ECompressionType : unsigned char
{
None,
SingleThreaded,
MultiThreaded
};
struct UV
{
float u;
@ -33,13 +83,14 @@ namespace GUI {
int m_width;
int m_height;
std::string m_source;
Compressor m_compressor;
public:
GLTexture();
virtual ~GLTexture();
bool load_from_file(const std::string& filename, bool use_mipmaps);
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
bool load_from_file(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
bool load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
// meanings of states: (std::pair<int, bool>)
// first field (int):
// 0 -> no changes
@ -48,7 +99,7 @@ namespace GUI {
// second field (bool):
// false -> no changes
// true -> add background color
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px);
bool load_from_svg_files_as_sprites_array(const std::vector<std::string>& filenames, const std::vector<std::pair<int, bool>>& states, unsigned int sprite_size_px, bool compress);
void reset();
unsigned int get_id() const { return m_id; }
@ -57,14 +108,18 @@ namespace GUI {
const std::string& get_source() const { return m_source; }
bool unsent_compressed_data_available() const { return m_compressor.unsent_compressed_data_available(); }
void send_compressed_data_to_gpu() { m_compressor.send_compressed_data_to_gpu(); }
bool all_compressed_data_sent_to_gpu() const { return m_compressor.all_compressed_data_sent_to_gpu(); }
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);
private:
bool load_from_png(const std::string& filename, bool use_mipmaps);
bool load_from_svg(const std::string& filename, bool use_mipmaps, unsigned int max_size_px);
bool load_from_png(const std::string& filename, bool use_mipmaps, ECompressionType compression_type, bool apply_anisotropy);
bool load_from_svg(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px);
friend class Compressor;
};
} // namespace GUI

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,8 @@ class GLToolbarItem
public:
typedef std::function<void()> ActionCallback;
typedef std::function<bool()> VisibilityCallback;
typedef std::function<bool()> EnabledStateCallback;
typedef std::function<bool()> EnablingCallback;
typedef std::function<void(float, float, float, float)> RenderCallback;
enum EType : unsigned char
{
@ -45,6 +46,14 @@ public:
Num_Types
};
enum EActionType : unsigned char
{
Undefined,
Left,
Right,
Num_Action_Types
};
enum EState : unsigned char
{
Normal,
@ -57,29 +66,43 @@ public:
struct Data
{
struct Option
{
bool toggable;
ActionCallback action_callback;
RenderCallback render_callback;
Option();
bool can_render() const { return toggable && (render_callback != nullptr); }
};
std::string name;
#if ENABLE_SVG_ICONS
std::string icon_filename;
#endif // ENABLE_SVG_ICONS
std::string tooltip;
std::string additional_tooltip;
unsigned int sprite_id;
bool is_toggable;
// mouse left click
Option left;
// mouse right click
Option right;
bool visible;
ActionCallback action_callback;
VisibilityCallback visibility_callback;
EnabledStateCallback enabled_state_callback;
EnablingCallback enabling_callback;
Data();
};
static const ActionCallback Default_Action_Callback;
static const VisibilityCallback Default_Visibility_Callback;
static const EnabledStateCallback Default_Enabled_State_Callback;
static const EnablingCallback Default_Enabling_Callback;
static const RenderCallback Default_Render_Callback;
private:
EType m_type;
EState m_state;
Data m_data;
EActionType m_last_action_type;
public:
GLToolbarItem(EType type, const Data& data);
@ -88,22 +111,30 @@ public:
void set_state(EState state) { m_state = state; }
const std::string& get_name() const { return m_data.name; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_data.icon_filename; }
#endif // ENABLE_SVG_ICONS
const std::string& get_tooltip() const { return m_data.tooltip; }
const std::string& get_additional_tooltip() const { return m_data.additional_tooltip; }
void set_additional_tooltip(const std::string& text) { m_data.additional_tooltip = text; }
void do_action() { m_data.action_callback(); }
void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); }
void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); }
bool is_enabled() const { return m_state != Disabled; }
bool is_disabled() const { return m_state == Disabled; }
bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); }
bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); }
bool is_toggable() const { return m_data.is_toggable; }
bool is_visible() const { return m_data.visible; }
bool is_separator() const { return m_type == Separator; }
bool is_left_toggable() const { return m_data.left.toggable; }
bool is_right_toggable() const { return m_data.right.toggable; }
bool has_left_render_callback() const { return m_data.left.render_callback != nullptr; }
bool has_right_render_callback() const { return m_data.right.render_callback != nullptr; }
EActionType get_last_action_type() const { return m_last_action_type; }
void reset_last_action_type() { m_last_action_type = Undefined; }
// returns true if the state changes
bool update_visibility();
// returns true if the state changes
@ -118,27 +149,6 @@ private:
friend class GLToolbar;
};
#if !ENABLE_SVG_ICONS
// 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
{
struct Metadata
{
// path of the file containing the icons' texture
std::string filename;
// size of the square icons, in pixels
unsigned int icon_size;
Metadata();
};
GLTexture texture;
Metadata metadata;
};
#endif // !ENABLE_SVG_ICONS
struct BackgroundTexture
{
struct Metadata
@ -164,9 +174,7 @@ struct BackgroundTexture
class GLToolbar
{
public:
#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
@ -184,29 +192,32 @@ public:
Num_Types
};
enum EOrientation : unsigned int
enum EHorizontalOrientation : unsigned char
{
Top,
Bottom,
Left,
Right,
Center,
Num_Locations
HO_Left,
HO_Center,
HO_Right,
Num_Horizontal_Orientations
};
enum EVerticalOrientation : unsigned char
{
VO_Top,
VO_Center,
VO_Bottom,
Num_Vertical_Orientations
};
EType type;
EOrientation orientation;
EHorizontalOrientation horizontal_orientation;
EVerticalOrientation vertical_orientation;
float top;
float left;
float border;
float separator_size;
float gap_size;
#if ENABLE_SVG_ICONS
float icons_size;
float scale;
#else
float icons_scale;
#endif // ENABLE_SVG_ICONS
float width;
float height;
@ -219,16 +230,10 @@ private:
typedef std::vector<GLToolbarItem*> ItemsList;
EType m_type;
#if ENABLE_SVG_ICONS
std::string m_name;
#endif // ENABLE_SVG_ICONS
bool m_enabled;
#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
#else
ItemsIconsTexture m_icons_texture;
#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
mutable Layout m_layout;
@ -249,36 +254,27 @@ private:
MouseCapture m_mouse_capture;
std::string m_tooltip;
unsigned int m_pressed_toggable_id;
public:
#if ENABLE_SVG_ICONS
GLToolbar(EType type, const std::string& name);
#else
explicit GLToolbar(EType type);
#endif // ENABLE_SVG_ICONS
~GLToolbar();
#if ENABLE_SVG_ICONS
bool init(const BackgroundTexture::Metadata& background_texture);
#else
bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture);
#endif // ENABLE_SVG_ICONS
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
Layout::EOrientation get_layout_orientation() const;
void set_layout_orientation(Layout::EOrientation orientation);
Layout::EHorizontalOrientation get_horizontal_orientation() const { return m_layout.horizontal_orientation; }
void set_horizontal_orientation(Layout::EHorizontalOrientation orientation) { m_layout.horizontal_orientation = orientation; }
Layout::EVerticalOrientation get_vertical_orientation() const { return m_layout.vertical_orientation; }
void set_vertical_orientation(Layout::EVerticalOrientation orientation) { m_layout.vertical_orientation = orientation; }
void set_position(float top, float left);
void set_border(float border);
void set_separator_size(float size);
void set_gap_size(float size);
#if ENABLE_SVG_ICONS
void set_icons_size(float size);
void set_scale(float scale);
#else
void set_icons_scale(float scale);
#endif // ENABLE_SVG_ICONS
bool is_enabled() const;
void set_enabled(bool enable);
@ -295,8 +291,17 @@ public:
bool is_item_disabled(const std::string& name) const;
bool is_item_visible(const std::string& name) const;
bool is_any_item_pressed() const;
unsigned int get_item_id(const std::string& name) const;
void force_left_action(unsigned int item_id, GLCanvas3D& parent);
void force_right_action(unsigned int item_id, GLCanvas3D& parent);
const std::string& get_tooltip() const { return m_tooltip; }
void get_additional_tooltip(unsigned int item_id, std::string& text);
void set_additional_tooltip(unsigned int item_id, const std::string& text);
// returns true if any item changed its state
bool update_items_state();
@ -312,7 +317,7 @@ private:
float get_height_horizontal() const;
float get_height_vertical() const;
float get_main_size() const;
void do_action(unsigned int item_id, GLCanvas3D& parent);
void do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover);
std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent);
std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent);
std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent);
@ -321,12 +326,11 @@ private:
int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const;
void render_background(float left, float top, float right, float bottom, float border) const;
void render_horizontal(const GLCanvas3D& parent) const;
void render_vertical(const GLCanvas3D& parent) const;
#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
#endif // ENABLE_SVG_ICONS
// returns true if any item changed its state
bool update_items_visibility();

View file

@ -135,8 +135,7 @@ void config_wizard(int reason)
wxGetApp().load_current_presets();
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA &&
wxGetApp().obj_list()->has_multi_part_objects())
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA && model_has_multi_part_objects(wxGetApp().model()))
{
show_info(nullptr,
_(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" +
@ -149,6 +148,13 @@ void config_wizard(int reason)
void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/)
{
try{
if (config.def()->get(opt_key)->type == coBools && config.def()->get(opt_key)->nullable) {
ConfigOptionBoolsNullable* vec_new = new ConfigOptionBoolsNullable{ boost::any_cast<unsigned char>(value) };
config.option<ConfigOptionBoolsNullable>(opt_key)->set_at(vec_new, opt_index, 0);
return;
}
switch (config.def()->get(opt_key)->type) {
case coFloatOrPercent:{
std::string str = boost::any_cast<std::string>(value);

View file

@ -67,9 +67,12 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
/* FT_MODEL */ "Known files (*.stl, *.obj, *.amf, *.xml, *.3mf, *.prusa)|*.stl;*.STL;*.obj;*.OBJ;*.amf;*.AMF;*.xml;*.XML;*.3mf;*.3MF;*.prusa;*.PRUSA",
/* FT_PROJECT */ "Project files (*.3mf, *.amf)|*.3mf;*.3MF;*.amf;*.AMF",
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
/* FT_PNGZIP */"Masked SLA files (*.sl1)|*.sl1;*.SL1",
/* FT_INI */ "INI files (*.ini)|*.ini;*.INI",
/* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG",
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
/* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1",
};
std::string out = defaults[file_type];
@ -123,7 +126,16 @@ static void generic_exception_handle()
try {
throw;
} catch (const std::exception &ex) {
} catch (const std::bad_alloc& ex) {
// bad_alloc in main thread is most likely fatal. Report immediately to the user (wxLogError would be delayed)
// and terminate the app so it is at least certain to happen now.
wxString errmsg = wxString::Format(_(L("%s has encountered an error. It was likely caused by running out of memory. "
"If you are sure you have enough RAM on your system, this may also be a bug and we would "
"be glad if you reported it.\n\nThe application will now terminate.")), SLIC3R_APP_NAME);
wxMessageBox(errmsg + "\n\n" + wxString(ex.what()), _(L("Fatal error")), wxOK | wxICON_ERROR);
BOOST_LOG_TRIVIAL(error) << boost::format("std::bad_alloc exception: %1%") % ex.what();
std::terminate();
} catch (const std::exception& ex) {
wxLogError("Internal error: %s", ex.what());
BOOST_LOG_TRIVIAL(error) << boost::format("Uncaught exception: %1%") % ex.what();
throw;
@ -141,6 +153,18 @@ GUI_App::GUI_App()
, m_imgui(new ImGuiWrapper())
{}
GUI_App::~GUI_App()
{
if (app_config != nullptr)
delete app_config;
if (preset_bundle != nullptr)
delete preset_bundle;
if (preset_updater != nullptr)
delete preset_updater;
}
bool GUI_App::OnInit()
{
try {
@ -158,7 +182,8 @@ bool GUI_App::on_init_inner()
wxCHECK_MSG(wxDirExists(resources_dir), false,
wxString::Format("Resources path does not exist or is not a directory: %s", resources_dir));
SetAppName(SLIC3R_APP_KEY);
//SetAppName(SLIC3R_APP_KEY);
SetAppName(SLIC3R_APP_KEY "-alpha");
SetAppDisplayName(SLIC3R_APP_NAME);
// Enable this to get the default Win32 COMCTRL32 behavior of static boxes.
@ -265,11 +290,23 @@ bool GUI_App::on_init_inner()
}
CallAfter([this] {
if (!config_wizard_startup(app_conf_exists)) {
// Only notify if there was no wizard so as not to bother too much ...
preset_updater->slic3r_update_notify();
}
config_wizard_startup(app_conf_exists);
preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle);
const GLCanvas3DManager::GLInfo &glinfo = GLCanvas3DManager::get_gl_info();
if (! glinfo.is_version_greater_or_equal_to(2, 0)) {
// Complain about the OpenGL version.
wxString message = wxString::Format(
_(L("PrusaSlicer requires OpenGL 2.0 capable graphics driver to run correctly, \n"
"while OpenGL version %s, render %s, vendor %s was detected.")), wxString(glinfo.get_version()), wxString(glinfo.get_renderer()), wxString(glinfo.get_vendor()));
message += "\n";
message += _(L("You may need to update your graphics card driver."));
#ifdef _WIN32
message += "\n";
message += _(L("As a workaround, you may run PrusaSlicer with a software rendered 3D graphics by running prusa-slicer.exe with the --sw_renderer parameter."));
#endif
wxMessageBox(message, wxString("PrusaSlicer - ") + _(L("Unsupported OpenGL version")), wxOK | wxICON_ERROR);
}
});
}
});
@ -524,7 +561,7 @@ void GUI_App::persist_window_geometry(wxTopLevelWindow *window, bool default_max
});
}
void GUI_App::load_project(wxWindow *parent, wxString& input_file)
void GUI_App::load_project(wxWindow *parent, wxString& input_file) const
{
input_file.Clear();
wxFileDialog dialog(parent ? parent : GetTopWindow(),
@ -536,7 +573,7 @@ void GUI_App::load_project(wxWindow *parent, wxString& input_file)
input_file = dialog.GetPath();
}
void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files)
void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const
{
input_files.Clear();
wxFileDialog dialog(parent ? parent : GetTopWindow(),
@ -844,7 +881,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
// 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 GUI_App::check_unsaved_changes()
bool GUI_App::check_unsaved_changes(const wxString &header)
{
wxString dirty;
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
@ -858,8 +895,12 @@ bool GUI_App::check_unsaved_changes()
// No changes, the application may close or reload presets.
return true;
// Ask the user.
wxString message;
if (! header.empty())
message = header + "\n\n";
message += _(L("The presets on the following tabs were modified")) + ": " + dirty + "\n\n" + _(L("Discard changes and continue anyway?"));
wxMessageDialog dialog(mainframe,
_(L("The presets on the following tabs were modified")) + ": " + dirty + "\n\n" + _(L("Discard changes and continue anyway?")),
message,
wxString(SLIC3R_APP_NAME) + " - " + _(L("Unsaved Presets")),
wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT);
return dialog.ShowModal() == wxID_YES;
@ -924,14 +965,19 @@ ObjectList* GUI_App::obj_list()
return sidebar().obj_list();
}
ObjectLayers* GUI_App::obj_layers()
{
return sidebar().obj_layers();
}
Plater* GUI_App::plater()
{
return plater_;
}
ModelObjectPtrs* GUI_App::model_objects()
Model& GUI_App::model()
{
return &plater_->model().objects;
return plater_->model();
}
wxNotebook* GUI_App::tab_panel() const
@ -939,6 +985,7 @@ wxNotebook* GUI_App::tab_panel() const
return mainframe->m_tabpanel;
}
// extruders count from selected printer preset
int GUI_App::extruders_cnt() const
{
const Preset& preset = preset_bundle->printers.get_selected_preset();
@ -946,9 +993,45 @@ int GUI_App::extruders_cnt() const
preset.config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
}
// extruders count from edited printer preset
int GUI_App::extruders_edited_cnt() const
{
const Preset& preset = preset_bundle->printers.get_edited_preset();
return preset.printer_technology() == ptSLA ? 1 :
preset.config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
}
wxString GUI_App::current_language_code_safe() const
{
// Translate the language code to a code, for which Prusa Research maintains translations.
wxString language_code = this->current_language_code();
size_t idx_underscore = language_code.find(language_code);
if (idx_underscore != wxString::npos)
language_code = language_code.substr(0, idx_underscore);
const std::map<wxString, wxString> mapping {
{ "cs", "cs_CZ", },
{ "sk", "cs_CZ", },
{ "de", "de_DE", },
{ "es", "es_ES", },
{ "fr", "fr_FR", },
{ "it", "it_IT", },
{ "ja", "ja_JP", },
{ "ko", "ko_KR", },
{ "pl", "pl_PL", },
{ "uk", "uk_UA", },
{ "zh", "zh_CN", },
};
auto it = mapping.find(language_code);
if (it != mapping.end())
language_code = it->second;
else
language_code = "en_US";
return language_code;
}
void GUI_App::open_web_page_localized(const std::string &http_address)
{
wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code());
wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe());
}
void GUI_App::window_pos_save(wxTopLevelWindow* window, const std::string &name)

View file

@ -44,6 +44,9 @@ enum FileType
FT_INI,
FT_SVG,
FT_TEX,
FT_PNGZIP,
FT_SIZE,
@ -95,6 +98,7 @@ public:
bool initialized() const { return m_initialized; }
GUI_App();
~GUI_App();
static unsigned get_colour_approx_luma(const wxColour &colour);
static bool dark_mode();
@ -120,8 +124,8 @@ public:
void recreate_GUI();
void system_info();
void keyboard_shortcuts();
void load_project(wxWindow *parent, wxString& input_file);
void import_model(wxWindow *parent, wxArrayString& input_files);
void load_project(wxWindow *parent, wxString& input_file) const;
void import_model(wxWindow *parent, wxArrayString& input_files) const;
static bool catch_error(std::function<void()> cb, const std::string& err);
void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false);
@ -138,11 +142,13 @@ public:
void update_mode();
void add_config_menu(wxMenuBar *menu);
bool check_unsaved_changes();
bool check_unsaved_changes(const wxString &header = wxString());
bool checked_tab(Tab* tab);
void load_current_presets();
wxString current_language_code() { return m_wxLocale != nullptr ? m_wxLocale->GetCanonicalName() : wxString("en_US"); }
wxString current_language_code() const { return m_wxLocale != nullptr ? m_wxLocale->GetCanonicalName() : wxString("en_US"); }
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
wxString current_language_code_safe() const;
virtual bool OnExceptionInMainLoop();
@ -155,8 +161,9 @@ public:
ObjectManipulation* obj_manipul();
ObjectSettings* obj_settings();
ObjectList* obj_list();
ObjectLayers* obj_layers();
Plater* plater();
std::vector<ModelObject*> *model_objects();
Model& model();
AppConfig* app_config{ nullptr };
PresetBundle* preset_bundle{ nullptr };
@ -166,6 +173,7 @@ public:
wxNotebook* tab_panel() const ;
int extruders_cnt() const;
int extruders_edited_cnt() const;
std::vector<Tab *> tabs_list;

View file

@ -0,0 +1,341 @@
#include "GUI_ObjectLayers.hpp"
#include "GUI_ObjectList.hpp"
#include "OptionsGroup.hpp"
#include "PresetBundle.hpp"
#include "libslic3r/Model.hpp"
#include "GLCanvas3D.hpp"
#include <boost/algorithm/string.hpp>
#include "I18N.hpp"
#include <wx/wupdlock.h>
namespace Slic3r
{
namespace GUI
{
ObjectLayers::ObjectLayers(wxWindow* parent) :
OG_Settings(parent, true)
{
m_grid_sizer = new wxFlexGridSizer(3, 5, 5); // "Min Z", "Max Z", "Layer height" & buttons sizer
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
// Legend for object layers
for (const std::string col : { "Min Z", "Max Z", "Layer height" }) {
auto temp = new wxStaticText(m_parent, wxID_ANY, _(L(col)), wxDefaultPosition, /*size*/wxDefaultSize, wxST_ELLIPSIZE_MIDDLE);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->SetFont(wxGetApp().bold_font());
m_grid_sizer->Add(temp);
}
m_og->sizer->Clear(true);
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
m_bmp_delete = ScalableBitmap(parent, "remove_copies"/*"cross"*/);
m_bmp_add = ScalableBitmap(parent, "add_copies");
}
void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range)
{
if (is_last_edited_range && m_selection_type == editor->type()) {
/* Workaround! Under OSX we should use CallAfter() for SetFocus() after LayerEditors "reorganizations",
* because of selected control's strange behavior:
* cursor is set to the control, but blue border - doesn't.
* And as a result we couldn't edit this control.
* */
#ifdef __WXOSX__
wxTheApp->CallAfter([editor]() {
#endif
editor->SetFocus();
editor->SetInsertionPointEnd();
#ifdef __WXOSX__
});
#endif
}
}
wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
{
const bool is_last_edited_range = range == m_selectable_range;
auto set_focus_data = [range, this](const EditorType type)
{
m_selectable_range = range;
m_selection_type = type;
};
auto update_focus_data = [range, this](const t_layer_height_range& new_range, EditorType type, bool enter_pressed)
{
// change selectable range for new one, if enter was pressed or if same range was selected
if (enter_pressed || m_selectable_range == range)
m_selectable_range = new_range;
if (enter_pressed)
m_selection_type = type;
};
// Add control for the "Min Z"
auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ,
set_focus_data, [range, update_focus_data, this](coordf_t min_z, bool enter_pressed)
{
if (fabs(min_z - range.first) < EPSILON) {
m_selection_type = etUndef;
return false;
}
// data for next focusing
coordf_t max_z = min_z < range.second ? range.second : min_z + 0.5;
const t_layer_height_range& new_range = { min_z, max_z };
update_focus_data(new_range, etMinZ, enter_pressed);
return wxGetApp().obj_list()->edit_layer_range(range, new_range);
});
select_editor(editor, is_last_edited_range);
m_grid_sizer->Add(editor);
// Add control for the "Max Z"
editor = new LayerRangeEditor(this, double_to_string(range.second), etMaxZ,
set_focus_data, [range, update_focus_data, this](coordf_t max_z, bool enter_pressed)
{
if (fabs(max_z - range.second) < EPSILON || range.first > max_z) {
m_selection_type = etUndef;
return false; // LayersList would not be updated/recreated
}
// data for next focusing
const t_layer_height_range& new_range = { range.first, max_z };
update_focus_data(new_range, etMaxZ, enter_pressed);
return wxGetApp().obj_list()->edit_layer_range(range, new_range);
});
select_editor(editor, is_last_edited_range);
m_grid_sizer->Add(editor);
// Add control for the "Layer height"
editor = new LayerRangeEditor(this,
double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()),
etLayerHeight, set_focus_data, [range, this](coordf_t layer_height, bool)
{
return wxGetApp().obj_list()->edit_layer_range(range, layer_height);
});
select_editor(editor, is_last_edited_range);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(editor);
m_grid_sizer->Add(sizer);
return sizer;
}
void ObjectLayers::create_layers_list()
{
for (const auto layer : m_object->layer_config_ranges)
{
const t_layer_height_range& range = layer.first;
auto sizer = create_layer(range);
auto del_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_delete);
del_btn->SetToolTip(_(L("Remove layer")));
sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
del_btn->Bind(wxEVT_BUTTON, [this, range](wxEvent &event) {
wxGetApp().obj_list()->del_layer_range(range);
});
auto add_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_add);
add_btn->SetToolTip(_(L("Add layer")));
sizer->Add(add_btn, 0, wxRIGHT, em_unit(m_parent));
add_btn->Bind(wxEVT_BUTTON, [this, range](wxEvent &event) {
wxGetApp().obj_list()->add_layer_range_after_current(range);
});
}
}
void ObjectLayers::update_layers_list()
{
ObjectList* objects_ctrl = wxGetApp().obj_list();
if (objects_ctrl->multiple_selection()) return;
const auto item = objects_ctrl->GetSelection();
if (!item) return;
const int obj_idx = objects_ctrl->get_selected_obj_idx();
if (obj_idx < 0) return;
const ItemType type = objects_ctrl->GetModel()->GetItemType(item);
if (!(type & (itLayerRoot | itLayer))) return;
m_object = objects_ctrl->object(obj_idx);
if (!m_object || m_object->layer_config_ranges.empty()) return;
// Delete all controls from options group except of the legends
const int cols = m_grid_sizer->GetEffectiveColsCount();
const int rows = m_grid_sizer->GetEffectiveRowsCount();
for (int idx = cols*rows-1; idx >= cols; idx--) {
wxSizerItem* t = m_grid_sizer->GetItem(idx);
if (t->IsSizer())
t->GetSizer()->Clear(true);
else
t->DeleteWindows();
m_grid_sizer->Remove(idx);
}
// Add new control according to the selected item
if (type & itLayerRoot)
create_layers_list();
else
create_layer(objects_ctrl->GetModel()->GetLayerRangeByItem(item));
m_parent->Layout();
}
void ObjectLayers::update_scene_from_editor_selection() const
{
// needed to show the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_layers_data_focus_event(m_selectable_range, m_selection_type);
}
void ObjectLayers::UpdateAndShow(const bool show)
{
if (show)
update_layers_list();
OG_Settings::UpdateAndShow(show);
}
void ObjectLayers::msw_rescale()
{
m_bmp_delete.msw_rescale();
m_bmp_add.msw_rescale();
}
void ObjectLayers::reset_selection()
{
m_selectable_range = { 0.0, 0.0 };
m_selection_type = etLayerHeight;
}
LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
const wxString& value,
EditorType type,
std::function<void(EditorType)> set_focus_data_fn,
std::function<bool(coordf_t, bool enter_pressed)> edit_fn
) :
m_valid_value(value),
m_type(type),
m_set_focus_data(set_focus_data_fn),
wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition,
wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
{
this->SetFont(wxGetApp().normal_font());
this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
{
m_enter_pressed = true;
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
if (m_type&etLayerHeight) {
if (!edit_fn(get_value(), true))
SetValue(m_valid_value);
else
m_valid_value = double_to_string(get_value());
m_call_kill_focus = true;
}
else if (!edit_fn(get_value(), true)) {
SetValue(m_valid_value);
m_call_kill_focus = true;
}
}, this->GetId());
this->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
{
if (!m_enter_pressed) {
#ifndef __WXGTK__
/* Update data for next editor selection.
* But under GTK it lucks like there is no information about selected control at e.GetWindow(),
* so we'll take it from wxEVT_LEFT_DOWN event
* */
LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());
if (new_editor)
new_editor->set_focus_data();
#endif // not __WXGTK__
// If LayersList wasn't updated/recreated, we should call e.Skip()
if (m_type & etLayerHeight) {
if (!edit_fn(get_value(), false))
SetValue(m_valid_value);
else
m_valid_value = double_to_string(get_value());
e.Skip();
}
else if (!edit_fn(get_value(), false)) {
SetValue(m_valid_value);
e.Skip();
}
}
else if (m_call_kill_focus) {
m_call_kill_focus = false;
e.Skip();
}
}, this->GetId());
this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
{
set_focus_data();
parent->update_scene_from_editor_selection();
e.Skip();
}, this->GetId());
#ifdef __WXGTK__ // Workaround! To take information about selectable range
this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
{
set_focus_data();
e.Skip();
}, this->GetId());
#endif //__WXGTK__
this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event)
{
// select all text using Ctrl+A
if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
this->SetSelection(-1, -1); //select all
event.Skip();
}));
}
coordf_t LayerRangeEditor::get_value()
{
wxString str = GetValue();
coordf_t layer_height;
// Replace the first occurence of comma in decimal number.
str.Replace(",", ".", false);
if (str == ".")
layer_height = 0.0;
else
{
if (!str.ToCDouble(&layer_height) || layer_height < 0.0f)
{
show_error(m_parent, _(L("Invalid numeric input.")));
SetValue(double_to_string(layer_height));
}
}
return layer_height;
}
} //namespace GUI
} //namespace Slic3r

View file

@ -0,0 +1,88 @@
#ifndef slic3r_GUI_ObjectLayers_hpp_
#define slic3r_GUI_ObjectLayers_hpp_
#include "GUI_ObjectSettings.hpp"
#include "wxExtensions.hpp"
#ifdef __WXOSX__
#include "../libslic3r/PrintConfig.hpp"
#endif
class wxBoxSizer;
namespace Slic3r {
class ModelObject;
namespace GUI {
class ConfigOptionsGroup;
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
class ObjectLayers;
enum EditorType
{
etUndef = 0,
etMinZ = 1,
etMaxZ = 2,
etLayerHeight = 4,
};
class LayerRangeEditor : public wxTextCtrl
{
bool m_enter_pressed { false };
bool m_call_kill_focus { false };
wxString m_valid_value;
EditorType m_type;
std::function<void(EditorType)> m_set_focus_data;
public:
LayerRangeEditor( ObjectLayers* parent,
const wxString& value = wxEmptyString,
EditorType type = etUndef,
std::function<void(EditorType)> set_focus_data_fn = [](EditorType) {;},
std::function<bool(coordf_t, bool)> edit_fn = [](coordf_t, bool) {return false; }
);
~LayerRangeEditor() {}
EditorType type() const {return m_type;}
void set_focus_data() const { m_set_focus_data(m_type);}
private:
coordf_t get_value();
};
class ObjectLayers : public OG_Settings
{
ScalableBitmap m_bmp_delete;
ScalableBitmap m_bmp_add;
ModelObject* m_object {nullptr};
wxFlexGridSizer* m_grid_sizer;
t_layer_height_range m_selectable_range;
EditorType m_selection_type {etUndef};
public:
ObjectLayers(wxWindow* parent);
~ObjectLayers() {}
void select_editor(LayerRangeEditor* editor, const bool is_last_edited_range);
wxSizer* create_layer(const t_layer_height_range& range); // without_buttons
void create_layers_list();
void update_layers_list();
void update_scene_from_editor_selection() const;
void UpdateAndShow(const bool show) override;
void msw_rescale();
void reset_selection();
void set_selectable_range(const t_layer_height_range& range) { m_selectable_range = range; }
friend class LayerRangeEditor;
};
}}
#endif // slic3r_GUI_ObjectLayers_hpp_

File diff suppressed because it is too large Load diff

View file

@ -26,13 +26,17 @@ enum class ModelVolumeType : int;
// FIXME: broken build on mac os because of this is missing:
typedef std::vector<std::string> t_config_option_keys;
typedef std::map<std::string, std::vector<std::string>> FreqSettingsBundle;
typedef std::map<std::string, std::vector<std::string>> SettingsBundle;
// category -> vector ( option ; label )
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
namespace GUI {
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
@ -62,12 +66,20 @@ struct ItemForDelete
class ObjectList : public wxDataViewCtrl
{
public:
enum SELECTION_MODE
{
smUndef,
smVolume,
smInstance
} m_selection_mode {smUndef};
smUndef = 0,
smVolume = 1,
smInstance = 2,
smLayer = 4,
smSettings = 8, // used for undo/redo
smLayerRoot = 16, // used for undo/redo
};
private:
SELECTION_MODE m_selection_mode {smUndef};
int m_selected_layers_range_idx;
struct dragged_item_data
{
@ -119,12 +131,17 @@ class ObjectList : public wxDataViewCtrl
MenuWithSeparators m_menu_part;
MenuWithSeparators m_menu_sla_object;
MenuWithSeparators m_menu_instance;
wxMenuItem* m_menu_item_split { nullptr };
wxMenuItem* m_menu_item_split_part { nullptr };
MenuWithSeparators m_menu_layer;
wxMenuItem* m_menu_item_settings { nullptr };
wxMenuItem* m_menu_item_split_instances { nullptr };
std::vector<wxBitmap*> m_bmp_vector;
ObjectDataViewModel *m_objects_model{ nullptr };
DynamicPrintConfig *m_config {nullptr};
std::vector<ModelObject*> *m_objects{ nullptr };
std::vector<wxBitmap*> m_bmp_vector;
t_layer_config_ranges m_layer_config_ranges_cache;
int m_selected_object_id = -1;
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
@ -140,10 +157,14 @@ class ObjectList : public wxDataViewCtrl
int m_selected_row = 0;
wxDataViewItem m_last_selected_item {nullptr};
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
int m_last_selected_column = -1;
#endif /* __MSW__ */
#if 0
FreqSettingsBundle m_freq_settings_fff;
FreqSettingsBundle m_freq_settings_sla;
SettingsBundle m_freq_settings_fff;
SettingsBundle m_freq_settings_sla;
#endif
public:
@ -153,11 +174,11 @@ public:
std::map<std::string, wxBitmap> CATEGORY_ICON;
ObjectDataViewModel *m_objects_model{ nullptr };
DynamicPrintConfig *m_config {nullptr};
std::vector<ModelObject*> *m_objects{ nullptr };
ObjectDataViewModel* GetModel() const { return m_objects_model; }
DynamicPrintConfig* config() const { return m_config; }
std::vector<ModelObject*>* objects() const { return m_objects; }
ModelObject* object(const int obj_idx) const ;
void create_objects_ctrl();
void create_popup_menus();
@ -192,16 +213,23 @@ public:
void key_event(wxKeyEvent& event);
#endif /* __WXOSX__ */
void copy();
void paste();
void undo();
void redo();
void get_settings_choice(const wxString& category_name);
void get_freq_settings_choice(const wxString& bundle_name);
void update_settings_item();
void show_settings(const wxDataViewItem settings_item);
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
void append_menu_items_add_volume(wxMenu* menu);
wxMenuItem* append_menu_item_split(wxMenu* menu);
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
wxMenuItem* append_menu_item_settings(wxMenu* menu);
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
wxMenuItem* append_menu_item_printable(wxMenu* menu, wxWindow* parent);
void append_menu_items_osx(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu) const ;
@ -215,17 +243,25 @@ public:
wxMenu* create_settings_popupmenu(wxMenu *parent_menu);
void create_freq_settings_popupmenu(wxMenu *parent_menu);
void update_opt_keys(t_config_option_keys& t_optopt_keys);
void update_opt_keys(t_config_option_keys& t_optopt_keys, const bool is_object);
void load_subobject(ModelVolumeType type);
void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void del_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config();
void del_settings_from_config(const wxDataViewItem& parent_item);
void del_instances_from_object(const int obj_idx);
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
void del_layers_from_object(const int obj_idx);
bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
void split();
void layers_editing();
wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item);
wxDataViewItem add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config);
DynamicPrintConfig get_default_layer_config(const int obj_idx);
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
bool is_splittable();
bool selected_instances_of_same_object();
@ -235,12 +271,13 @@ public:
wxBoxSizer* get_sizer() {return m_sizer;}
int get_selected_obj_idx() const;
DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const;
SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_object_settings);
void changed_object(const int obj_idx = -1) const;
void part_selection_changed();
// Add object to the list
void add_object_to_list(size_t obj_idx);
void add_object_to_list(size_t obj_idx, bool call_selection_changed = true);
// Delete object from the list
void delete_object_from_list();
void delete_object_from_list(const size_t obj_idx);
@ -265,10 +302,21 @@ public:
// Remove objects/sub-object from the list
void remove();
void del_layer_range(const t_layer_height_range& range);
void add_layer_range_after_current(const t_layer_height_range& current_range);
void add_layer_item (const t_layer_height_range& range,
const wxDataViewItem layers_item,
const int layer_idx = -1);
bool edit_layer_range(const t_layer_height_range& range, coordf_t layer_height);
bool edit_layer_range(const t_layer_height_range& range,
const t_layer_height_range& new_range);
void init_objects();
bool multiple_selection() const ;
bool is_selected(const ItemType type) const;
int get_selected_layers_range_idx() const;
void set_selected_layers_range_idx(const int range_idx) { m_selected_layers_range_idx = range_idx; }
void set_selection_mode(SELECTION_MODE mode) { m_selection_mode = mode; }
void update_selections();
void update_selections_on_canvas();
void select_item(const wxDataViewItem& item);
@ -284,8 +332,10 @@ public:
void change_part_type();
void last_volume_is_deleted(const int obj_idx);
bool has_multi_part_objects();
void update_settings_items();
void update_and_show_object_settings_item();
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
void update_object_list_by_printer_technology();
void update_object_menu();
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
@ -295,16 +345,24 @@ public:
void fix_through_netfabb();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void fill_layer_config_ranges_cache();
void paste_layers_into_list();
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
void msw_rescale();
void update_after_undo_redo();
//update printable state for item from objects model
void update_printable_state(int obj_idx, int instance_idx);
void toggle_printable_state(wxDataViewItem item);
private:
#ifdef __WXOSX__
// void OnChar(wxKeyEvent& event);
#endif /* __WXOSX__ */
void OnContextMenu(wxDataViewEvent &event);
void list_manipulation();
void OnBeginDrag(wxDataViewEvent &event);
void OnDropPossible(wxDataViewEvent &event);
@ -312,6 +370,10 @@ private:
bool can_drop(const wxDataViewItem& item) const ;
void ItemValueChanged(wxDataViewEvent &event);
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
void OnEditingStarted(wxDataViewEvent &event);
#endif /* __WXMSW__ */
void OnEditingDone(wxDataViewEvent &event);
void show_multi_selection_menu();

View file

@ -17,6 +17,28 @@ namespace Slic3r
namespace GUI
{
// Helper function to be used by drop to bed button. Returns lowest point of this
// volume in world coordinate system.
static double get_volume_min_z(const GLVolume* volume)
{
const Transform3f& world_matrix = volume->world_matrix().cast<float>();
// need to get the ModelVolume pointer
const ModelObject* mo = wxGetApp().model().objects[volume->composite_id.object_id];
const ModelVolume* mv = mo->volumes[volume->composite_id.volume_id];
const TriangleMesh& hull = mv->get_convex_hull();
float min_z = std::numeric_limits<float>::max();
for (const stl_facet& facet : hull.stl.facet_start) {
for (int i = 0; i < 3; ++ i)
min_z = std::min(min_z, Vec3f::UnitZ().dot(world_matrix * facet.vertex[i]));
}
return min_z;
}
static wxBitmapComboBox* create_word_local_combo(wxWindow *parent)
{
wxSize size(15 * wxGetApp().em_unit(), -1);
@ -92,6 +114,7 @@ void msw_rescale_word_local_combo(wxBitmapComboBox* combo)
combo->SetValue(selection);
}
ObjectManipulation::ObjectManipulation(wxWindow* parent) :
OG_Settings(parent, true)
#ifndef __APPLE__
@ -130,8 +153,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
auto manifold_warning_icon = [this](wxWindow* parent) {
m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_fix_throught_netfab_bitmap);
// auto sizer = new wxBoxSizer(wxHORIZONTAL);
// sizer->Add(m_fix_throught_netfab_bitmap);
if (is_windows10())
m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e)
@ -144,17 +167,19 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
});
return sizer;
// return sizer;
return m_fix_throught_netfab_bitmap;
};
line.append_widget(manifold_warning_icon);
// line.append_widget(manifold_warning_icon);
line.near_label_widget = manifold_warning_icon;
def.label = "";
def.gui_type = "legend";
def.tooltip = L("Object name");
#ifdef __APPLE__
def.width = 19;
def.width = 20;
#else
def.width = 21;
def.width = 22;
#endif
def.set_default_value(new ConfigOptionString{ " " });
line.append_option(Option(def, "object_name"));
@ -162,16 +187,71 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
const int field_width = 5;
// Mirror button size:
const int mirror_btn_width = 3;
// Legend for object modification
line = Line{ "", "" };
def.label = "";
def.type = coString;
def.width = field_width/*50*/;
def.width = field_width - mirror_btn_width;//field_width/*50*/;
for (const std::string axis : { "x", "y", "z" }) {
const std::string label = boost::algorithm::to_upper_copy(axis);
def.set_default_value(new ConfigOptionString{ " " + label });
Option option = Option(def, axis + "_axis_legend");
// Load bitmaps to be used for the mirroring buttons:
m_mirror_bitmap_on = ScalableBitmap(parent, "mirroring_on");
m_mirror_bitmap_off = ScalableBitmap(parent, "mirroring_off");
m_mirror_bitmap_hidden = ScalableBitmap(parent, "mirroring_transparent.png");
static const char axes[] = { 'X', 'Y', 'Z' };
for (size_t axis_idx = 0; axis_idx < sizeof(axes); axis_idx++) {
const char label = axes[axis_idx];
def.set_default_value(new ConfigOptionString{ std::string(" ") + label });
Option option(def, std::string() + label + "_axis_legend");
// We will add a button to toggle mirroring to each axis:
auto mirror_button = [this, mirror_btn_width, axis_idx, label](wxWindow* parent) {
wxSize btn_size(em_unit(parent) * mirror_btn_width, em_unit(parent) * mirror_btn_width);
auto btn = new ScalableButton(parent, wxID_ANY, "mirroring_off", wxEmptyString, btn_size, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER | wxTRANSPARENT_WINDOW);
btn->SetToolTip(wxString::Format(_(L("Toggle %c axis mirroring")), (int)label));
btn->SetBitmapDisabled_(m_mirror_bitmap_hidden);
m_mirror_buttons[axis_idx].first = btn;
m_mirror_buttons[axis_idx].second = mbShown;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn);
btn->Bind(wxEVT_BUTTON, [this, axis_idx](wxCommandEvent &e) {
Axis axis = (Axis)(axis_idx + X);
if (m_mirror_buttons[axis_idx].second == mbHidden)
return;
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
volume->set_volume_mirror(axis, -volume->get_volume_mirror(axis));
}
else if (selection.is_single_full_instance()) {
for (unsigned int idx : selection.get_volume_idxs()){
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
volume->set_instance_mirror(axis, -volume->get_instance_mirror(axis));
}
}
else
return;
// Update mirroring at the GLVolumes.
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
selection.synchronize_unselected_volumes();
// Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
canvas->do_mirror(L("Set Mirror"));
UpdateAndShow(true);
});
return sizer;
};
option.side_widget = mirror_button;
line.append_option(option);
}
line.near_label_widget = [this](wxWindow* parent) {
@ -190,8 +270,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
def.set_default_value(new ConfigOptionFloat(0.0));
def.width = field_width/*50*/;
// Add "uniform scaling" button in front of "Scale" option
if (option_name == "Scale") {
// Add "uniform scaling" button in front of "Scale" option
line.near_label_widget = [this](wxWindow* parent) {
auto btn = new LockButton(parent, wxID_ANY);
btn->Bind(wxEVT_BUTTON, [btn, this](wxCommandEvent &event){
@ -201,8 +281,89 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
m_lock_bnt = btn;
return btn;
};
// Add reset scale button
auto reset_scale_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset scale")));
m_reset_scale_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Reset scale")));
change_scale_value(0, 100.);
change_scale_value(1, 100.);
change_scale_value(2, 100.);
});
return sizer;
};
line.append_widget(reset_scale_button);
}
else if (option_name == "Rotation") {
// Add reset rotation button
auto reset_rotation_button = [this](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "undo"));
btn->SetToolTip(_(L("Reset rotation")));
m_reset_rotation_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) {
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(*selection.get_volume_idxs().begin()));
volume->set_volume_rotation(Vec3d::Zero());
}
else if (selection.is_single_full_instance()) {
for (unsigned int idx : selection.get_volume_idxs()){
GLVolume* volume = const_cast<GLVolume*>(selection.get_volume(idx));
volume->set_instance_rotation(Vec3d::Zero());
}
}
else
return;
// Update rotation at the GLVolumes.
selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL);
selection.synchronize_unselected_volumes();
// Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing.
canvas->do_rotate(L("Reset Rotation"));
UpdateAndShow(true);
});
return sizer;
};
line.append_widget(reset_rotation_button);
}
else if (option_name == "Position") {
// Add drop to bed button
auto drop_to_bed_button = [=](wxWindow* parent) {
auto btn = new ScalableButton(parent, wxID_ANY, ScalableBitmap(parent, "drop_to_bed"));
btn->SetToolTip(_(L("Drop to bed")));
m_drop_to_bed_button = btn;
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(btn, wxBU_EXACTFIT);
btn->Bind(wxEVT_BUTTON, [=](wxCommandEvent &e) {
// ???
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
if (selection.is_single_volume() || selection.is_single_modifier()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
const Geometry::Transformation& instance_trafo = volume->get_instance_transformation();
Vec3d diff = m_cache.position - instance_trafo.get_matrix(true).inverse() * Vec3d(0., 0., get_volume_min_z(volume));
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Drop to bed")));
change_position_value(0, diff.x());
change_position_value(1, diff.y());
change_position_value(2, diff.z());
}
});
return sizer;
};
line.append_widget(drop_to_bed_button);
}
// Add empty bmp (Its size have to be equal to PrusaLockButton) in front of "Size" option to label alignment
else if (option_name == "Size") {
line.near_label_widget = [this](wxWindow* parent) {
@ -224,8 +385,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
return line;
};
// Settings table
m_og->sidetext_width = 3;
m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label);
m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label);
m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label);
@ -233,12 +394,23 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
// call back for a rescale of button "Set uniform scale"
m_og->rescale_near_label_widget = [this](wxWindow* win) {
// rescale lock icon
auto *ctrl = dynamic_cast<LockButton*>(win);
if (ctrl == nullptr)
if (ctrl != nullptr) {
ctrl->msw_rescale();
return;
ctrl->msw_rescale();
}
if (win == m_fix_throught_netfab_bitmap)
return;
// rescale "place" of the empty icon (to correct layout of the "Size" and "Scale")
if (dynamic_cast<wxStaticBitmap*>(win) != nullptr)
win->SetMinSize(create_scaled_bitmap(m_parent, "one_layer_lock_on.png").GetSize());
};
}
void ObjectManipulation::Show(const bool show)
{
@ -308,7 +480,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_scale = m_new_size.cwiseProduct(selection.get_unscaled_instance_bounding_box().size().cwiseInverse()) * 100.;
} else {
m_new_rotation = volume->get_instance_rotation() * (180. / M_PI);
m_new_size = volume->get_instance_transformation().get_scaling_factor().cwiseProduct((*wxGetApp().model_objects())[volume->object_idx()]->raw_mesh_bounding_box().size());
m_new_size = volume->get_instance_transformation().get_scaling_factor().cwiseProduct(wxGetApp().model().objects[volume->object_idx()]->raw_mesh_bounding_box().size());
m_new_scale = volume->get_instance_scaling_factor() * 100.;
}
@ -332,7 +504,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_position = volume->get_volume_offset();
m_new_rotation = volume->get_volume_rotation() * (180. / M_PI);
m_new_scale = volume->get_volume_scaling_factor() * 100.;
m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box.size());
m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box().size());
m_new_enabled = true;
}
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot))
@ -390,11 +562,13 @@ void ObjectManipulation::update_if_dirty()
if (selection.requires_uniform_scale()) {
m_lock_bnt->SetLock(true);
m_lock_bnt->Disable();
m_lock_bnt->SetToolTip(_(L("You cann't use non-uniform scaling mode for multiple objects/parts selection")));
m_lock_bnt->disable();
}
else {
m_lock_bnt->SetLock(m_uniform_scale);
m_lock_bnt->Enable();
m_lock_bnt->SetToolTip(wxEmptyString);
m_lock_bnt->enable();
}
{
@ -408,9 +582,100 @@ void ObjectManipulation::update_if_dirty()
else
m_og->disable();
update_reset_buttons_visibility();
update_mirror_buttons_visibility();
m_dirty = false;
}
void ObjectManipulation::update_reset_buttons_visibility()
{
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
if (!canvas)
return;
const Selection& selection = canvas->get_selection();
bool show_rotation = false;
bool show_scale = false;
bool show_drop_to_bed = false;
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Vec3d rotation;
Vec3d scale;
double min_z = 0.;
if (selection.is_single_full_instance()) {
rotation = volume->get_instance_rotation();
scale = volume->get_instance_scaling_factor();
}
else {
rotation = volume->get_volume_rotation();
scale = volume->get_volume_scaling_factor();
min_z = get_volume_min_z(volume);
}
show_rotation = !rotation.isApprox(Vec3d::Zero());
show_scale = !scale.isApprox(Vec3d::Ones());
show_drop_to_bed = (std::abs(min_z) > EPSILON);
}
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed]{
m_reset_rotation_button->Show(show_rotation);
m_reset_scale_button->Show(show_scale);
m_drop_to_bed_button->Show(show_drop_to_bed);
});
}
void ObjectManipulation::update_mirror_buttons_visibility()
{
GLCanvas3D* canvas = wxGetApp().plater()->canvas3D();
Selection& selection = canvas->get_selection();
std::array<MirrorButtonState, 3> new_states = {mbHidden, mbHidden, mbHidden};
if (!m_world_coordinates) {
if (selection.is_single_full_instance() || selection.is_single_modifier() || selection.is_single_volume()) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Vec3d mirror;
if (selection.is_single_full_instance())
mirror = volume->get_instance_mirror();
else
mirror = volume->get_volume_mirror();
for (unsigned char i=0; i<3; ++i)
new_states[i] = (mirror[i] < 0. ? mbActive : mbShown);
}
}
else {
// the mirroring buttons should be hidden in world coordinates,
// unless we make it actually mirror in world coords.
}
// Hiding the buttons through Hide() always messed up the sizers. As a workaround, the button
// is assigned a transparent bitmap. We must of course remember the actual state.
wxGetApp().CallAfter([this, new_states]{
for (int i=0; i<3; ++i) {
if (new_states[i] != m_mirror_buttons[i].second) {
const ScalableBitmap* bmp;
switch (new_states[i]) {
case mbHidden : bmp = &m_mirror_bitmap_hidden; m_mirror_buttons[i].first->Enable(false); break;
case mbShown : bmp = &m_mirror_bitmap_off; m_mirror_buttons[i].first->Enable(true); break;
case mbActive : bmp = &m_mirror_bitmap_on; m_mirror_buttons[i].first->Enable(true); break;
}
m_mirror_buttons[i].first->SetBitmap_(*bmp);
m_mirror_buttons[i].second = new_states[i];
}
}
});
}
#ifndef __APPLE__
void ObjectManipulation::emulate_kill_focus()
{
@ -431,6 +696,7 @@ void ObjectManipulation::emulate_kill_focus()
void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
{
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0,0) : m_manifold_warning_bmp.bmp().GetSize());
m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
}
@ -458,7 +724,7 @@ void ObjectManipulation::change_position_value(int axis, double value)
Selection& selection = canvas->get_selection();
selection.start_dragging();
selection.translate(position - m_cache.position, selection.requires_local_axes());
canvas->do_move();
canvas->do_move(L("Set Position"));
m_cache.position = position;
m_cache.position_rounded(axis) = DBL_MAX;
@ -489,11 +755,11 @@ void ObjectManipulation::change_rotation_value(int axis, double value)
selection.rotate(
(M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation),
transformation_type);
canvas->do_rotate();
canvas->do_rotate(L("Set Orientation"));
m_cache.rotation = rotation;
m_cache.rotation_rounded(axis) = DBL_MAX;
this->UpdateAndShow(true);
this->UpdateAndShow(true);
}
void ObjectManipulation::change_scale_value(int axis, double value)
@ -511,6 +777,7 @@ void ObjectManipulation::change_scale_value(int axis, double value)
this->UpdateAndShow(true);
}
void ObjectManipulation::change_size_value(int axis, double value)
{
if (std::abs(m_cache.size_rounded(axis) - value) < EPSILON)
@ -523,11 +790,11 @@ void ObjectManipulation::change_size_value(int axis, double value)
Vec3d ref_size = m_cache.size;
if (selection.is_single_volume() || selection.is_single_modifier())
ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box.size();
else if (selection.is_single_full_instance())
ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box().size();
else if (selection.is_single_full_instance())
ref_size = m_world_coordinates ?
selection.get_unscaled_instance_bounding_box().size() :
(*wxGetApp().model_objects())[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size();
wxGetApp().model().objects[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size();
this->do_scale(axis, 100. * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2)));
@ -553,7 +820,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const
selection.start_dragging();
selection.scale(scaling_factor * 0.01, transformation_type);
wxGetApp().plater()->canvas3D()->do_scale();
wxGetApp().plater()->canvas3D()->do_scale(L("Set Scale"));
}
void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any& value)
@ -650,7 +917,7 @@ void ObjectManipulation::set_uniform_scaling(const bool new_value)
return;
}
// Bake the rotation into the meshes of the object.
(*wxGetApp().model_objects())[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id);
wxGetApp().model().objects[volume->composite_id.object_id]->bake_xy_rotation_into_meshes(volume->composite_id.instance_id);
// Update the 3D scene, selections etc.
wxGetApp().plater()->update();
// Recalculate cached values at this panel, refresh the screen.
@ -664,7 +931,20 @@ void ObjectManipulation::msw_rescale()
{
msw_rescale_word_local_combo(m_word_local_combo);
m_manifold_warning_bmp.msw_rescale();
m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
const wxString& tooltip = m_fix_throught_netfab_bitmap->GetToolTipText();
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
m_fix_throught_netfab_bitmap->SetMinSize(tooltip.IsEmpty() ? wxSize(0, 0) : m_manifold_warning_bmp.bmp().GetSize());
m_mirror_bitmap_on.msw_rescale();
m_mirror_bitmap_off.msw_rescale();
m_mirror_bitmap_hidden.msw_rescale();
m_reset_scale_button->msw_rescale();
m_reset_rotation_button->msw_rescale();
m_drop_to_bed_button->msw_rescale();
for (int id = 0; id < 3; ++id)
m_mirror_buttons[id].first->msw_rescale();
get_og()->msw_rescale();
}

View file

@ -53,6 +53,24 @@ class ObjectManipulation : public OG_Settings
wxStaticText* m_scale_Label = nullptr;
wxStaticText* m_rotate_Label = nullptr;
// Non-owning pointers to the reset buttons, so we can hide and show them.
ScalableButton* m_reset_scale_button = nullptr;
ScalableButton* m_reset_rotation_button = nullptr;
ScalableButton* m_drop_to_bed_button = nullptr;
// Mirroring buttons and their current state
enum MirrorButtonState {
mbHidden,
mbShown,
mbActive
};
std::array<std::pair<ScalableButton*, MirrorButtonState>, 3> m_mirror_buttons;
// Bitmaps for the mirroring buttons.
ScalableBitmap m_mirror_bitmap_on;
ScalableBitmap m_mirror_bitmap_off;
ScalableBitmap m_mirror_bitmap_hidden;
// Needs to be updated from OnIdle?
bool m_dirty = false;
// Cached labels for the delayed update, not localized!
@ -111,10 +129,10 @@ private:
void reset_settings_value();
void update_settings_value(const Selection& selection);
// update size values after scale unit changing or "gizmos"
void update_size_value(const Vec3d& size);
// update rotation value after "gizmos"
void update_rotation_value(const Vec3d& rotation);
// Show or hide scale/rotation reset buttons if needed
void update_reset_buttons_visibility();
//Show or hide mirror buttons
void update_mirror_buttons_visibility();
// change values
void change_position_value(int axis, double value);

View file

@ -61,20 +61,31 @@ ObjectSettings::ObjectSettings(wxWindow* parent) :
m_og->sizer->Add(m_settings_list_sizer, 1, wxEXPAND | wxLEFT, 5);
m_bmp_delete = ScalableBitmap(parent, "cross");
m_bmp_delete_focus = ScalableBitmap(parent, "cross_focus");
}
void ObjectSettings::update_settings_list()
bool ObjectSettings::update_settings_list()
{
m_settings_list_sizer->Clear(true);
m_og_settings.resize(0);
auto objects_ctrl = wxGetApp().obj_list();
auto objects_model = wxGetApp().obj_list()->m_objects_model;
auto config = wxGetApp().obj_list()->m_config;
auto objects_model = wxGetApp().obj_list()->GetModel();
auto config = wxGetApp().obj_list()->config();
const auto item = objects_ctrl->GetSelection();
if (item && !objects_ctrl->multiple_selection() &&
config && objects_model->IsSettingsItem(item))
{
if (!item || !objects_model->IsSettingsItem(item) || !config || objects_ctrl->multiple_selection())
return false;
const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject;
SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(config, is_object_settings);
if (!cat_options.empty())
{
std::vector<std::string> categories;
categories.reserve(cat_options.size());
auto extra_column = [config, this](wxWindow* parent, const Line& line)
{
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
@ -82,7 +93,11 @@ void ObjectSettings::update_settings_list()
auto btn = new ScalableButton(parent, wxID_ANY, m_bmp_delete);
btn->SetToolTip(_(L("Remove parameter")));
btn->SetBitmapFocus(m_bmp_delete_focus.bmp());
btn->SetBitmapHover(m_bmp_delete_focus.bmp());
btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) {
wxGetApp().plater()->take_snapshot(wxString::Format(_(L("Delete Option %s")), opt_key));
config->erase(opt_key);
wxGetApp().obj_list()->changed_object();
wxTheApp->CallAfter([this]() {
@ -94,90 +109,70 @@ void ObjectSettings::update_settings_list()
return btn;
};
std::map<std::string, std::vector<std::string>> cat_options;
auto opt_keys = config->keys();
objects_ctrl->update_opt_keys(opt_keys); // update options list according to print technology
m_og_settings.resize(0);
std::vector<std::string> categories;
if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return;
for (auto& cat : cat_options)
{
const int extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
categories.push_back(cat.first);
for (auto& opt_key : opt_keys) {
auto category = config->def()->get(opt_key)->category;
if (category.empty() ||
(category == "Extruders" && extruders_cnt == 1)) continue;
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
optgroup->label_width = 15;
optgroup->sidetext_width = 5.5;
std::vector< std::string > new_category;
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
wxGetApp().obj_list()->changed_object(); };
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;
}
// call back for rescaling of the extracolumn control
optgroup->rescale_extra_column_item = [this](wxWindow* win) {
auto *ctrl = dynamic_cast<ScalableButton*>(win);
if (ctrl == nullptr)
return;
ctrl->SetBitmap_(m_bmp_delete);
ctrl->SetBitmapFocus(m_bmp_delete_focus.bmp());
ctrl->SetBitmapHover(m_bmp_delete_focus.bmp());
};
for (auto& cat : cat_options) {
if (cat.second.size() == 1 && cat.second[0] == "extruder")
continue;
const bool is_extruders_cat = cat.first == "Extruders";
for (auto& opt : cat.second)
{
Option option = optgroup->get_option(opt);
option.opt.width = 12;
if (is_extruders_cat)
option.opt.max = wxGetApp().extruders_edited_cnt();
optgroup->append_single_option_line(option);
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
optgroup->label_width = 15;
optgroup->sidetext_width = 5.5;
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
wxGetApp().obj_list()->changed_object(); };
const bool is_extriders_cat = cat.first == "Extruders";
for (auto& opt : cat.second)
{
if (opt == "extruder")
continue;
Option option = optgroup->get_option(opt);
option.opt.width = 12;
if (is_extriders_cat)
option.opt.max = wxGetApp().extruders_cnt();
optgroup->append_single_option_line(option);
}
optgroup->reload_config();
m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
// call back for rescaling of the extracolumn control
optgroup->rescale_extra_column_item = [this](wxWindow* win) {
auto *ctrl = dynamic_cast<ScalableButton*>(win);
if (ctrl == nullptr)
return;
ctrl->SetBitmap_(m_bmp_delete);
optgroup->get_field(opt)->m_on_change = [optgroup](const std::string& opt_id, const boost::any& value) {
// first of all take a snapshot and then change value in configuration
wxGetApp().plater()->take_snapshot(wxString::Format(_(L("Change Option %s")), opt_id));
optgroup->on_change_OG(opt_id, value);
};
m_og_settings.push_back(optgroup);
categories.push_back(cat.first);
}
optgroup->reload_config();
m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
m_og_settings.push_back(optgroup);
}
if (m_og_settings.empty()) {
objects_ctrl->select_item(objects_model->Delete(item));
}
else {
if (!categories.empty())
objects_model->UpdateSettingsDigest(item, categories);
}
}
if (!categories.empty())
objects_model->UpdateSettingsDigest(item, categories);
}
else
{
objects_ctrl->select_item(objects_model->Delete(item));
return false;
}
return true;
}
void ObjectSettings::UpdateAndShow(const bool show)
{
if (show)
update_settings_list();
OG_Settings::UpdateAndShow(show);
OG_Settings::UpdateAndShow(show ? update_settings_list() : false);
}
void ObjectSettings::msw_rescale()
{
m_bmp_delete.msw_rescale();
m_bmp_delete_focus.msw_rescale();
for (auto group : m_og_settings)
group->msw_rescale();

View file

@ -39,12 +39,13 @@ class ObjectSettings : public OG_Settings
std::vector <std::shared_ptr<ConfigOptionsGroup>> m_og_settings;
ScalableBitmap m_bmp_delete;
ScalableBitmap m_bmp_delete_focus;
public:
ObjectSettings(wxWindow* parent);
~ObjectSettings() {}
void update_settings_list();
bool update_settings_list();
void UpdateAndShow(const bool show) override;
void msw_rescale();
};

View file

@ -63,7 +63,8 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_
m_canvas->set_config(config);
m_canvas->enable_gizmos(true);
m_canvas->enable_selection(true);
m_canvas->enable_toolbar(true);
m_canvas->enable_main_toolbar(true);
m_canvas->enable_undoredo_toolbar(true);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
@ -175,6 +176,7 @@ Preview::Preview(
, m_checkbox_retractions(nullptr)
, m_checkbox_unretractions(nullptr)
, m_checkbox_shells(nullptr)
, m_checkbox_legend(nullptr)
, m_config(config)
, m_process(process)
, m_gcode_preview_data(gcode_preview_data)
@ -251,6 +253,9 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
m_checkbox_unretractions = new wxCheckBox(this, wxID_ANY, _(L("Unretractions")));
m_checkbox_shells = new wxCheckBox(this, wxID_ANY, _(L("Shells")));
m_checkbox_legend = new wxCheckBox(this, wxID_ANY, _(L("Legend")));
m_checkbox_legend->SetValue(true);
wxBoxSizer* top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(m_canvas_widget, 1, wxALL | wxEXPAND, 0);
top_sizer->Add(m_double_slider_sizer, 0, wxEXPAND, 0);
@ -269,6 +274,8 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view
bottom_sizer->Add(m_checkbox_unretractions, 0, wxEXPAND | wxALL, 5);
bottom_sizer->AddSpacer(10);
bottom_sizer->Add(m_checkbox_shells, 0, wxEXPAND | wxALL, 5);
bottom_sizer->AddSpacer(20);
bottom_sizer->Add(m_checkbox_legend, 0, wxEXPAND | wxALL, 5);
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(top_sizer, 1, wxALL | wxEXPAND, 0);
@ -313,6 +320,12 @@ Preview::~Preview()
}
}
void Preview::set_as_dirty()
{
if (m_canvas != nullptr)
m_canvas->set_as_dirty();
}
void Preview::set_number_extruders(unsigned int number_extruders)
{
if (m_number_extruders != number_extruders)
@ -414,6 +427,18 @@ void Preview::msw_rescale()
refresh_print();
}
void Preview::move_double_slider(wxKeyEvent& evt)
{
if (m_slider)
m_slider->OnKeyDown(evt);
}
void Preview::edit_double_slider(wxKeyEvent& evt)
{
if (m_slider)
m_slider->OnChar(evt);
}
void Preview::bind_event_handlers()
{
this->Bind(wxEVT_SIZE, &Preview::on_size, this);
@ -423,6 +448,7 @@ void Preview::bind_event_handlers()
m_checkbox_retractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this);
m_checkbox_unretractions->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this);
m_checkbox_shells->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this);
m_checkbox_legend->Bind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this);
}
void Preview::unbind_event_handlers()
@ -434,6 +460,7 @@ void Preview::unbind_event_handlers()
m_checkbox_retractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_retractions, this);
m_checkbox_unretractions->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_unretractions, this);
m_checkbox_shells->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_shells, this);
m_checkbox_legend->Unbind(wxEVT_CHECKBOX, &Preview::on_checkbox_legend, this);
}
void Preview::show_hide_ui_elements(const std::string& what)
@ -445,6 +472,7 @@ void Preview::show_hide_ui_elements(const std::string& what)
m_checkbox_retractions->Enable(enable);
m_checkbox_unretractions->Enable(enable);
m_checkbox_shells->Enable(enable);
m_checkbox_legend->Enable(enable);
enable = (what != "none");
m_label_view_type->Enable(enable);
@ -457,6 +485,7 @@ void Preview::show_hide_ui_elements(const std::string& what)
m_checkbox_retractions->Show(visible);
m_checkbox_unretractions->Show(visible);
m_checkbox_shells->Show(visible);
m_checkbox_legend->Show(visible);
m_label_view_type->Show(visible);
m_choice_view_type->Show(visible);
}
@ -523,6 +552,32 @@ void Preview::on_checkbox_shells(wxCommandEvent& evt)
refresh_print();
}
void Preview::on_checkbox_legend(wxCommandEvent& evt)
{
m_canvas->enable_legend_texture(m_checkbox_legend->IsChecked());
m_canvas_widget->Refresh();
}
void Preview::update_view_type()
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
const wxString& choice = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() &&
wxGetApp().extruders_edited_cnt()==1 ?
_(L("Color Print")) :
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
_(L("Tool")) :
_(L("Feature type"));
int type = m_choice_view_type->FindString(choice);
if (m_choice_view_type->GetSelection() != type) {
m_choice_view_type->SetSelection(type);
if (0 <= type && type < (int)GCodePreviewData::Extrusion::Num_View_Types)
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
m_preferred_color_mode = "feature";
}
}
void Preview::create_double_slider()
{
m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100);
@ -535,23 +590,13 @@ void Preview::create_double_slider()
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
auto& config = wxGetApp().preset_bundle->project_config;
((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
m_schedule_background_process();
wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights")->values = m_slider->GetTicksValues();
m_schedule_background_process();
const wxString& choise = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() ? _(L("Color Print")) :
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
_(L("Tool")) : _(L("Feature type"));
update_view_type();
int type = m_choice_view_type->FindString(choise);
if (m_choice_view_type->GetSelection() != type) {
m_choice_view_type->SetSelection(type);
if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
m_preferred_color_mode = "feature";
}
reload_print();
});
reload_print();
});
}
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
@ -673,6 +718,11 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event)
m_slider->SetHigherValue(new_pos);
if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue());
}
else if (key == 'L') {
m_checkbox_legend->SetValue(!m_checkbox_legend->GetValue());
auto evt = wxCommandEvent();
on_checkbox_legend(evt);
}
else if (key == 'S')
m_slider->ChangeOneLayerLock();
else
@ -769,9 +819,14 @@ void Preview::load_print_as_fff(bool keep_z_range)
// Load the real G-code preview.
m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
m_loaded = true;
} else
} else {
// disable color change information for multi-material presets
if (wxGetApp().extruders_edited_cnt() > 1)
color_print_values.clear();
// Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values);
}
show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple");
// recalculates zs and update sliders accordingly
std::vector<double> zs = m_canvas->get_current_print_zs(true);

View file

@ -80,6 +80,7 @@ class Preview : public wxPanel
wxCheckBox* m_checkbox_retractions;
wxCheckBox* m_checkbox_unretractions;
wxCheckBox* m_checkbox_shells;
wxCheckBox* m_checkbox_legend;
DynamicPrintConfig* m_config;
BackgroundSlicingProcess* m_process;
@ -110,6 +111,8 @@ public:
wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; }
GLCanvas3D* get_canvas3d() { return m_canvas; }
void set_as_dirty();
void set_number_extruders(unsigned int number_extruders);
void set_canvas_as_dirty();
void set_enabled(bool enabled);
@ -122,6 +125,12 @@ public:
void refresh_print();
void msw_rescale();
void move_double_slider(wxKeyEvent& evt);
void edit_double_slider(wxKeyEvent& evt);
void update_view_type();
bool is_loaded() const { return m_loaded; }
private:
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);
@ -141,6 +150,7 @@ private:
void on_checkbox_retractions(wxCommandEvent& evt);
void on_checkbox_unretractions(wxCommandEvent& evt);
void on_checkbox_shells(wxCommandEvent& evt);
void on_checkbox_legend(wxCommandEvent& evt);
// Create/Update/Reset double slider on 3dPreview
void create_double_slider();

View file

@ -62,7 +62,7 @@ template<class F> typename F::FN winapi_get_function(const wchar_t *dll, const c
static HINSTANCE dll_handle = LoadLibraryExW(dll, nullptr, 0);
if (dll_handle == nullptr) { return nullptr; }
return (F::FN)GetProcAddress(dll_handle, fn_name);
return (typename F::FN)GetProcAddress(dll_handle, fn_name);
}
#endif

View file

@ -64,6 +64,12 @@ public:
m_prev_scale_factor = m_scale_factor;
m_normal_font = get_default_font_for_dpi(dpi);
/* Because of default window font is a primary display font,
* We should set correct font for window before getting em_unit value.
*/
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
this->SetFont(m_normal_font);
#endif
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
@ -72,6 +78,8 @@ public:
this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
m_new_font_point_size = get_default_font_for_dpi(evt.dpi).GetPointSize();
if (!m_can_rescale)
return;
@ -124,6 +132,8 @@ private:
float m_prev_scale_factor;
bool m_can_rescale{ true };
int m_new_font_point_size;
// void recalc_font()
// {
// wxClientDC dc(this);
@ -135,14 +145,22 @@ private:
// check if new scale is differ from previous
bool is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }
// function for a font scaling of the window
void scale_win_font(wxWindow *window, const int font_point_size)
{
wxFont new_font(window->GetFont());
new_font.SetPointSize(font_point_size);
window->SetFont(new_font);
}
// recursive function for scaling fonts for all controls in Window
void scale_controls_fonts(wxWindow *window, const float scale_f)
void scale_controls_fonts(wxWindow *window, const int font_point_size)
{
auto children = window->GetChildren();
for (auto child : children) {
scale_controls_fonts(child, scale_f);
child->SetFont(child->GetFont().Scaled(scale_f));
scale_controls_fonts(child, font_point_size);
scale_win_font(child, font_point_size);
}
window->Layout();
@ -151,18 +169,18 @@ private:
void rescale(const wxRect &suggested_rect)
{
this->Freeze();
const float relative_scale_factor = m_scale_factor / m_prev_scale_factor;
// rescale fonts of all controls
scale_controls_fonts(this, relative_scale_factor);
this->SetFont(this->GetFont().Scaled(relative_scale_factor));
scale_controls_fonts(this, m_new_font_point_size);
// rescale current window font
scale_win_font(this, m_new_font_point_size);
// rescale normal_font value
m_normal_font = m_normal_font.Scaled(relative_scale_factor);
// set normal application font as a current window font
m_normal_font = this->GetFont();
// An analog of em_unit value from GUI_App.
m_em_unit = std::max<size_t>(10, 10 * m_scale_factor);
// update em_unit value for new window font
m_em_unit = std::max<size_t>(10, this->GetTextExtent("m").x - 1);
// rescale missed controls sizes and images
on_dpi_changed(suggested_rect);

View file

@ -29,19 +29,21 @@ GLGizmoBase::Grabber::Grabber()
color[0] = 1.0f;
color[1] = 1.0f;
color[2] = 1.0f;
color[3] = 1.0f;
}
void GLGizmoBase::Grabber::render(bool hover, float size) const
{
float render_color[3];
float render_color[4];
if (hover)
{
render_color[0] = 1.0f - color[0];
render_color[1] = 1.0f - color[1];
render_color[2] = 1.0f - color[2];
render_color[3] = color[3];
}
else
::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
::memcpy((void*)render_color, (const void*)color, 4 * sizeof(float));
render(size, render_color, true);
}
@ -63,7 +65,7 @@ void GLGizmoBase::Grabber::render(float size, const float* render_color, bool us
if (use_lighting)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor3fv(render_color));
glsafe(::glColor4fv(render_color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(center(0), center(1), center(2)));
@ -132,26 +134,21 @@ void GLGizmoBase::Grabber::render_face(float half_size) const
glsafe(::glEnd());
}
#if ENABLE_SVG_ICONS
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
#else
GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id)
#endif // ENABLE_SVG_ICONS
: m_parent(parent)
, m_group_id(-1)
, m_state(Off)
, m_shortcut_key(0)
#if ENABLE_SVG_ICONS
, m_icon_filename(icon_filename)
#endif // ENABLE_SVG_ICONS
, m_sprite_id(sprite_id)
, m_hover_id(-1)
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
{
::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));
::memcpy((void*)m_base_color, (const void*)DEFAULT_BASE_COLOR, 4 * sizeof(float));
::memcpy((void*)m_drag_color, (const void*)DEFAULT_DRAG_COLOR, 4 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)DEFAULT_HIGHLIGHT_COLOR, 4 * sizeof(float));
}
void GLGizmoBase::set_hover_id(int id)
@ -166,12 +163,12 @@ void GLGizmoBase::set_hover_id(int id)
void GLGizmoBase::set_highlight_color(const float* color)
{
if (color != nullptr)
::memcpy((void*)m_highlight_color, (const void*)color, 3 * sizeof(float));
::memcpy((void*)m_highlight_color, (const void*)color, 4 * sizeof(float));
}
void GLGizmoBase::enable_grabber(unsigned int id)
{
if ((0 <= id) && (id < (unsigned int)m_grabbers.size()))
if (id < m_grabbers.size())
m_grabbers[id].enabled = true;
on_enable_grabber(id);
@ -179,13 +176,13 @@ void GLGizmoBase::enable_grabber(unsigned int id)
void GLGizmoBase::disable_grabber(unsigned int id)
{
if ((0 <= id) && (id < (unsigned int)m_grabbers.size()))
if (id < m_grabbers.size())
m_grabbers[id].enabled = false;
on_disable_grabber(id);
}
void GLGizmoBase::start_dragging(const Selection& selection)
void GLGizmoBase::start_dragging()
{
m_dragging = true;
@ -194,7 +191,7 @@ void GLGizmoBase::start_dragging(const Selection& selection)
m_grabbers[i].dragging = (m_hover_id == i);
}
on_start_dragging(selection);
on_start_dragging();
}
void GLGizmoBase::stop_dragging()
@ -209,13 +206,13 @@ void GLGizmoBase::stop_dragging()
on_stop_dragging();
}
void GLGizmoBase::update(const UpdateData& data, const Selection& selection)
void GLGizmoBase::update(const UpdateData& data)
{
if (m_hover_id != -1)
on_update(data, selection);
on_update(data);
}
std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
std::array<float, 4> GLGizmoBase::picking_color_component(unsigned int id) const
{
static const float INV_255 = 1.0f / 255.0f;
@ -225,9 +222,12 @@ std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
id -= m_group_id;
// color components are encoded to match the calculation of volume_id made into GLCanvas3D::_picking_pass()
return std::array<float, 3> { (float)((id >> 0) & 0xff) * INV_255, // red
(float)((id >> 8) & 0xff) * INV_255, // green
(float)((id >> 16) & 0xff) * INV_255 }; // blue
return std::array<float, 4> {
float((id >> 0) & 0xff) * INV_255, // red
float((id >> 8) & 0xff) * INV_255, // green
float((id >> 16) & 0xff) * INV_255, // blue
float(picking_checksum_alpha_channel(id & 0xff, (id >> 8) & 0xff, (id >> 16) & 0xff))* INV_255 // checksum for validating against unwanted alpha blending and multi sampling
};
}
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
@ -252,10 +252,11 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
{
if (m_grabbers[i].enabled)
{
std::array<float, 3> color = picking_color_component(i);
std::array<float, 4> color = picking_color_component(i);
m_grabbers[i].color[0] = color[0];
m_grabbers[i].color[1] = color[1];
m_grabbers[i].color[2] = color[2];
m_grabbers[i].color[3] = color[3];
m_grabbers[i].render_for_picking(mean_size);
}
}
@ -272,5 +273,20 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const
return Slic3r::string_printf("%.*f", decimals, value);
}
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
{
// 8 bit hash for the color
unsigned char b = ((((37 * red) + green) & 0x0ff) * 37 + blue) & 0x0ff;
// Increase enthropy by a bit reversal
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
// Flip every second bit to increase the enthropy even more.
b ^= 0x55;
return b;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -6,6 +6,7 @@
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/Selection.hpp"
#include <cereal/archives/binary.hpp>
class wxWindow;
class GLUquadric;
@ -20,11 +21,11 @@ class ModelObject;
namespace GUI {
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] = { { 0.75f, 0.0f, 0.0f }, { 0.0f, 0.75f, 0.0f }, { 0.0f, 0.0f, 0.75f } };
static const float CONSTRAINED_COLOR[3] = { 0.5f, 0.5f, 0.5f };
static const float DEFAULT_BASE_COLOR[4] = { 0.625f, 0.625f, 0.625f, 1.0f };
static const float DEFAULT_DRAG_COLOR[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const float DEFAULT_HIGHLIGHT_COLOR[4] = { 1.0f, 0.38f, 0.0f, 1.0f };
static const float AXES_COLOR[][4] = { { 0.75f, 0.0f, 0.0f, 1.0f }, { 0.0f, 0.75f, 0.0f, 1.0f }, { 0.0f, 0.0f, 0.75f, 1.0f } };
static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f };
@ -47,7 +48,7 @@ protected:
Vec3d center;
Vec3d angles;
float color[3];
float color[4];
bool enabled;
bool dragging;
@ -75,10 +76,10 @@ public:
struct UpdateData
{
const Linef3 mouse_ray;
const Point* mouse_pos;
const Linef3& mouse_ray;
const Point& mouse_pos;
UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr)
UpdateData(const Linef3& mouse_ray, const Point& mouse_pos)
: mouse_ray(mouse_ray), mouse_pos(mouse_pos)
{}
};
@ -89,28 +90,25 @@ protected:
int m_group_id;
EState m_state;
int m_shortcut_key;
#if ENABLE_SVG_ICONS
std::string m_icon_filename;
#endif // ENABLE_SVG_ICONS
unsigned int m_sprite_id;
int m_hover_id;
bool m_dragging;
float m_base_color[3];
float m_drag_color[3];
float m_highlight_color[3];
float m_base_color[4];
float m_drag_color[4];
float m_highlight_color[4];
mutable std::vector<Grabber> m_grabbers;
ImGuiWrapper* m_imgui;
public:
#if ENABLE_SVG_ICONS
GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoBase() {}
bool init() { return on_init(); }
void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); }
void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); }
std::string get_name() const { return on_get_name(); }
int get_group_id() const { return m_group_id; }
@ -122,11 +120,9 @@ public:
int get_shortcut_key() const { return m_shortcut_key; }
void set_shortcut_key(int key) { m_shortcut_key = key; }
#if ENABLE_SVG_ICONS
const std::string& get_icon_filename() const { return m_icon_filename; }
#endif // ENABLE_SVG_ICONS
bool is_activable(const Selection& selection) const { return on_is_activable(selection); }
bool is_activable() const { return on_is_activable(); }
bool is_selectable() const { return on_is_selectable(); }
unsigned int get_sprite_id() const { return m_sprite_id; }
@ -139,36 +135,38 @@ public:
void enable_grabber(unsigned int id);
void disable_grabber(unsigned int id);
void start_dragging(const Selection& selection);
void start_dragging();
void stop_dragging();
bool is_dragging() const { return m_dragging; }
void update(const UpdateData& data, const Selection& selection);
void update(const UpdateData& data);
void render(const Selection& selection) const { on_render(selection); }
void render_for_picking(const Selection& selection) const { on_render_for_picking(selection); }
void render_input_window(float x, float y, float bottom_limit, const Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); }
void render() const { on_render(); }
void render_for_picking() const { on_render_for_picking(); }
void render_input_window(float x, float y, float bottom_limit) { on_render_input_window(x, y, bottom_limit); }
protected:
virtual bool on_init() = 0;
virtual void on_load(cereal::BinaryInputArchive& ar) {}
virtual void on_save(cereal::BinaryOutputArchive& ar) const {}
virtual std::string on_get_name() const = 0;
virtual void on_set_state() {}
virtual void on_set_hover_id() {}
virtual bool on_is_activable(const Selection& selection) const { return true; }
virtual bool on_is_activable() const { return true; }
virtual bool on_is_selectable() const { return true; }
virtual void on_enable_grabber(unsigned int id) {}
virtual void on_disable_grabber(unsigned int id) {}
virtual void on_start_dragging(const Selection& selection) {}
virtual void on_start_dragging() {}
virtual void on_stop_dragging() {}
virtual void on_update(const UpdateData& data, const Selection& selection) = 0;
virtual void on_render(const Selection& selection) const = 0;
virtual void on_render_for_picking(const Selection& selection) const = 0;
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) {}
virtual void on_update(const UpdateData& data) {}
virtual void on_render() const = 0;
virtual void on_render_for_picking() const = 0;
virtual void on_render_input_window(float x, float y, float bottom_limit) {}
// Returns the picking color for the given id, based on the BASE_ID constant
// No check is made for clashing with other picking color (i.e. GLVolumes)
std::array<float, 3> picking_color_component(unsigned int id) const;
std::array<float, 4> picking_color_component(unsigned int id) const;
void render_grabbers(const BoundingBoxf3& box) const;
void render_grabbers(float size) const;
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
@ -177,6 +175,10 @@ protected:
std::string format(float value, unsigned int decimals) const;
};
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
// were not interpolated by alpha blending or multi sampling.
extern unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue);
} // namespace GUI
} // namespace Slic3r

View file

@ -15,59 +15,12 @@
namespace Slic3r {
namespace GUI {
class GLGizmoCutPanel : public wxPanel
{
public:
GLGizmoCutPanel(wxWindow *parent);
void display(bool display);
private:
bool m_active;
wxCheckBox *m_cb_rotate;
wxButton *m_btn_cut;
wxButton *m_btn_cancel;
};
GLGizmoCutPanel::GLGizmoCutPanel(wxWindow *parent)
: wxPanel(parent)
, m_active(false)
, m_cb_rotate(new wxCheckBox(this, wxID_ANY, _(L("Rotate lower part upwards"))))
, m_btn_cut(new wxButton(this, wxID_OK, _(L("Perform cut"))))
, m_btn_cancel(new wxButton(this, wxID_CANCEL, _(L("Cancel"))))
{
enum { MARGIN = 5 };
auto *sizer = new wxBoxSizer(wxHORIZONTAL);
auto *label = new wxStaticText(this, wxID_ANY, _(L("Cut object:")));
sizer->Add(label, 0, wxALL | wxALIGN_CENTER, MARGIN);
sizer->Add(m_cb_rotate, 0, wxALL | wxALIGN_CENTER, MARGIN);
sizer->AddStretchSpacer();
sizer->Add(m_btn_cut, 0, wxALL | wxALIGN_CENTER, MARGIN);
sizer->Add(m_btn_cancel, 0, wxALL | wxALIGN_CENTER, MARGIN);
SetSizer(sizer);
}
void GLGizmoCutPanel::display(bool display)
{
Show(display);
GetParent()->Layout();
}
const double GLGizmoCut::Offset = 10.0;
const double GLGizmoCut::Margin = 20.0;
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
const std::array<float, 4> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0, 1.0 };
#if ENABLE_SVG_ICONS
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_cut_z(0.0)
, m_max_z(0.0)
, m_keep_upper(true)
@ -75,7 +28,6 @@ GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id)
, m_rotate_lower(false)
{}
bool GLGizmoCut::on_init()
{
m_grabbers.emplace_back();
@ -96,15 +48,18 @@ void GLGizmoCut::on_set_state()
}
}
bool GLGizmoCut::on_is_activable(const Selection& selection) const
bool GLGizmoCut::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
return selection.is_single_full_instance() && !selection.is_wipe_tower();
}
void GLGizmoCut::on_start_dragging(const Selection& selection)
void GLGizmoCut::on_start_dragging()
{
if (m_hover_id == -1) { return; }
if (m_hover_id == -1)
return;
const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
m_start_z = m_cut_z;
update_max_z(selection);
@ -113,19 +68,21 @@ void GLGizmoCut::on_start_dragging(const Selection& selection)
m_drag_center(2) = m_cut_z;
}
void GLGizmoCut::on_update(const UpdateData& data, const Selection& selection)
void GLGizmoCut::on_update(const UpdateData& data)
{
if (m_hover_id != -1) {
set_cut_z(m_start_z + calc_projection(data.mouse_ray));
}
}
void GLGizmoCut::on_render(const Selection& selection) const
void GLGizmoCut::on_render() const
{
if (m_grabbers[0].dragging) {
set_tooltip("Z: " + format(m_cut_z, 2));
}
const Selection& selection = m_parent.get_selection();
update_max_z(selection);
const BoundingBoxf3& box = selection.get_bounding_box();
@ -171,14 +128,13 @@ void GLGizmoCut::on_render(const Selection& selection) const
m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
}
void GLGizmoCut::on_render_for_picking(const Selection& selection) const
void GLGizmoCut::on_render_for_picking() const
{
glsafe(::glDisable(GL_DEPTH_TEST));
render_grabbers_for_picking(selection.get_bounding_box());
render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
}
void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
{
const float approx_height = m_imgui->scaled(11.0f);
y = std::min(y, bottom_limit - approx_height);
@ -188,7 +144,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
ImGui::PushItemWidth(m_imgui->scaled(5.0f));
bool _value_changed = ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
m_imgui->checkbox(_(L("Keep lower part")), m_keep_lower);
@ -201,7 +157,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co
m_imgui->end();
if (cut_clicked && (m_keep_upper || m_keep_lower)) {
perform_cut(selection);
perform_cut(m_parent.get_selection());
}
}

View file

@ -11,7 +11,7 @@ class GLGizmoCut : public GLGizmoBase
{
static const double Offset;
static const double Margin;
static const std::array<float, 3> GrabberColor;
static const std::array<float, 4> GrabberColor;
mutable double m_cut_z;
double m_start_z;
@ -23,22 +23,20 @@ class GLGizmoCut : public GLGizmoBase
bool m_rotate_lower;
public:
#if ENABLE_SVG_ICONS
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
protected:
virtual bool on_init();
virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual void on_save(cereal::BinaryOutputArchive& ar) const { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); }
virtual std::string on_get_name() const;
virtual void on_set_state();
virtual bool on_is_activable(const Selection& selection) const;
virtual void on_start_dragging(const Selection& selection);
virtual void on_update(const UpdateData& data, const Selection& selection);
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
virtual bool on_is_activable() const;
virtual void on_start_dragging();
virtual void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
virtual void on_render_input_window(float x, float y, float bottom_limit);
private:
void update_max_z(const Selection& selection) const;

View file

@ -1,5 +1,7 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoFlatten.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include <numeric>
@ -9,13 +11,8 @@ namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_normal(Vec3d::Zero())
, m_starting_center(Vec3d::Zero())
{
@ -27,28 +24,46 @@ bool GLGizmoFlatten::on_init()
return true;
}
void GLGizmoFlatten::on_set_state()
{
// m_model_object pointer can be invalid (for instance because of undo/redo action),
// we should recover it from the object id
m_model_object = nullptr;
for (const auto mo : wxGetApp().model().objects) {
if (mo->id() == m_model_object_id) {
m_model_object = mo;
break;
}
}
if (m_state == On && is_plane_update_necessary())
update_planes();
}
std::string GLGizmoFlatten::on_get_name() const
{
return (_(L("Place on face")) + " [F]").ToUTF8().data();
}
bool GLGizmoFlatten::on_is_activable(const Selection& selection) const
bool GLGizmoFlatten::on_is_activable() const
{
return selection.is_single_full_instance();
return m_parent.get_selection().is_single_full_instance();
}
void GLGizmoFlatten::on_start_dragging(const Selection& selection)
void GLGizmoFlatten::on_start_dragging()
{
if (m_hover_id != -1)
{
assert(m_planes_valid);
m_normal = m_planes[m_hover_id].normal;
m_starting_center = selection.get_bounding_box().center();
m_starting_center = m_parent.get_selection().get_bounding_box().center();
}
}
void GLGizmoFlatten::on_render(const Selection& selection) const
void GLGizmoFlatten::on_render() const
{
const Selection& selection = m_parent.get_selection();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
@ -83,8 +98,10 @@ void GLGizmoFlatten::on_render(const Selection& selection) const
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoFlatten::on_render_for_picking(const Selection& selection) const
void GLGizmoFlatten::on_render_for_picking() const
{
const Selection& selection = m_parent.get_selection();
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_BLEND));
@ -98,7 +115,7 @@ void GLGizmoFlatten::on_render_for_picking(const Selection& selection) const
const_cast<GLGizmoFlatten*>(this)->update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i)
{
glsafe(::glColor3fv(picking_color_component(i).data()));
glsafe(::glColor4fv(picking_color_component(i).data()));
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
@ -120,6 +137,7 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
m_planes_valid = false;
}
m_model_object = model_object;
m_model_object_id = model_object ? model_object->id() : 0;
}
void GLGizmoFlatten::update_planes()

View file

@ -31,17 +31,14 @@ private:
bool m_planes_valid = false;
mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
ObjectID m_model_object_id = 0;
std::vector<const Transform3d*> instances_matrices;
void update_planes();
bool is_plane_update_necessary() const;
public:
#if ENABLE_SVG_ICONS
GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_normal() const;
@ -49,16 +46,11 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const Selection& selection) const;
virtual void on_start_dragging(const Selection& selection);
virtual void on_update(const UpdateData& data, const Selection& selection) {}
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
virtual void on_set_state()
{
if (m_state == On && is_plane_update_necessary())
update_planes();
}
virtual bool on_is_activable() const;
virtual void on_start_dragging();
virtual void on_render() const;
virtual void on_render_for_picking() const;
virtual void on_set_state() override;
};
} // namespace GUI

View file

@ -1,5 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoMove.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
@ -10,13 +11,8 @@ namespace GUI {
const double GLGizmoMove3D::Offset = 10.0;
#if ENABLE_SVG_ICONS
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_displacement(Vec3d::Zero())
, m_snap_step(1.0)
, m_starting_drag_position(Vec3d::Zero())
@ -52,12 +48,12 @@ std::string GLGizmoMove3D::on_get_name() const
return (_(L("Move")) + " [M]").ToUTF8().data();
}
void GLGizmoMove3D::on_start_dragging(const Selection& selection)
void GLGizmoMove3D::on_start_dragging()
{
if (m_hover_id != -1)
{
m_displacement = Vec3d::Zero();
const BoundingBoxf3& box = selection.get_bounding_box();
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
m_starting_box_bottom_center = box.center();
@ -70,7 +66,7 @@ void GLGizmoMove3D::on_stop_dragging()
m_displacement = Vec3d::Zero();
}
void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection)
void GLGizmoMove3D::on_update(const UpdateData& data)
{
if (m_hover_id == 0)
m_displacement(0) = calc_projection(data);
@ -80,8 +76,10 @@ void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection
m_displacement(2) = calc_projection(data);
}
void GLGizmoMove3D::on_render(const Selection& selection) const
void GLGizmoMove3D::on_render() const
{
const Selection& selection = m_parent.get_selection();
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
@ -106,15 +104,15 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
// 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));
::memcpy((void*)m_grabbers[0].color, (const void*)&AXES_COLOR[0], 4 * 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));
::memcpy((void*)m_grabbers[1].color, (const void*)&AXES_COLOR[1], 4 * 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));
::memcpy((void*)m_grabbers[2].color, (const void*)&AXES_COLOR[2], 4 * sizeof(float));
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
@ -125,7 +123,7 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
{
if (m_grabbers[i].enabled)
{
glsafe(::glColor3fv(AXES_COLOR[i]));
glsafe(::glColor4fv(AXES_COLOR[i]));
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[i].center.data());
@ -144,7 +142,7 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
else
{
// draw axis
glsafe(::glColor3fv(AXES_COLOR[m_hover_id]));
glsafe(::glColor4fv(AXES_COLOR[m_hover_id]));
::glBegin(GL_LINES);
::glVertex3dv(center.data());
::glVertex3dv(m_grabbers[m_hover_id].center.data());
@ -157,20 +155,21 @@ void GLGizmoMove3D::on_render(const Selection& selection) const
}
}
void GLGizmoMove3D::on_render_for_picking(const Selection& selection) const
void GLGizmoMove3D::on_render_for_picking() const
{
glsafe(::glDisable(GL_DEPTH_TEST));
const BoundingBoxf3& box = selection.get_bounding_box();
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
render_grabbers_for_picking(box);
render_grabber_extension(X, box, true);
render_grabber_extension(Y, box, true);
render_grabber_extension(Z, box, true);
}
void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit)
{
const Selection& selection = m_parent.get_selection();
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
@ -183,8 +182,8 @@ void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit,
m_imgui->input_vec3("", displacement, 100.0f, "%.2f");
m_imgui->end();
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
double GLGizmoMove3D::calc_projection(const UpdateData& data) const
{
@ -221,19 +220,20 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size);
float color[3];
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float));
float color[4];
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 4 * sizeof(float));
if (!picking && (m_hover_id != -1))
{
color[0] = 1.0f - color[0];
color[1] = 1.0f - color[1];
color[2] = 1.0f - color[2];
color[3] = color[3];
}
if (!picking)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor3fv(color));
glsafe(::glColor4fv(color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_grabbers[axis].center(0), m_grabbers[axis].center(1), m_grabbers[axis].center(2)));
if (axis == X)

View file

@ -22,11 +22,7 @@ class GLGizmoMove3D : public GLGizmoBase
GLUquadricObj* m_quadric;
public:
#if ENABLE_SVG_ICONS
GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoMove3D();
double get_snap_step(double step) const { return m_snap_step; }
@ -37,12 +33,14 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual void on_start_dragging(const Selection& selection);
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data, const Selection& selection);
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
virtual void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit);
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
private:
double calc_projection(const UpdateData& data) const;

View file

@ -1,5 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoRotate.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
@ -19,11 +20,7 @@ const unsigned int GLGizmoRotate::SnapRegionsCount = 8;
const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius
GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
#if ENABLE_SVG_ICONS
: GLGizmoBase(parent, "", -1)
#else
: GLGizmoBase(parent, -1)
#endif // ENABLE_SVG_ICONS
, m_axis(axis)
, m_angle(0.0)
, m_quadric(nullptr)
@ -40,11 +37,7 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis)
}
GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other)
#if ENABLE_SVG_ICONS
: GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id)
#else
: GLGizmoBase(other.m_parent, other.m_sprite_id)
#endif // ENABLE_SVG_ICONS
, m_axis(other.m_axis)
, m_angle(other.m_angle)
, m_quadric(nullptr)
@ -80,9 +73,9 @@ bool GLGizmoRotate::on_init()
return true;
}
void GLGizmoRotate::on_start_dragging(const Selection& selection)
void GLGizmoRotate::on_start_dragging()
{
const BoundingBoxf3& box = selection.get_bounding_box();
const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box();
m_center = box.center();
m_radius = Offset + box.radius();
m_snap_coarse_in_radius = m_radius / 3.0f;
@ -91,9 +84,9 @@ void GLGizmoRotate::on_start_dragging(const Selection& selection)
m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth;
}
void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection)
void GLGizmoRotate::on_update(const UpdateData& data)
{
Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, selection));
Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection()));
Vec2d orig_dir = Vec2d::UnitX();
Vec2d new_dir = mouse_pos.normalized();
@ -126,11 +119,12 @@ void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection
m_angle = theta;
}
void GLGizmoRotate::on_render(const Selection& selection) const
void GLGizmoRotate::on_render() const
{
if (!m_grabbers[0].enabled)
return;
const Selection& selection = m_parent.get_selection();
const BoundingBoxf3& box = selection.get_bounding_box();
std::string axis;
@ -161,7 +155,7 @@ void GLGizmoRotate::on_render(const Selection& selection) const
transform_to_local(selection);
glsafe(::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f));
glsafe(::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
render_circle();
@ -172,7 +166,7 @@ void GLGizmoRotate::on_render(const Selection& selection) const
render_reference_radius();
}
glsafe(::glColor3fv(m_highlight_color));
glsafe(::glColor4fv(m_highlight_color));
if (m_hover_id != -1)
render_angle();
@ -183,8 +177,10 @@ void GLGizmoRotate::on_render(const Selection& selection) const
glsafe(::glPopMatrix());
}
void GLGizmoRotate::on_render_for_picking(const Selection& selection) const
void GLGizmoRotate::on_render_for_picking() const
{
const Selection& selection = m_parent.get_selection();
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
@ -291,14 +287,14 @@ void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
m_grabbers[0].angles(2) = m_angle;
glsafe(::glColor3fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
glsafe(::glColor4fv((m_hover_id != -1) ? m_drag_color : m_highlight_color));
::glBegin(GL_LINES);
::glVertex3f(0.0f, 0.0f, 0.0f);
::glVertex3dv(m_grabbers[0].center.data());
glsafe(::glEnd());
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 4 * sizeof(float));
render_grabbers(box);
}
@ -310,8 +306,8 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size);
float color[3];
::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float));
float color[4];
::memcpy((void*)color, (const void*)m_grabbers[0].color, 4 * sizeof(float));
if (!picking && (m_hover_id != -1))
{
color[0] = 1.0f - color[0];
@ -322,7 +318,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
if (!picking)
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glColor3fv(color));
glsafe(::glColor4fv(color));
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_grabbers[0].center(0), m_grabbers[0].center(1), m_grabbers[0].center(2)));
glsafe(::glRotated(Geometry::rad2deg(m_angle), 0.0, 0.0, 1.0));
@ -417,13 +413,8 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons
return transform(mouse_ray, m).intersect_plane(0.0);
}
#if ENABLE_SVG_ICONS
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
{
m_gizmos.emplace_back(parent, GLGizmoRotate::X);
m_gizmos.emplace_back(parent, GLGizmoRotate::Y);
@ -458,10 +449,10 @@ std::string GLGizmoRotate3D::on_get_name() const
return (_(L("Rotate")) + " [R]").ToUTF8().data();
}
void GLGizmoRotate3D::on_start_dragging(const Selection& selection)
void GLGizmoRotate3D::on_start_dragging()
{
if ((0 <= m_hover_id) && (m_hover_id < 3))
m_gizmos[m_hover_id].start_dragging(selection);
m_gizmos[m_hover_id].start_dragging();
}
void GLGizmoRotate3D::on_stop_dragging()
@ -470,23 +461,23 @@ void GLGizmoRotate3D::on_stop_dragging()
m_gizmos[m_hover_id].stop_dragging();
}
void GLGizmoRotate3D::on_render(const Selection& selection) const
void GLGizmoRotate3D::on_render() const
{
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if ((m_hover_id == -1) || (m_hover_id == 0))
m_gizmos[X].render(selection);
m_gizmos[X].render();
if ((m_hover_id == -1) || (m_hover_id == 1))
m_gizmos[Y].render(selection);
m_gizmos[Y].render();
if ((m_hover_id == -1) || (m_hover_id == 2))
m_gizmos[Z].render(selection);
m_gizmos[Z].render();
}
void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit)
{
Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle()));
wxString label = _(L("Rotation (deg)"));
@ -495,8 +486,8 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limi
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", rotation, 100.0f, "%.2f");
m_imgui->end();
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI

View file

@ -52,10 +52,10 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const { return ""; }
virtual void on_start_dragging(const Selection& selection);
virtual void on_update(const UpdateData& data, const Selection& selection);
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
virtual void on_start_dragging();
virtual void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
private:
void render_circle() const;
@ -76,11 +76,7 @@ class GLGizmoRotate3D : public GLGizmoBase
std::vector<GLGizmoRotate> m_gizmos;
public:
#if ENABLE_SVG_ICONS
GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); }
void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); }
@ -97,41 +93,41 @@ protected:
}
virtual void on_set_hover_id()
{
for (unsigned int i = 0; i < 3; ++i)
for (int i = 0; i < 3; ++i)
{
m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1);
}
}
virtual bool on_is_activable(const Selection& selection) const { return true; }
virtual void on_enable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
if (id < 3)
m_gizmos[id].enable_grabber(0);
}
virtual void on_disable_grabber(unsigned int id)
{
if ((0 <= id) && (id < 3))
if (id < 3)
m_gizmos[id].disable_grabber(0);
}
virtual void on_start_dragging(const Selection& selection);
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data, const Selection& selection)
virtual void on_update(const UpdateData& data)
{
for (GLGizmoRotate& g : m_gizmos)
{
g.update(data, selection);
g.update(data);
}
}
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const
virtual void on_render() const;
virtual void on_render_for_picking() const
{
for (const GLGizmoRotate& g : m_gizmos)
{
g.render_for_picking(selection);
g.render_for_picking();
}
}
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit);
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
};

View file

@ -1,7 +1,6 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoScale.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include <GL/glew.h>
@ -13,13 +12,8 @@ namespace GUI {
const float GLGizmoScale3D::Offset = 5.0f;
#if ENABLE_SVG_ICONS
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_scale(Vec3d::Ones())
, m_offset(Vec3d::Zero())
, m_snap_step(0.05)
@ -53,13 +47,18 @@ std::string GLGizmoScale3D::on_get_name() const
return (_(L("Scale")) + " [S]").ToUTF8().data();
}
void GLGizmoScale3D::on_start_dragging(const Selection& selection)
bool GLGizmoScale3D::on_is_activable() const
{
return !m_parent.get_selection().is_wipe_tower();
}
void GLGizmoScale3D::on_start_dragging()
{
if (m_hover_id != -1)
{
m_starting.drag_position = m_grabbers[m_hover_id].center;
m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL);
m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : selection.get_bounding_box();
m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box();
const Vec3d& center = m_starting.box.center();
m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max(0), center(1), center(2));
@ -71,7 +70,7 @@ void GLGizmoScale3D::on_start_dragging(const Selection& selection)
}
}
void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selection)
void GLGizmoScale3D::on_update(const UpdateData& data)
{
if ((m_hover_id == 0) || (m_hover_id == 1))
do_scale_along_axis(X, data);
@ -83,8 +82,10 @@ void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selectio
do_scale_uniform(data);
}
void GLGizmoScale3D::on_render(const Selection& selection) const
void GLGizmoScale3D::on_render() const
{
const Selection& selection = m_parent.get_selection();
bool single_instance = selection.is_single_full_instance();
bool single_volume = selection.is_single_modifier() || selection.is_single_volume();
bool single_selection = single_instance || single_volume;
@ -136,7 +137,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
for (unsigned int idx : idxs)
{
const GLVolume* vol = selection.get_volume(idx);
m_box.merge(vol->bounding_box.transformed(vol->get_volume_transformation().get_matrix()));
m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix()));
}
// gets transform from first selected volume
@ -151,7 +152,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if (single_volume)
{
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
m_box = v->bounding_box;
m_box = v->bounding_box();
m_transform = v->world_matrix();
angles = Geometry::extract_euler_angles(m_transform);
// consider rotation+mirror only components of the transform for offsets
@ -171,20 +172,20 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
// x axis
m_grabbers[0].center = m_transform * Vec3d(m_box.min(0), center(1), center(2)) - offset_x;
m_grabbers[1].center = m_transform * Vec3d(m_box.max(0), center(1), center(2)) + offset_x;
::memcpy((void*)m_grabbers[0].color, (ctrl_down && (m_hover_id == 1)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (ctrl_down && (m_hover_id == 0)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 3 * sizeof(float));
::memcpy((void*)m_grabbers[0].color, (ctrl_down && (m_hover_id == 1)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 4 * sizeof(float));
::memcpy((void*)m_grabbers[1].color, (ctrl_down && (m_hover_id == 0)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[0], 4 * sizeof(float));
// y axis
m_grabbers[2].center = m_transform * Vec3d(center(0), m_box.min(1), center(2)) - offset_y;
m_grabbers[3].center = m_transform * Vec3d(center(0), m_box.max(1), center(2)) + offset_y;
::memcpy((void*)m_grabbers[2].color, (ctrl_down && (m_hover_id == 3)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (ctrl_down && (m_hover_id == 2)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 3 * sizeof(float));
::memcpy((void*)m_grabbers[2].color, (ctrl_down && (m_hover_id == 3)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 4 * sizeof(float));
::memcpy((void*)m_grabbers[3].color, (ctrl_down && (m_hover_id == 2)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[1], 4 * sizeof(float));
// z axis
m_grabbers[4].center = m_transform * Vec3d(center(0), center(1), m_box.min(2)) - offset_z;
m_grabbers[5].center = m_transform * Vec3d(center(0), center(1), m_box.max(2)) + offset_z;
::memcpy((void*)m_grabbers[4].color, (ctrl_down && (m_hover_id == 5)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (ctrl_down && (m_hover_id == 4)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 3 * sizeof(float));
::memcpy((void*)m_grabbers[4].color, (ctrl_down && (m_hover_id == 5)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 4 * sizeof(float));
::memcpy((void*)m_grabbers[5].color, (ctrl_down && (m_hover_id == 4)) ? (const void*)CONSTRAINED_COLOR : (const void*)&AXES_COLOR[2], 4 * sizeof(float));
// uniform
m_grabbers[6].center = m_transform * Vec3d(m_box.min(0), m_box.min(1), center(2)) - offset_x - offset_y;
@ -193,7 +194,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
m_grabbers[9].center = m_transform * Vec3d(m_box.min(0), m_box.max(1), center(2)) - offset_x + offset_y;
for (int i = 6; i < 10; ++i)
{
::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 3 * sizeof(float));
::memcpy((void*)m_grabbers[i].color, (const void*)m_highlight_color, 4 * sizeof(float));
}
// sets grabbers orientation
@ -213,20 +214,20 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
// draw connections
if (m_grabbers[0].enabled && m_grabbers[1].enabled)
{
glsafe(::glColor3fv(m_grabbers[0].color));
glsafe(::glColor4fv(m_grabbers[0].color));
render_grabbers_connection(0, 1);
}
if (m_grabbers[2].enabled && m_grabbers[3].enabled)
{
glsafe(::glColor3fv(m_grabbers[2].color));
glsafe(::glColor4fv(m_grabbers[2].color));
render_grabbers_connection(2, 3);
}
if (m_grabbers[4].enabled && m_grabbers[5].enabled)
{
glsafe(::glColor3fv(m_grabbers[4].color));
glsafe(::glColor4fv(m_grabbers[4].color));
render_grabbers_connection(4, 5);
}
glsafe(::glColor3fv(m_base_color));
glsafe(::glColor4fv(m_base_color));
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
@ -237,7 +238,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
// draw connection
glsafe(::glColor3fv(m_grabbers[0].color));
glsafe(::glColor4fv(m_grabbers[0].color));
render_grabbers_connection(0, 1);
// draw grabbers
m_grabbers[0].render(true, grabber_mean_size);
@ -246,7 +247,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
// draw connection
glsafe(::glColor3fv(m_grabbers[2].color));
glsafe(::glColor4fv(m_grabbers[2].color));
render_grabbers_connection(2, 3);
// draw grabbers
m_grabbers[2].render(true, grabber_mean_size);
@ -255,7 +256,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
// draw connection
glsafe(::glColor3fv(m_grabbers[4].color));
glsafe(::glColor4fv(m_grabbers[4].color));
render_grabbers_connection(4, 5);
// draw grabbers
m_grabbers[4].render(true, grabber_mean_size);
@ -264,7 +265,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if (m_hover_id >= 6)
{
// draw connection
glsafe(::glColor3fv(m_drag_color));
glsafe(::glColor4fv(m_drag_color));
render_grabbers_connection(6, 7);
render_grabbers_connection(7, 8);
render_grabbers_connection(8, 9);
@ -277,16 +278,16 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
}
}
void GLGizmoScale3D::on_render_for_picking(const Selection& selection) const
void GLGizmoScale3D::on_render_for_picking() const
{
glsafe(::glDisable(GL_DEPTH_TEST));
render_grabbers_for_picking(selection.get_bounding_box());
render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
}
void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
{
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit)
{
const Selection& selection = m_parent.get_selection();
bool single_instance = selection.is_single_full_instance();
wxString label = _(L("Scale (%)"));
@ -295,8 +296,8 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f");
m_imgui->end();
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
}
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const
{

View file

@ -32,11 +32,7 @@ class GLGizmoScale3D : public GLGizmoBase
StartingData m_starting;
public:
#if ENABLE_SVG_ICONS
GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
double get_snap_step(double step) const { return m_snap_step; }
void set_snap_step(double step) { m_snap_step = step; }
@ -49,12 +45,14 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const Selection& selection) const { return !selection.is_wipe_tower(); }
virtual void on_start_dragging(const Selection& selection);
virtual void on_update(const UpdateData& data, const Selection& selection);
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection);
virtual bool on_is_activable() const;
virtual void on_start_dragging();
virtual void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit);
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
private:
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;

View file

@ -19,14 +19,10 @@
namespace Slic3r {
namespace GUI {
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id)
#else
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id)
: GLGizmoBase(parent, sprite_id)
#endif // ENABLE_SVG_ICONS
, m_quadric(nullptr)
, m_its(nullptr)
{
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
@ -63,15 +59,16 @@ bool GLGizmoSlaSupports::on_init()
void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection)
{
if (selection.is_empty()) {
if (! model_object || selection.is_empty()) {
m_model_object = nullptr;
return;
}
if (m_model_object != model_object)
if (m_model_object != model_object || m_model_object_id != model_object->id()) {
m_model_object = model_object;
m_print_object_idx = -1;
}
m_model_object = model_object;
m_active_instance = selection.get_instance_idx();
if (model_object && selection.is_from_single_instance())
@ -83,10 +80,11 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
if (is_mesh_update_necessary()) {
update_mesh();
editing_mode_reload_cache();
reload_cache();
}
if (m_editing_mode_cache.empty() && m_model_object->sla_points_status != sla::PointsStatus::UserModified)
// If we triggered autogeneration before, check backend and fetch results if they are there
if (m_model_object->sla_points_status == sla::PointsStatus::Generating)
get_data_from_backend();
if (m_state == On) {
@ -98,16 +96,24 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
}
}
void GLGizmoSlaSupports::on_render(const Selection& selection) const
void GLGizmoSlaSupports::on_render() const
{
const Selection& selection = m_parent.get_selection();
// If current m_model_object does not match selection, ask GLCanvas3D to turn us off
if (m_state == On
&& (m_model_object != selection.get_model()->objects[selection.get_object_idx()]
|| m_active_instance != selection.get_instance_idx())) {
|| m_active_instance != selection.get_instance_idx()
|| m_model_object_id != m_model_object->id())) {
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_RESETGIZMOS));
return;
}
if (! m_its || ! m_mesh)
const_cast<GLGizmoSlaSupports*>(this)->update_mesh();
glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
@ -256,8 +262,13 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
}
void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
void GLGizmoSlaSupports::on_render_for_picking() const
{
const Selection& selection = m_parent.get_selection();
#if ENABLE_RENDER_PICKING_PASS
m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z();
#endif
glsafe(::glEnable(GL_DEPTH_TEST));
render_points(selection, true);
}
@ -275,30 +286,33 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
glsafe(::glTranslated(0.0, 0.0, m_z_shift));
glsafe(::glMultMatrixd(instance_matrix.data()));
float render_color[3];
for (int i = 0; i < (int)m_editing_mode_cache.size(); ++i)
float render_color[4];
size_t cache_size = m_editing_mode ? m_editing_cache.size() : m_normal_cache.size();
for (size_t i = 0; i < cache_size; ++i)
{
const sla::SupportPoint& support_point = m_editing_mode_cache[i].support_point;
const bool& point_selected = m_editing_mode_cache[i].selected;
const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
if (is_point_clipped(support_point.pos.cast<double>()))
continue;
// First decide about the color of the point.
if (picking) {
std::array<float, 3> color = picking_color_component(i);
std::array<float, 4> color = picking_color_component(i);
render_color[0] = color[0];
render_color[1] = color[1];
render_color[2] = color[2];
render_color[3] = color[3];
}
else {
render_color[3] = 1.f;
if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active
render_color[0] = 0.f;
render_color[1] = 1.0f;
render_color[2] = 1.0f;
}
else { // neigher hover nor picking
bool supports_new_island = m_lock_unique_islands && m_editing_mode_cache[i].support_point.is_new_island;
bool supports_new_island = m_lock_unique_islands && support_point.is_new_island;
if (m_editing_mode) {
render_color[0] = point_selected ? 1.0f : (supports_new_island ? 0.3f : 0.7f);
render_color[1] = point_selected ? 0.3f : (supports_new_island ? 0.3f : 0.7f);
@ -308,7 +322,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
for (unsigned char i=0; i<3; ++i) render_color[i] = 0.5f;
}
}
glsafe(::glColor3fv(render_color));
glsafe(::glColor4fv(render_color));
float render_color_emissive[4] = { 0.5f * render_color[0], 0.5f * render_color[1], 0.5f * render_color[2], 1.f};
glsafe(::glMaterialfv(GL_FRONT, GL_EMISSION, render_color_emissive));
@ -323,24 +337,24 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
// Matrices set, we can render the point mark now.
// If in editing mode, we'll also render a cone pointing to the sphere.
if (m_editing_mode) {
if (m_editing_mode_cache[i].normal == Vec3f::Zero())
if (m_editing_cache[i].normal == Vec3f::Zero())
update_cache_entry_normal(i); // in case the normal is not yet cached, find and cache it
Eigen::Quaterniond q;
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_mode_cache[i].normal.cast<double>());
q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast<double>());
Eigen::AngleAxisd aa(q);
glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2)));
const float cone_radius = 0.25f; // mm
const float cone_height = 0.75f;
glsafe(::glPushMatrix());
glsafe(::glTranslatef(0.f, 0.f, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale));
glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale));
::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 24, 1);
glsafe(::glTranslatef(0.f, 0.f, cone_height));
::gluDisk(m_quadric, 0.0, cone_radius, 24, 1);
glsafe(::glPopMatrix());
}
::gluSphere(m_quadric, m_editing_mode_cache[i].support_point.head_front_radius * RenderPointScale, 24, 12);
::gluSphere(m_quadric, support_point.head_front_radius * RenderPointScale, 24, 12);
if (vol->is_left_handed())
glFrontFace(GL_CCW);
@ -379,44 +393,43 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
bool GLGizmoSlaSupports::is_mesh_update_necessary() const
{
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
&& ((m_model_object->id() != m_current_mesh_model_id) || m_V.size()==0);
&& ((m_model_object->id() != m_model_object_id) || m_its == nullptr);
}
void GLGizmoSlaSupports::update_mesh()
{
if (! m_model_object)
return;
wxBusyCursor wait;
Eigen::MatrixXf& V = m_V;
Eigen::MatrixXi& F = m_F;
// We rely on SLA model object having a single volume,
// this way we can use that mesh directly.
// This mesh does not account for the possible Z up SLA offset.
m_mesh = &m_model_object->volumes.front()->mesh;
const_cast<TriangleMesh*>(m_mesh)->require_shared_vertices(); // TriangleMeshSlicer needs this
const stl_file& stl = m_mesh->stl;
V.resize(3 * stl.stats.number_of_facets, 3);
F.resize(stl.stats.number_of_facets, 3);
for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) {
const stl_facet* facet = stl.facet_start+i;
V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2);
V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2);
V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2);
F(i, 0) = 3*i+0;
F(i, 1) = 3*i+1;
F(i, 2) = 3*i+2;
}
m_current_mesh_model_id = m_model_object->id();
m_editing_mode = false;
m_mesh = &m_model_object->volumes.front()->mesh();
m_its = &m_mesh->its;
m_AABB = igl::AABB<Eigen::MatrixXf,3>();
m_AABB.init(m_V, m_F);
// If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it.
if (m_model_object_id != m_model_object->id() || (m_AABB.m_left == NULL && m_AABB.m_right == NULL))
{
m_AABB.deinit();
m_AABB.init(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3));
}
m_model_object_id = m_model_object->id();
disable_editing_mode();
}
// Unprojects the mouse position on the mesh and return the hit point and normal of the facet.
// The function throws if no intersection if found.
std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos)
// Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal
// Return false if no intersection was found, true otherwise.
bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal)
{
// if the gizmo doesn't have the V, F structures for igl, calculate them first:
if (m_V.size() == 0)
if (m_its == nullptr)
update_mesh();
const Camera& camera = m_parent.get_camera();
@ -442,8 +455,11 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
point1 = inv * point1;
point2 = inv * point2;
if (!m_AABB.intersect_ray(m_V, m_F, point1.cast<float>(), (point2-point1).cast<float>(), hits))
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
if (!m_AABB.intersect_ray(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
point1.cast<float>(), (point2-point1).cast<float>(), hits))
return false; // no intersection found
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
@ -457,9 +473,9 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
igl::Hit& hit = hits[i];
int fid = hit.id; // facet id
bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
result = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
result = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
if (m_clipping_plane_distance == 0.f || !is_point_clipped(result.cast<double>()))
break;
}
@ -467,14 +483,12 @@ std::pair<Vec3f, Vec3f> GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
// All hits are either clipped, or there is an odd number of unclipped
// hits - meaning the nearest must be from inside the mesh.
throw std::invalid_argument("unproject_on_mesh(): No intersection found.");
return false;
}
// Calculate and return both the point and the facet normal.
return std::make_pair(
result,
a.cross(b)
);
pos_and_normal = std::make_pair(result, a.cross(b));
return true;
}
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
@ -493,7 +507,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
}
}
else {
if (m_editing_mode_cache[m_hover_id].selected)
if (m_editing_cache[m_hover_id].selected)
unselect_point(m_hover_id);
else {
if (!alt_down)
@ -512,16 +526,15 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// If there is some selection, don't add new point and deselect everything instead.
if (m_selection_empty) {
try {
std::pair<Vec3f, Vec3f> pos_and_normal = unproject_on_mesh(mouse_position); // don't create anything if this throws
m_editing_mode_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
m_unsaved_changes = true;
std::pair<Vec3f, Vec3f> pos_and_normal;
if (unproject_on_mesh(mouse_position, pos_and_normal)) { // we got an intersection
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Add support point")));
m_editing_cache.emplace_back(sla::SupportPoint(pos_and_normal.first, m_new_point_head_diameter/2.f, false), false, pos_and_normal.second);
m_parent.set_as_dirty();
m_wait_for_up_event = true;
}
catch (...) { // not clicked on object
else
return false;
}
}
else
select_point(NoPoints);
@ -537,8 +550,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// First collect positions of all the points in world coordinates.
const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix();
std::vector<Vec3d> points;
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
for (unsigned int i=0; i<m_editing_cache.size(); ++i) {
const sla::SupportPoint &support_point = m_editing_cache[i].support_point;
points.push_back(instance_matrix * support_point.pos.cast<double>());
points.back()(2) += m_z_shift;
}
@ -550,7 +563,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true);
Vec3f direction_to_camera = camera.get_dir_forward().cast<float>();
Vec3f direction_to_camera = -camera.get_dir_forward().cast<float>();
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
Vec3f scaling = volume->get_instance_scaling_factor().cast<float>();
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
@ -558,21 +571,24 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
// Iterate over all points in the rectangle and check that they are neither clipped by the
// clipping plane nor obscured by the mesh.
for (const unsigned int i : selected_idxs) {
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
const sla::SupportPoint &support_point = m_editing_cache[i].support_point;
if (!is_point_clipped(support_point.pos.cast<double>())) {
bool is_obscured = false;
// Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<igl::Hit> hits;
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
if (m_AABB.intersect_ray(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
support_point.pos + direction_to_camera_mesh * (support_point.head_front_radius + EPSILON), direction_to_camera_mesh, hits)) {
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
if (m_clipping_plane_distance != 0.f) {
// If the closest hit facet normal points in the same direction as the ray,
// we are looking through the mesh and should therefore discard the point:
int fid = hits.front().id; // facet id
Vec3f a = (m_V.row(m_F(fid, 1)) - m_V.row(m_F(fid, 0)));
Vec3f b = (m_V.row(m_F(fid, 2)) - m_V.row(m_F(fid, 0)));
Vec3f a = (m_its->vertices[m_its->indices[fid](1)] - m_its->vertices[m_its->indices[fid](0)]);
Vec3f b = (m_its->vertices[m_its->indices[fid](2)] - m_its->vertices[m_its->indices[fid](0)]);
if ((a.cross(b)).dot(direction_to_camera_mesh) > 0.f)
is_obscured = true;
@ -582,7 +598,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
int fid = hit.id; // facet id
Vec3f bc = Vec3f(1-hit.u-hit.v, hit.u, hit.v); // barycentric coordinates of the hit
Vec3f hit_pos = bc(0) * m_V.row(m_F(fid, 0)) + bc(1) * m_V.row(m_F(fid, 1)) + bc(2)*m_V.row(m_F(fid, 2));
Vec3f hit_pos = bc(0) * m_its->vertices[m_its->indices[fid](0)] + bc(1) * m_its->vertices[m_its->indices[fid](1)] + bc(2)*m_its->vertices[m_its->indices[fid](2)];
if (is_point_clipped(hit_pos.cast<double>())) {
hits.erase(hits.begin()+j);
--j;
@ -695,13 +711,17 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
void GLGizmoSlaSupports::delete_selected_points(bool force)
{
for (unsigned int idx=0; idx<m_editing_mode_cache.size(); ++idx) {
if (m_editing_mode_cache[idx].selected && (!m_editing_mode_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
m_editing_mode_cache.erase(m_editing_mode_cache.begin() + (idx--));
m_unsaved_changes = true;
if (! m_editing_mode) {
std::cout << "DEBUGGING: delete_selected_points called out of editing mode!" << std::endl;
std::abort();
}
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Delete support point")));
for (unsigned int idx=0; idx<m_editing_cache.size(); ++idx) {
if (m_editing_cache[idx].selected && (!m_editing_cache[idx].support_point.is_new_island || !m_lock_unique_islands || force)) {
m_editing_cache.erase(m_editing_cache.begin() + (idx--));
}
// This should trigger the support generation
// wxGetApp().plater()->reslice_SLA_supports(*m_model_object);
}
select_point(NoPoints);
@ -709,20 +729,21 @@ void GLGizmoSlaSupports::delete_selected_points(bool force)
//m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
void GLGizmoSlaSupports::on_update(const UpdateData& data, const Selection& selection)
void GLGizmoSlaSupports::on_update(const UpdateData& data)
{
if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
std::pair<Vec3f, Vec3f> pos_and_normal;
try {
pos_and_normal = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1)));
if (! m_editing_mode)
return;
else {
if (m_hover_id != -1 && (! m_editing_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) {
std::pair<Vec3f, Vec3f> pos_and_normal;
if (! unproject_on_mesh(data.mouse_pos.cast<double>(), pos_and_normal))
return;
m_editing_cache[m_hover_id].support_point.pos = pos_and_normal.first;
m_editing_cache[m_hover_id].support_point.is_new_island = false;
m_editing_cache[m_hover_id].normal = pos_and_normal.second;
// Do not update immediately, wait until the mouse is released.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
catch (...) { return; }
m_editing_mode_cache[m_hover_id].support_point.pos = pos_and_normal.first;
m_editing_mode_cache[m_hover_id].support_point.is_new_island = false;
m_editing_mode_cache[m_hover_id].normal = pos_and_normal.second;
m_unsaved_changes = true;
// Do not update immediately, wait until the mouse is released.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
}
@ -757,12 +778,15 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st
void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const
{
int idx = 0;
Eigen::Matrix<float, 1, 3> pp = m_editing_mode_cache[i].support_point.pos;
Eigen::Matrix<float, 1, 3> pp = m_editing_cache[i].support_point.pos;
Eigen::Matrix<float, 1, 3> cc;
m_AABB.squared_distance(m_V, m_F, pp, idx, cc);
Vec3f a = (m_V.row(m_F(idx, 1)) - m_V.row(m_F(idx, 0)));
Vec3f b = (m_V.row(m_F(idx, 2)) - m_V.row(m_F(idx, 0)));
m_editing_mode_cache[i].normal = a.cross(b);
m_AABB.squared_distance(
MapMatrixXfUnaligned(m_its->vertices.front().data(), m_its->vertices.size(), 3),
MapMatrixXiUnaligned(m_its->indices.front().data(), m_its->indices.size(), 3),
pp, idx, cc);
Vec3f a = (m_its->vertices[m_its->indices[idx](1)] - m_its->vertices[m_its->indices[idx](0)]);
Vec3f b = (m_its->vertices[m_its->indices[idx](2)] - m_its->vertices[m_its->indices[idx](0)]);
m_editing_cache[i].normal = a.cross(b);
}
@ -826,7 +850,7 @@ void GLGizmoSlaSupports::make_line_segments() const
*/
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection)
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit)
{
if (!m_model_object)
return;
@ -871,13 +895,34 @@ RENDER_AGAIN:
ImGui::SameLine(diameter_slider_left);
ImGui::PushItemWidth(window_width - diameter_slider_left);
if (ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f")) {
// value was changed
for (auto& cache_entry : m_editing_mode_cache)
if (cache_entry.selected) {
// Following is a nasty way to:
// - save the initial value of the slider before one starts messing with it
// - keep updating the head radius during sliding so it is continuosly refreshed in 3D scene
// - take correct undo/redo snapshot after the user is done with moving the slider
float initial_value = m_new_point_head_diameter;
ImGui::SliderFloat("", &m_new_point_head_diameter, 0.1f, diameter_upper_cap, "%.1f");
if (ImGui::IsItemClicked()) {
if (m_old_point_head_diameter == 0.f)
m_old_point_head_diameter = initial_value;
}
if (ImGui::IsItemEdited()) {
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f;
m_unsaved_changes = true;
}
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
// momentarily restore the old value to take snapshot
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.support_point.head_front_radius = m_old_point_head_diameter / 2.f;
float backup = m_new_point_head_diameter;
m_new_point_head_diameter = m_old_point_head_diameter;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change point head diameter")));
m_new_point_head_diameter = backup;
for (auto& cache_entry : m_editing_cache)
if (cache_entry.selected)
cache_entry.support_point.head_front_radius = m_new_point_head_diameter / 2.f;
m_old_point_head_diameter = 0.f;
}
bool changed = m_lock_unique_islands;
@ -888,7 +933,7 @@ RENDER_AGAIN:
remove_selected = m_imgui->button(m_desc.at("remove_selected"));
m_imgui->disabled_end();
m_imgui->disabled_begin(m_editing_mode_cache.empty());
m_imgui->disabled_begin(m_editing_cache.empty());
remove_all = m_imgui->button(m_desc.at("remove_all"));
m_imgui->disabled_end();
@ -914,23 +959,34 @@ RENDER_AGAIN:
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
bool value_changed = ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
if (value_changed)
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
bool slider_clicked = ImGui::IsItemClicked(); // someone clicked the slider
bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
m_imgui->text(m_desc.at("points_density"));
ImGui::SameLine(settings_sliders_left);
if (ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%")) {
value_changed = true;
ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%");
slider_clicked |= ImGui::IsItemClicked();
slider_edited |= ImGui::IsItemEdited();
slider_released |= ImGui::IsItemDeactivatedAfterEdit();
if (slider_clicked) { // stash the values of the settings so we know what to revert to after undo
m_minimal_point_distance_stash = minimal_point_distance;
m_density_stash = density;
}
if (slider_edited) {
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
}
if (value_changed) { // Update side panel
wxTheApp->CallAfter([]() {
wxGetApp().obj_settings()->UpdateAndShow(true);
wxGetApp().obj_list()->update_settings_items();
});
if (slider_released) {
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = m_minimal_point_distance_stash;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)m_density_stash;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support parameter change")));
m_model_object->config.opt<ConfigOptionFloat>("support_points_minimal_distance", true)->value = minimal_point_distance;
m_model_object->config.opt<ConfigOptionInt>("support_points_density_relative", true)->value = (int)density;
wxGetApp().obj_list()->update_and_show_object_settings_item();
}
bool generate = m_imgui->button(m_desc.at("auto_generate"));
@ -942,12 +998,12 @@ RENDER_AGAIN:
if (m_imgui->button(m_desc.at("manual_editing")))
switch_to_editing_mode();
m_imgui->disabled_begin(m_editing_mode_cache.empty());
m_imgui->disabled_begin(m_normal_cache.empty());
remove_all = m_imgui->button(m_desc.at("remove_all"));
m_imgui->disabled_end();
// m_imgui->text("");
// m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::None ? _(L("No points (will be autogenerated)")) :
// m_imgui->text(m_model_object->sla_points_status == sla::PointsStatus::NoPoints ? _(L("No points (will be autogenerated)")) :
// (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated ? _(L("Autogenerated points (no modifications)")) :
// (m_model_object->sla_points_status == sla::PointsStatus::UserModified ? _(L("User-modified points")) :
// (m_model_object->sla_points_status == sla::PointsStatus::Generating ? _(L("Generation in progress...")) : "UNKNOWN STATUS"))));
@ -989,11 +1045,18 @@ RENDER_AGAIN:
if (remove_selected || remove_all) {
force_refresh = false;
m_parent.set_as_dirty();
if (remove_all)
bool was_in_editing = m_editing_mode;
if (! was_in_editing)
switch_to_editing_mode();
if (remove_all) {
select_point(AllPoints);
delete_selected_points(remove_all);
if (remove_all && !m_editing_mode)
delete_selected_points(true); // true - delete regardless of locked status
}
if (remove_selected)
delete_selected_points(false); // leave locked points
if (! was_in_editing)
editing_mode_apply_changes();
if (first_run) {
first_run = false;
goto RENDER_AGAIN;
@ -1004,11 +1067,13 @@ RENDER_AGAIN:
m_parent.set_as_dirty();
}
bool GLGizmoSlaSupports::on_is_activable(const Selection& selection) const
bool GLGizmoSlaSupports::on_is_activable() const
{
const Selection& selection = m_parent.get_selection();
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA
|| !selection.is_from_single_instance())
return false;
return false;
// Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside.
const Selection::IndicesList& list = selection.get_volume_idxs();
@ -1029,88 +1094,166 @@ std::string GLGizmoSlaSupports::on_get_name() const
return (_(L("SLA Support Points")) + " [L]").ToUTF8().data();
}
void GLGizmoSlaSupports::on_set_state()
{
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
if (is_mesh_update_necessary())
update_mesh();
if (m_state == Hover)
return;
// we'll now reload support points:
if (m_model_object)
editing_mode_reload_cache();
m_parent.toggle_model_objects_visibility(false);
if (m_model_object)
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
// Set default head diameter from config.
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
// m_model_object pointer can be invalid (for instance because of undo/redo action),
// we should recover it from the object id
m_model_object = nullptr;
for (const auto mo : wxGetApp().model().objects) {
if (mo->id() == m_model_object_id) {
m_model_object = mo;
break;
}
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
}
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on")));
if (is_mesh_update_necessary())
update_mesh();
// we'll now reload support points:
if (m_model_object)
reload_cache();
m_parent.toggle_model_objects_visibility(false);
if (m_model_object)
m_parent.toggle_model_objects_visibility(true, m_model_object, m_active_instance);
// Set default head diameter from config.
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
}
if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off
bool will_ask = m_model_object && m_editing_mode && unsaved_changes();
if (will_ask) {
wxGetApp().CallAfter([this]() {
// Following is called through CallAfter, because otherwise there was a problem
// on OSX with the wxMessageDialog being shown several times when clicked into.
if (m_model_object) {
if (m_unsaved_changes) {
wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually edited support points?")) + "\n",
_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
if (dlg.ShowModal() == wxID_YES)
editing_mode_apply_changes();
else
editing_mode_discard_changes();
}
}
m_parent.toggle_model_objects_visibility(true);
m_editing_mode = false; // so it is not active next time the gizmo opens
m_editing_mode_cache.clear();
m_clipping_plane_distance = 0.f;
// Release triangle mesh slicer and the AABB spatial search structure.
m_AABB.deinit();
m_V = Eigen::MatrixXf();
m_F = Eigen::MatrixXi();
m_tms.reset();
m_supports_tms.reset();
wxMessageDialog dlg(GUI::wxGetApp().mainframe, _(L("Do you want to save your manually "
"edited support points?")) + "\n",_(L("Save changes?")), wxICON_QUESTION | wxYES | wxNO);
if (dlg.ShowModal() == wxID_YES)
editing_mode_apply_changes();
else
editing_mode_discard_changes();
});
// refuse to be turned off so the gizmo is active when the CallAfter is executed
m_state = m_old_state;
}
m_old_state = m_state;
else {
// we are actually shutting down
disable_editing_mode(); // so it is not active next time the gizmo opens
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off")));
m_parent.toggle_model_objects_visibility(true);
m_normal_cache.clear();
m_clipping_plane_distance = 0.f;
// Release triangle mesh slicer and the AABB spatial search structure.
m_AABB.deinit();
m_its = nullptr;
m_tms.reset();
m_supports_tms.reset();
}
}
m_old_state = m_state;
}
void GLGizmoSlaSupports::on_start_dragging(const Selection& selection)
void GLGizmoSlaSupports::on_start_dragging()
{
if (m_hover_id != -1) {
select_point(NoPoints);
select_point(m_hover_id);
m_point_before_drag = m_editing_cache[m_hover_id];
}
else
m_point_before_drag = CacheEntry();
}
void GLGizmoSlaSupports::on_stop_dragging()
{
if (m_hover_id != -1) {
CacheEntry backup = m_editing_cache[m_hover_id];
if (m_point_before_drag.support_point.pos != Vec3f::Zero() // some point was touched
&& backup.support_point.pos != m_point_before_drag.support_point.pos) // and it was moved, not just selected
{
m_editing_cache[m_hover_id] = m_point_before_drag;
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move support point")));
m_editing_cache[m_hover_id] = backup;
}
}
m_point_before_drag = CacheEntry();
}
void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar)
{
ar(m_clipping_plane_distance,
m_clipping_plane_normal,
m_model_object_id,
m_new_point_head_diameter,
m_normal_cache,
m_editing_cache,
m_selection_empty
);
}
void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const
{
ar(m_clipping_plane_distance,
m_clipping_plane_normal,
m_model_object_id,
m_new_point_head_diameter,
m_normal_cache,
m_editing_cache,
m_selection_empty
);
}
void GLGizmoSlaSupports::select_point(int i)
{
if (! m_editing_mode) {
std::cout << "DEBUGGING: select_point called when out of editing mode!" << std::endl;
std::abort();
}
if (i == AllPoints || i == NoPoints) {
for (auto& point_and_selection : m_editing_mode_cache)
for (auto& point_and_selection : m_editing_cache)
point_and_selection.selected = ( i == AllPoints );
m_selection_empty = (i == NoPoints);
if (i == AllPoints)
m_new_point_head_diameter = m_editing_mode_cache[0].support_point.head_front_radius * 2.f;
m_new_point_head_diameter = m_editing_cache[0].support_point.head_front_radius * 2.f;
}
else {
m_editing_mode_cache[i].selected = true;
m_editing_cache[i].selected = true;
m_selection_empty = false;
m_new_point_head_diameter = m_editing_mode_cache[i].support_point.head_front_radius * 2.f;
m_new_point_head_diameter = m_editing_cache[i].support_point.head_front_radius * 2.f;
}
}
void GLGizmoSlaSupports::unselect_point(int i)
{
m_editing_mode_cache[i].selected = false;
if (! m_editing_mode) {
std::cout << "DEBUGGING: unselect_point called when out of editing mode!" << std::endl;
std::abort();
}
m_editing_cache[i].selected = false;
m_selection_empty = true;
for (const CacheEntry& ce : m_editing_mode_cache) {
for (const CacheEntry& ce : m_editing_cache) {
if (ce.selected) {
m_selection_empty = false;
break;
@ -1120,21 +1263,15 @@ void GLGizmoSlaSupports::unselect_point(int i)
void GLGizmoSlaSupports::editing_mode_discard_changes()
{
// If the points were autogenerated, they may not be on the ModelObject yet.
// Because the user probably messed with the cache, we will get the data
// from the backend again.
if (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated)
get_data_from_backend();
else {
m_editing_mode_cache.clear();
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
m_editing_mode_cache.emplace_back(point, false);
if (! m_editing_mode) {
std::cout << "DEBUGGING: editing_mode_discard_changes called when out of editing mode!" << std::endl;
std::abort();
}
m_editing_mode = false;
m_unsaved_changes = false;
select_point(NoPoints);
disable_editing_mode();
}
@ -1143,51 +1280,69 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
{
// If there are no changes, don't touch the front-end. The data in the cache could have been
// taken from the backend and copying them to ModelObject would needlessly invalidate them.
if (m_unsaved_changes) {
disable_editing_mode(); // this leaves the editing mode undo/redo stack and must be done before the snapshot is taken
if (unsaved_changes()) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Support points edit")));
m_normal_cache.clear();
for (const CacheEntry& ce : m_editing_cache)
m_normal_cache.push_back(ce.support_point);
m_model_object->sla_points_status = sla::PointsStatus::UserModified;
m_model_object->sla_support_points.clear();
for (const CacheEntry& cache_entry : m_editing_mode_cache)
m_model_object->sla_support_points.push_back(cache_entry.support_point);
m_model_object->sla_support_points = m_normal_cache;
// Recalculate support structures once the editing mode is left.
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
// m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
wxGetApp().CallAfter([this]() { wxGetApp().plater()->reslice_SLA_supports(*m_model_object); });
reslice_SLA_supports();
}
m_editing_mode = false;
m_unsaved_changes = false;
}
void GLGizmoSlaSupports::editing_mode_reload_cache()
void GLGizmoSlaSupports::reload_cache()
{
m_editing_mode_cache.clear();
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
m_editing_mode_cache.emplace_back(point, false);
m_unsaved_changes = false;
m_normal_cache.clear();
if (m_model_object->sla_points_status == sla::PointsStatus::AutoGenerated || m_model_object->sla_points_status == sla::PointsStatus::Generating)
get_data_from_backend();
else
for (const sla::SupportPoint& point : m_model_object->sla_support_points)
m_normal_cache.emplace_back(point);
}
bool GLGizmoSlaSupports::has_backend_supports() const
{
// find SlaPrintObject with this ID
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == m_model_object->id())
return po->is_step_done(slaposSupportPoints);
}
return false;
}
void GLGizmoSlaSupports::reslice_SLA_supports() const
{
wxGetApp().CallAfter([this]() { wxGetApp().plater()->reslice_SLA_supports(*m_model_object); });
}
void GLGizmoSlaSupports::get_data_from_backend()
{
if (! has_backend_supports())
return;
// find the respective SLAPrintObject, we need a pointer to it
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == m_model_object->id() && po->is_step_done(slaposSupportPoints)) {
m_editing_mode_cache.clear();
if (po->model_object()->id() == m_model_object->id()) {
m_normal_cache.clear();
const std::vector<sla::SupportPoint>& points = po->get_support_points();
auto mat = po->trafo().inverse().cast<float>();
for (unsigned int i=0; i<points.size();++i)
m_editing_mode_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island), false);
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified)
m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated;
m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island));
m_model_object->sla_points_status = sla::PointsStatus::AutoGenerated;
break;
}
}
m_unsaved_changes = false;
// We don't copy the data into ModelObject, as this would stop the background processing.
}
@ -1201,11 +1356,10 @@ void GLGizmoSlaSupports::auto_generate()
"Are you sure you want to do it?\n"
)), _(L("Warning")), wxICON_WARNING | wxYES | wxNO);
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_editing_mode_cache.empty() || dlg.ShowModal() == wxID_YES) {
m_model_object->sla_support_points.clear();
if (m_model_object->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Autogenerate support points")));
wxGetApp().CallAfter([this]() { reslice_SLA_supports(); });
m_model_object->sla_points_status = sla::PointsStatus::Generating;
m_editing_mode_cache.clear();
wxGetApp().CallAfter([this]() { wxGetApp().plater()->reslice_SLA_supports(*m_model_object); });
}
}
@ -1213,13 +1367,37 @@ void GLGizmoSlaSupports::auto_generate()
void GLGizmoSlaSupports::switch_to_editing_mode()
{
if (m_model_object->sla_points_status != sla::PointsStatus::AutoGenerated)
editing_mode_reload_cache();
m_unsaved_changes = false;
wxGetApp().plater()->enter_gizmos_stack();
m_editing_mode = true;
m_editing_cache.clear();
for (const sla::SupportPoint& sp : m_normal_cache)
m_editing_cache.emplace_back(sp);
select_point(NoPoints);
}
void GLGizmoSlaSupports::disable_editing_mode()
{
if (m_editing_mode) {
m_editing_mode = false;
wxGetApp().plater()->leave_gizmos_stack();
}
}
bool GLGizmoSlaSupports::unsaved_changes() const
{
if (m_editing_cache.size() != m_normal_cache.size())
return true;
for (size_t i=0; i<m_editing_cache.size(); ++i)
if (m_editing_cache[i].support_point != m_normal_cache[i])
return true;
return false;
}
void GLGizmoSlaSupports::reset_clipping_plane_normal() const
{
@ -1292,4 +1470,4 @@ SlaGizmoHelpDialog::SlaGizmoHelpDialog()
} // namespace GUI
} // namespace Slic3r
} // namespace Slic3r

View file

@ -14,6 +14,8 @@
#include "libslic3r/SLAPrint.hpp"
#include <wx/dialog.h>
#include <cereal/types/vector.hpp>
namespace Slic3r {
namespace GUI {
@ -26,19 +28,20 @@ class GLGizmoSlaSupports : public GLGizmoBase
{
private:
ModelObject* m_model_object = nullptr;
ModelID m_current_mesh_model_id = 0;
ObjectID m_model_object_id = 0;
int m_active_instance = -1;
float m_active_instance_bb_radius; // to cache the bb
mutable float m_z_shift = 0.f;
std::pair<Vec3f, Vec3f> unproject_on_mesh(const Vec2d& mouse_pos);
bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal);
const float RenderPointScale = 1.f;
GLUquadricObj* m_quadric;
Eigen::MatrixXf m_V; // vertices
Eigen::MatrixXi m_F; // facets indices
igl::AABB<Eigen::MatrixXf,3> m_AABB;
typedef Eigen::Map<const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXfUnaligned;
typedef Eigen::Map<const Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor | Eigen::DontAlign>> MapMatrixXiUnaligned;
igl::AABB<MapMatrixXfUnaligned, 3> m_AABB;
const TriangleMesh* m_mesh;
const indexed_triangle_set* m_its;
mutable const TriangleMesh* m_supports_mesh;
mutable std::vector<Vec2f> m_triangles;
mutable std::vector<Vec2f> m_supports_triangles;
@ -48,20 +51,33 @@ private:
class CacheEntry {
public:
CacheEntry(const sla::SupportPoint& point, bool sel, const Vec3f& norm = Vec3f::Zero()) :
CacheEntry() :
support_point(sla::SupportPoint()), selected(false), normal(Vec3f::Zero()) {}
CacheEntry(const sla::SupportPoint& point, bool sel = false, const Vec3f& norm = Vec3f::Zero()) :
support_point(point), selected(sel), normal(norm) {}
bool operator==(const CacheEntry& rhs) const {
return (support_point == rhs.support_point);
}
bool operator!=(const CacheEntry& rhs) const {
return ! ((*this) == rhs);
}
sla::SupportPoint support_point;
bool selected; // whether the point is selected
Vec3f normal;
template<class Archive>
void serialize(Archive & ar)
{
ar(support_point, selected, normal);
}
};
public:
#if ENABLE_SVG_ICONS
GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
#else
GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id);
#endif // ENABLE_SVG_ICONS
virtual ~GLGizmoSlaSupports();
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down);
@ -70,12 +86,14 @@ public:
bool is_in_editing_mode() const { return m_editing_mode; }
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
bool has_backend_supports() const;
void reslice_SLA_supports() const;
private:
bool on_init();
void on_update(const UpdateData& data, const Selection& selection);
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
//void render_selection_rectangle() const;
void render_points(const Selection& selection, bool picking = false) const;
@ -83,13 +101,21 @@ private:
bool is_mesh_update_necessary() const;
void update_mesh();
void update_cache_entry_normal(unsigned int i) const;
bool unsaved_changes() const;
EState m_no_hover_state = Off;
EState m_no_hover_old_state = Off;
bool m_lock_unique_islands = false;
bool m_editing_mode = false; // Is editing mode active?
bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes).
float m_new_point_head_diameter; // Size of a new point.
float m_minimal_point_distance = 20.f;
mutable std::vector<CacheEntry> m_editing_mode_cache; // a support point and whether it is currently selected
CacheEntry m_point_before_drag; // undo/redo - so we know what state was edited
float m_old_point_head_diameter = 0.; // the same
float m_minimal_point_distance_stash = 0.f; // and again
float m_density_stash = 0.f; // and again
mutable std::vector<CacheEntry> m_editing_cache; // a support point and whether it is currently selected
std::vector<sla::SupportPoint> m_normal_cache; // to restore after discarding changes or undo/redo
float m_clipping_plane_distance = 0.f;
mutable float m_old_clipping_plane_distance = 0.f;
mutable Vec3d m_old_clipping_plane_normal;
@ -102,7 +128,6 @@ private:
GLSelectionRectangle m_selection_rectangle;
bool m_wait_for_up_event = false;
bool m_unsaved_changes = false; // Are there unsaved changes in manual mode?
bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
@ -123,20 +148,29 @@ private:
void unselect_point(int i);
void editing_mode_apply_changes();
void editing_mode_discard_changes();
void editing_mode_reload_cache();
void reload_cache();
void get_data_from_backend();
void auto_generate();
void switch_to_editing_mode();
void disable_editing_mode();
void reset_clipping_plane_normal() const;
protected:
void on_set_state() override;
void on_start_dragging(const Selection& selection) override;
virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override;
virtual void on_set_hover_id()
{
if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id)
m_hover_id = -1;
}
void on_start_dragging() override;
void on_stop_dragging() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual std::string on_get_name() const;
virtual bool on_is_activable(const Selection& selection) const;
virtual bool on_is_activable() const;
virtual bool on_is_selectable() const;
virtual void on_load(cereal::BinaryInputArchive& ar) override;
virtual void on_save(cereal::BinaryOutputArchive& ar) const override;
};

File diff suppressed because it is too large Load diff

View file

@ -4,14 +4,18 @@
#include "slic3r/GUI/GLTexture.hpp"
#include "slic3r/GUI/GLToolbar.hpp"
#include "slic3r/GUI/Gizmos/GLGizmos.hpp"
#include "libslic3r/ObjectID.hpp"
#include <map>
namespace Slic3r {
namespace UndoRedo {
struct Snapshot;
}
namespace GUI {
class Selection;
class GLGizmoBase;
class GLCanvas3D;
class ClippingPlane;
@ -43,12 +47,10 @@ public:
float get_height() const { return m_top - m_bottom; }
};
class GLGizmosManager
class GLGizmosManager : public Slic3r::ObjectBase
{
public:
#if ENABLE_SVG_ICONS
static const float Default_Icons_Size;
#endif // ENABLE_SVG_ICONS
enum EType : unsigned char
{
@ -63,24 +65,17 @@ public:
};
private:
GLCanvas3D& m_parent;
bool m_enabled;
typedef std::map<EType, GLGizmoBase*> GizmosMap;
GizmosMap m_gizmos;
#if ENABLE_SVG_ICONS
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
#else
ItemsIconsTexture m_icons_texture;
#endif // ENABLE_SVG_ICONS
BackgroundTexture m_background_texture;
EType m_current;
#if ENABLE_SVG_ICONS
float m_overlay_icons_size;
float m_overlay_scale;
#else
float m_overlay_icons_scale;
#endif // ENABLE_SVG_ICONS
float m_overlay_border;
float m_overlay_gap_y;
@ -99,38 +94,71 @@ private:
MouseCapture m_mouse_capture;
std::string m_tooltip;
bool m_serializing;
public:
GLGizmosManager();
explicit GLGizmosManager(GLCanvas3D& parent);
~GLGizmosManager();
bool init(GLCanvas3D& parent);
bool init();
template<class Archive>
void load(Archive& ar)
{
if (!m_enabled)
return;
m_serializing = true;
ar(m_current);
GLGizmoBase* curr = get_current();
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) {
GLGizmoBase* gizmo = it->second;
if (gizmo != nullptr) {
gizmo->set_hover_id(-1);
gizmo->set_state((it->second == curr) ? GLGizmoBase::On : GLGizmoBase::Off);
if (gizmo == curr)
gizmo->load(ar);
}
}
}
template<class Archive>
void save(Archive& ar) const
{
if (!m_enabled)
return;
ar(m_current);
GLGizmoBase* curr = get_current();
if (curr != nullptr)
curr->save(ar);
}
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; }
#if ENABLE_SVG_ICONS
void set_overlay_icon_size(float size);
#endif // ENABLE_SVG_ICONS
void set_overlay_scale(float scale);
void refresh_on_off_state(const Selection& selection);
void refresh_on_off_state();
void reset_all_states();
void set_hover_id(int id);
void enable_grabber(EType type, unsigned int id, bool enable);
void update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos = nullptr);
void update_data(GLCanvas3D& canvas);
void update(const Linef3& mouse_ray, const Point& mouse_pos);
void update_data();
Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const;
EType get_current_type() const { return m_current; }
bool is_running() const;
bool handle_shortcut(int key, const Selection& selection);
bool handle_shortcut(int key);
bool is_dragging() const;
void start_dragging(const Selection& selection);
void start_dragging();
void stop_dragging();
Vec3d get_displacement() const;
@ -147,43 +175,50 @@ public:
void set_flattening_data(const ModelObject* model_object);
void set_sla_support_data(ModelObject* model_object, const Selection& selection);
void set_sla_support_data(ModelObject* model_object);
bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false);
ClippingPlane get_sla_clipping_plane() const;
bool wants_reslice_supports_on_undo() const;
void render_current_gizmo(const Selection& selection) const;
void render_current_gizmo_for_picking_pass(const Selection& selection) const;
void render_current_gizmo() const;
void render_current_gizmo_for_picking_pass() const;
void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
void render_overlay() const;
const std::string& get_tooltip() const { return m_tooltip; }
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas);
bool on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas);
bool on_char(wxKeyEvent& evt, GLCanvas3D& canvas);
bool on_key(wxKeyEvent& evt, GLCanvas3D& canvas);
bool on_mouse(wxMouseEvent& evt);
bool on_mouse_wheel(wxMouseEvent& evt);
bool on_char(wxKeyEvent& evt);
bool on_key(wxKeyEvent& evt);
void update_after_undo_redo(const UndoRedo::Snapshot& snapshot);
private:
void reset();
void do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const;
void render_background(float left, float top, float right, float bottom, float border) const;
void do_render_overlay() const;
float get_total_overlay_height() const;
float get_total_overlay_width() const;
GLGizmoBase* get_current() const;
#if ENABLE_SVG_ICONS
bool generate_icons_texture() const;
#endif // ENABLE_SVG_ICONS
void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection);
std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos);
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const;
void update_on_off_state(const Vec2d& mouse_pos);
std::string update_hover_state(const Vec2d& mouse_pos);
bool overlay_contains_mouse(const Vec2d& mouse_pos) const;
bool grabber_contains_mouse() const;
};
} // namespace GUI
} // namespace Slic3r
namespace cereal
{
template <class Archive> struct specialize<Archive, Slic3r::GUI::GLGizmosManager, cereal::specialization::member_load_save> {};
}
#endif // slic3r_GUI_GLGizmosManager_hpp_

View file

@ -206,7 +206,7 @@ void ImGuiWrapper::new_frame()
}
if (m_font_texture == 0) {
init_font();
init_font(true);
}
ImGui::NewFrame();
@ -233,9 +233,9 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text)
return size;
}
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag)
void ImGuiWrapper::set_next_window_pos(float x, float y, int flag, float pivot_x, float pivot_y)
{
ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag);
ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag, ImVec2(pivot_x, pivot_y));
ImGui::SetNextWindowSize(ImVec2(0.0, 0.0));
}
@ -326,9 +326,9 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
int selection_out = -1;
bool res = false;
const char *selection_str = selection < options.size() ? options[selection].c_str() : "";
const char *selection_str = selection < (int)options.size() ? options[selection].c_str() : "";
if (ImGui::BeginCombo("", selection_str)) {
for (int i = 0; i < options.size(); i++) {
for (int i = 0; i < (int)options.size(); i++) {
if (ImGui::Selectable(options[i].c_str(), i == selection)) {
selection_out = i;
}
@ -342,6 +342,32 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>&
return res;
}
bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected)
{
bool is_hovered = false;
ImGui::ListBoxHeader("", size);
int i=0;
const char* item_text;
while (items_getter(is_undo, i, &item_text))
{
ImGui::Selectable(item_text, i < hovered);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", item_text);
hovered = i;
is_hovered = true;
}
if (ImGui::IsItemClicked())
selected = i;
i++;
}
ImGui::ListBoxFooter();
return is_hovered;
}
void ImGuiWrapper::disabled_begin(bool disabled)
{
wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call");
@ -383,7 +409,7 @@ bool ImGuiWrapper::want_any_input() const
return io.WantCaptureMouse || io.WantCaptureKeyboard || io.WantTextInput;
}
void ImGuiWrapper::init_font()
void ImGuiWrapper::init_font(bool compress)
{
destroy_font();
@ -412,7 +438,10 @@ void ImGuiWrapper::init_font()
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
glsafe(::glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
if (compress && GLEW_EXT_texture_compression_s3tc)
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
else
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels));
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)m_font_texture;

View file

@ -51,7 +51,7 @@ public:
ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); }
ImVec2 calc_text_size(const wxString &text);
void set_next_window_pos(float x, float y, int flag);
void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f);
void set_next_window_bg_alpha(float alpha);
bool begin(const std::string &name, int flags = 0);
@ -67,6 +67,7 @@ public:
void text(const std::string &label);
void text(const wxString &label);
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection); // Use -1 to not mark any option as selected
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected);
void disabled_begin(bool disabled);
void disabled_end();
@ -77,7 +78,7 @@ public:
bool want_any_input() const;
private:
void init_font();
void init_font(bool compress);
void init_input();
void init_style();
void render_draw_data(ImDrawData *draw_data);

View file

@ -3,26 +3,27 @@
#include "libslic3r/Utils.hpp"
#include "GUI.hpp"
#include <wx/scrolwin.h>
#include <wx/display.h>
#include "GUI_App.hpp"
#include "wxExtensions.hpp"
namespace Slic3r {
namespace Slic3r {
namespace GUI {
KBShortcutsDialog::KBShortcutsDialog()
: DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Keyboard Shortcuts")),
: DPIDialog(NULL, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(L("Keyboard Shortcuts")),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
{
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
auto main_sizer = new wxBoxSizer(wxVERTICAL);
auto main_sizer = new wxBoxSizer(wxVERTICAL);
// logo
m_logo_bmp = ScalableBitmap(this, "PrusaSlicer_32px.png", 32);
// fonts
const wxFont& font = wxGetApp().normal_font();
const wxFont& bold_font = wxGetApp().bold_font();
const wxFont& bold_font = wxGetApp().bold_font();
SetFont(font);
wxFont head_font = bold_font;
@ -34,7 +35,9 @@ KBShortcutsDialog::KBShortcutsDialog()
fill_shortcuts();
auto panel = new wxPanel(this);
panel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, get_size());
panel->SetScrollbars(1, 20, 1, 2);
auto main_grid_sizer = new wxFlexGridSizer(2, 10, 10);
panel->SetSizer(main_grid_sizer);
main_sizer->Add(panel, 1, wxEXPAND | wxALL, 0);
@ -78,17 +81,17 @@ KBShortcutsDialog::KBShortcutsDialog()
grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL);
}
}
wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK);
this->SetEscapeId(wxID_OK);
this->Bind(wxEVT_BUTTON, &KBShortcutsDialog::onCloseDialog, this, wxID_OK);
main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 15);
this->Bind(wxEVT_LEFT_DOWN, &KBShortcutsDialog::onCloseDialog, this);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
SetSizer(main_sizer);
main_sizer->SetSizeHints(this);
}
void KBShortcutsDialog::fill_shortcuts()
@ -132,6 +135,7 @@ void KBShortcutsDialog::fill_shortcuts()
plater_shortcuts.reserve(20);
plater_shortcuts.push_back(Shortcut("A", L("Arrange")));
plater_shortcuts.push_back(Shortcut("Shift+A", L("Arrange selection")));
plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects")));
plater_shortcuts.push_back(Shortcut("Del", L("Delete selected")));
plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete All")));
@ -147,22 +151,26 @@ void KBShortcutsDialog::fill_shortcuts()
plater_shortcuts.push_back(Shortcut("F", L("Press to scale selection to fit print volume\nin Gizmo scale")));
plater_shortcuts.push_back(Shortcut(alt, L("Press to activate deselection rectangle\nor to scale or rotate selected objects\naround their own center")));
plater_shortcuts.push_back(Shortcut(ctrl, L("Press to activate one direction scaling in Gizmo scale")));
plater_shortcuts.push_back(Shortcut("K", L("Change camera type")));
plater_shortcuts.push_back(Shortcut("B", L("Zoom to Bed")));
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to all objects in scene, if none selected")));
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object")));
plater_shortcuts.push_back(Shortcut("I", L("Zoom in")));
plater_shortcuts.push_back(Shortcut("O", L("Zoom out")));
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection")));
#if ENABLE_RENDER_PICKING_PASS
plater_shortcuts.push_back(Shortcut("T", L("Toggle picking pass texture rendering on/off")));
#endif // ENABLE_RENDER_PICKING_PASS
m_full_shortcuts.push_back(std::make_pair(_(L("Plater Shortcuts")), std::make_pair(plater_shortcuts, szRight)));
// Shortcuts gizmo_shortcuts;
// gizmo_shortcuts.reserve(2);
//
//
// gizmo_shortcuts.push_back(Shortcut("Shift+", L("Press to snap by 5% in Gizmo Scale\n or by 1mm in Gizmo Move")));
// gizmo_shortcuts.push_back(Shortcut(alt, L("Press to scale or rotate selected objects around their own center")));
//
//
// m_full_shortcuts.push_back(std::make_pair(_(L("Gizmo Shortcuts")), std::make_pair(gizmo_shortcuts, 1)));
@ -173,6 +181,7 @@ void KBShortcutsDialog::fill_shortcuts()
preview_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Lower Layer")));
preview_shortcuts.push_back(Shortcut("U", L("Upper Layer")));
preview_shortcuts.push_back(Shortcut("D", L("Lower Layer")));
preview_shortcuts.push_back(Shortcut("L", L("Show/Hide (L)egend")));
m_full_shortcuts.push_back(std::make_pair(_(L("Preview Shortcuts")), std::make_pair(preview_shortcuts, szLeft)));
@ -201,7 +210,9 @@ void KBShortcutsDialog::on_dpi_changed(const wxRect &suggested_rect)
msw_buttons_rescale(this, em, { wxID_OK });
const wxSize& size = wxSize(85 * em, 75 * em);
wxSize size = get_size();
panel->SetMinSize(size);
SetMinSize(size);
Fit();
@ -214,5 +225,30 @@ void KBShortcutsDialog::onCloseDialog(wxEvent &)
this->EndModal(wxID_CLOSE);
}
wxSize KBShortcutsDialog::get_size()
{
wxTopLevelWindow* window = Slic3r::GUI::find_toplevel_parent(this);
const int display_idx = wxDisplay::GetFromWindow(window);
wxRect display;
if (display_idx == wxNOT_FOUND) {
display = wxDisplay(0u).GetClientArea();
window->Move(display.GetTopLeft());
}
else {
display = wxDisplay(display_idx).GetClientArea();
}
const int em = em_unit();
wxSize dialog_size = wxSize(90 * em, 85 * em);
const int margin = 10 * em;
if (dialog_size.x > display.GetWidth())
dialog_size.x = display.GetWidth() - margin;
if (dialog_size.y > display.GetHeight())
dialog_size.y = display.GetHeight() - margin;
return dialog_size;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -23,10 +23,10 @@ class KBShortcutsDialog : public DPIDialog
typedef std::vector< Shortcut > Shortcuts;
typedef std::vector< std::pair<wxString, std::pair<Shortcuts, PLACED_SIZER_ID>> > ShortcutsVec;
wxString text_info {wxEmptyString};
wxScrolledWindow* panel;
ShortcutsVec m_full_shortcuts;
ScalableBitmap m_logo_bmp;
ShortcutsVec m_full_shortcuts;
ScalableBitmap m_logo_bmp;
std::vector<wxStaticBitmap*> m_head_bitmaps;
public:
@ -39,6 +39,7 @@ protected:
private:
void onCloseDialog(wxEvent &);
wxSize get_size();
};
} // namespace GUI

View file

@ -35,13 +35,16 @@ namespace GUI {
MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
, m_recent_projects(9)
{
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp().update_fonts(this);
/*
#ifndef __WXOSX__ // Don't call SetFont under OSX to avoid name cutting in ObjectList
this->SetFont(this->normal_font());
#endif
// Font is already set in DPIFrame constructor
*/
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
@ -54,7 +57,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
#endif // _WIN32
// initialize status bar
m_statusbar = new ProgressStatusBar(this);
m_statusbar.reset(new ProgressStatusBar(this));
m_statusbar->embed(this);
m_statusbar->set_status_text(_(L("Version")) + " " +
SLIC3R_VERSION +
@ -103,6 +106,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
event.Veto();
return;
}
if(m_plater) m_plater->stop_jobs();
// Weird things happen as the Paint messages are floating around the windows being destructed.
// Avoid the Paint messages by hiding the main window.
@ -138,6 +143,8 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
update_ui_from_settings(); // FIXME (?)
}
MainFrame::~MainFrame() = default;
void MainFrame::update_title()
{
wxString title = wxEmptyString;
@ -240,6 +247,11 @@ bool MainFrame::can_export_model() const
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
bool MainFrame::can_export_toolpaths() const
{
return (m_plater != nullptr) && (m_plater->printer_technology() == ptFFF) && m_plater->is_preview_shown() && m_plater->is_preview_loaded() && m_plater->has_toolpaths_to_export();
}
bool MainFrame::can_export_supports() const
{
if ((m_plater == nullptr) || (m_plater->printer_technology() != ptSLA) || m_plater->model().objects.empty())
@ -274,6 +286,18 @@ bool MainFrame::can_export_gcode() const
return true;
}
bool MainFrame::can_send_gcode() const
{
if (m_plater == nullptr)
return false;
if (m_plater->model().objects.empty())
return false;
const auto print_host_opt = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionString>("print_host");
return print_host_opt != nullptr && !print_host_opt->value.empty();
}
bool MainFrame::can_slice() const
{
bool bg_proc = wxGetApp().app_config->get("background_processing") == "1";
@ -379,6 +403,41 @@ void MainFrame::init_menubar()
append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr,
[this](){return m_plater != nullptr; }, this);
wxMenu* recent_projects_menu = new wxMenu();
wxMenuItem* recent_projects_submenu = append_submenu(fileMenu, recent_projects_menu, wxID_ANY, _(L("Recent projects")), "");
m_recent_projects.UseMenu(recent_projects_menu);
Bind(wxEVT_MENU, [this](wxCommandEvent& evt) {
size_t file_id = evt.GetId() - wxID_FILE1;
wxString filename = m_recent_projects.GetHistoryFile(file_id);
if (wxFileExists(filename))
m_plater->load_project(filename);
else
{
wxMessageDialog msg(this, _(L("The selected project is no more available")), _(L("Error")));
msg.ShowModal();
m_recent_projects.RemoveFileFromHistory(file_id);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
wxGetApp().app_config->save();
}
}, wxID_FILE1, wxID_FILE9);
std::vector<std::string> recent_projects = wxGetApp().app_config->get_recent_projects();
std::reverse(recent_projects.begin(), recent_projects.end());
for (const std::string& project : recent_projects)
{
m_recent_projects.AddFileToHistory(from_u8(project));
}
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
@ -411,17 +470,25 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, menu_icon("export_gcode"), nullptr,
[this](){return can_export_gcode(); }, this);
m_changeable_menu_items.push_back(item_export_gcode);
wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")),
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, menu_icon("export_gcode"), nullptr,
[this](){return can_send_gcode(); }, this);
m_changeable_menu_items.push_back(item_send_gcode);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"), nullptr,
[this](){return can_export_model(); }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL including supports")) + dots, _(L("Export current plate as STL including supports")),
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL &including supports")) + dots, _(L("Export current plate as STL including supports")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, menu_icon("export_plater"), nullptr,
[this](){return can_export_supports(); }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, menu_icon("export_plater"), nullptr,
[this](){return can_export_model(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &toolpaths as OBJ")) + dots, _(L("Export toolpaths as OBJ")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, menu_icon("export_plater"), nullptr,
[this]() {return can_export_toolpaths(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")),
[this](wxCommandEvent&) { export_config(); }, menu_icon("export_config"));
append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")),
@ -498,13 +565,21 @@ void MainFrame::init_menubar()
_(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); },
menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z",
_(L("Undo")), [this](wxCommandEvent&) { m_plater->undo(); },
"undo", nullptr, [this](){return m_plater->can_undo(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("&Redo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y",
_(L("Redo")), [this](wxCommandEvent&) { m_plater->redo(); },
"redo", nullptr, [this](){return m_plater->can_redo(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy(); }, this);
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
_(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste(); }, this);
menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
}
// Window menu
@ -641,7 +716,8 @@ void MainFrame::update_menubar()
{
const bool is_fff = plater()->printer_technology() == ptFFF;
m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("Export")) ) + dots + "\tCtrl+G");
m_changeable_menu_items[miExport] ->SetItemLabel((is_fff ? _(L("Export &G-code")) : _(L("E&xport")) ) + dots + "\tCtrl+G");
m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G");
m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3");
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, menu_icon(is_fff ? "spool": "resin")));
@ -664,29 +740,26 @@ void MainFrame::quick_slice(const int qs)
// select input file
if (!(qs & qsReslice)) {
auto dlg = new wxFileDialog(this, _(L("Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):")),
wxFileDialog dlg(this, _(L("Choose a file to slice (STL/OBJ/AMF/3MF/PRUSA):")),
wxGetApp().app_config->get_last_dir(), "",
file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
if (dlg.ShowModal() != wxID_OK)
return;
}
input_file = dlg->GetPath();
dlg->Destroy();
input_file = dlg.GetPath();
if (!(qs & qsExportSVG))
m_qs_last_input_file = input_file;
}
else {
if (m_qs_last_input_file.IsEmpty()) {
auto dlg = new wxMessageDialog(this, _(L("No previously sliced file.")),
wxMessageDialog dlg(this, _(L("No previously sliced file.")),
_(L("Error")), wxICON_ERROR | wxOK);
dlg->ShowModal();
dlg.ShowModal();
return;
}
if (std::ifstream(m_qs_last_input_file.ToUTF8().data())) {
auto dlg = new wxMessageDialog(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")),
wxMessageDialog dlg(this, _(L("Previously sliced file ("))+m_qs_last_input_file+_(L(") not found.")),
_(L("File Not Found")), wxICON_ERROR | wxOK);
dlg->ShowModal();
dlg.ShowModal();
return;
}
input_file = m_qs_last_input_file;
@ -720,30 +793,24 @@ void MainFrame::quick_slice(const int qs)
}
else if (qs & qsSaveAs) {
// The following line may die if the output_filename_format template substitution fails.
auto dlg = new wxFileDialog(this, wxString::Format(_(L("Save %s file as:")) , qs & qsExportSVG ? _(L("SVG")) : _(L("G-code")) ),
wxFileDialog dlg(this, wxString::Format(_(L("Save %s file as:")) , qs & qsExportSVG ? _(L("SVG")) : _(L("G-code")) ),
wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file),
qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
if (dlg.ShowModal() != wxID_OK)
return;
}
output_file = dlg->GetPath();
dlg->Destroy();
output_file = dlg.GetPath();
if (!(qs & qsExportSVG))
m_qs_last_output_file = output_file;
wxGetApp().app_config->update_last_output_dir(get_dir_name(output_file));
}
else if (qs & qsExportPNG) {
auto dlg = new wxFileDialog(this, _(L("Save zip file as:")),
wxFileDialog dlg(this, _(L("Save zip file as:")),
wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)),
get_base_name(output_file), "*.sl1", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
if (dlg.ShowModal() != wxID_OK)
return;
}
output_file = dlg->GetPath();
dlg->Destroy();
output_file = dlg.GetPath();
}
// show processbar dialog
@ -789,28 +856,22 @@ void MainFrame::repair_stl()
{
wxString input_file;
{
auto dlg = new wxFileDialog(this, _(L("Select the STL file to repair:")),
wxFileDialog dlg(this, _(L("Select the STL file to repair:")),
wxGetApp().app_config->get_last_dir(), "",
file_wildcards(FT_STL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
if (dlg.ShowModal() != wxID_OK)
return;
}
input_file = dlg->GetPath();
dlg->Destroy();
input_file = dlg.GetPath();
}
wxString output_file = input_file;
{
auto dlg = new wxFileDialog( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"),
wxFileDialog dlg( this, L("Save OBJ file (less prone to coordinate errors than STL) as:"),
get_dir_name(output_file), get_base_name(output_file, ".obj"),
file_wildcards(FT_OBJ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
if (dlg.ShowModal() != wxID_OK)
return;
}
output_file = dlg->GetPath();
dlg->Destroy();
output_file = dlg.GetPath();
}
auto tmesh = new Slic3r::TriangleMesh();
@ -831,14 +892,13 @@ void MainFrame::export_config()
return;
}
// Ask user for the file name for the config file.
auto dlg = new wxFileDialog(this, _(L("Save configuration as:")),
wxFileDialog dlg(this, _(L("Save configuration as:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
!m_last_config.IsEmpty() ? get_base_name(m_last_config) : "config.ini",
file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
wxString file;
if (dlg->ShowModal() == wxID_OK)
file = dlg->GetPath();
dlg->Destroy();
if (dlg.ShowModal() == wxID_OK)
file = dlg.GetPath();
if (!file.IsEmpty()) {
wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file;
@ -851,13 +911,12 @@ void MainFrame::load_config_file()
{
if (!wxGetApp().check_unsaved_changes())
return;
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")),
wxFileDialog dlg(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
wxString file;
if (dlg->ShowModal() == wxID_OK)
file = dlg->GetPath();
dlg->Destroy();
if (dlg.ShowModal() == wxID_OK)
file = dlg.GetPath();
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file;
@ -888,14 +947,13 @@ void MainFrame::export_configbundle()
return;
}
// Ask user for a file name.
auto dlg = new wxFileDialog(this, _(L("Save presets bundle as:")),
wxFileDialog dlg(this, _(L("Save presets bundle as:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
SLIC3R_APP_KEY "_config_bundle.ini",
file_wildcards(FT_INI), wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
wxString file;
if (dlg->ShowModal() == wxID_OK)
file = dlg->GetPath();
dlg->Destroy();
if (dlg.ShowModal() == wxID_OK)
file = dlg.GetPath();
if (!file.IsEmpty()) {
// Export the config bundle.
wxGetApp().app_config->update_config_dir(get_dir_name(file));
@ -915,15 +973,12 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
if (!wxGetApp().check_unsaved_changes())
return;
if (file.IsEmpty()) {
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")),
wxFileDialog dlg(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", file_wildcards(FT_INI), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
return;
}
file = dlg->GetPath();
dlg->Destroy();
if (dlg.ShowModal() != wxID_OK)
return;
file = dlg.GetPath();
}
wxGetApp().app_config->update_config_dir(get_dir_name(file));
@ -976,7 +1031,8 @@ void MainFrame::load_config(const DynamicPrintConfig& config)
if (! boost::algorithm::ends_with(opt_key, "_settings_id"))
tab->get_config()->option(opt_key)->set(config.option(opt_key));
}
wxGetApp().load_current_presets();
wxGetApp().load_current_presets();
#endif
}
@ -1041,6 +1097,23 @@ void MainFrame::on_config_changed(DynamicPrintConfig* config) const
m_plater->on_config_change(*config); // propagate config change events to the plater
}
void MainFrame::add_to_recent_projects(const wxString& filename)
{
if (wxFileExists(filename))
{
m_recent_projects.AddFileToHistory(filename);
std::vector<std::string> recent_projects;
size_t count = m_recent_projects.GetCount();
for (size_t i = 0; i < count; ++i)
{
recent_projects.push_back(into_u8(m_recent_projects.GetHistoryFile(i)));
}
wxGetApp().app_config->set_recent_projects(recent_projects);
wxGetApp().app_config->save();
}
}
//
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void MainFrame::update_ui_from_settings()

View file

@ -1,4 +1,4 @@
#ifndef slic3r_MainFrame_hpp_
#ifndef slic3r_MainFrame_hpp_
#define slic3r_MainFrame_hpp_
#include "libslic3r/PrintConfig.hpp"
@ -6,6 +6,7 @@
#include <wx/frame.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/filehistory.h>
#include <string>
#include <map>
@ -64,8 +65,10 @@ class MainFrame : public DPIFrame
bool can_start_new_project() const;
bool can_save() const;
bool can_export_model() const;
bool can_export_toolpaths() const;
bool can_export_supports() const;
bool can_export_gcode() const;
bool can_send_gcode() const;
bool can_slice() const;
bool can_change_view() const;
bool can_select() const;
@ -78,18 +81,21 @@ class MainFrame : public DPIFrame
enum MenuItems
{ // FFF SLA
miExport = 0, // Export G-code Export
miSend, // Send G-code Send to print
miMaterialTab, // Filament Settings Material Settings
};
// vector of a MenuBar items changeable in respect to printer technology
std::vector<wxMenuItem*> m_changeable_menu_items;
wxFileHistory m_recent_projects;
protected:
virtual void on_dpi_changed(const wxRect &suggested_rect);
public:
MainFrame();
~MainFrame() {}
~MainFrame();
Plater* plater() { return m_plater; }
@ -121,12 +127,14 @@ public:
// Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
void on_config_changed(DynamicPrintConfig* cfg) const ;
void add_to_recent_projects(const wxString& filename);
PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; }
Plater* m_plater { nullptr };
wxNotebook* m_tabpanel { nullptr };
wxProgressDialog* m_progress_dialog { nullptr };
ProgressStatusBar* m_statusbar { nullptr };
std::unique_ptr<ProgressStatusBar> m_statusbar;
};
} // GUI

View file

@ -8,8 +8,6 @@
#include <wx/font.h>
#include <wx/bitmap.h>
#include "slic3r/Utils/Semver.hpp"
class wxBoxSizer;
class wxCheckBox;
class wxStaticBitmap;

View file

@ -1,4 +1,4 @@
#include "OptionsGroup.hpp"
#include "OptionsGroup.hpp"
#include "ConfigExceptions.hpp"
#include <utility>
@ -202,7 +202,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// 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(m_near_label_widget_ptrs.back(), 0, wxRIGHT, 7);
sizer->Add(m_near_label_widget_ptrs.back(), 0, wxRIGHT | wxALIGN_CENTER_VERTICAL, 7);
sizer->Add(label, 0, (staticbox ? 0 : wxALIGN_RIGHT | wxRIGHT) | wxALIGN_CENTER_VERTICAL, 5);
}
}
@ -266,7 +266,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
is_sizer_field(field) ?
v_sizer->Add(field->getSizer(), 0, wxEXPAND) :
v_sizer->Add(field->getWindow(), 0, wxEXPAND);
return;
break;//return;
}
is_sizer_field(field) ?
@ -276,7 +276,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
// add sidetext if any
if (option.sidetext != "") {
auto sidetext = new wxStaticText( this->ctrl_parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition,
/*wxSize(sidetext_width*wxGetApp().em_unit(), -1)*/wxDefaultSize, wxALIGN_LEFT);
wxSize(sidetext_width != -1 ? sidetext_width*wxGetApp().em_unit() : -1, -1) /*wxDefaultSize*/, wxALIGN_LEFT);
sidetext->SetBackgroundStyle(wxBG_STYLE_PAINT);
sidetext->SetFont(wxGetApp().normal_font());
sizer_tmp->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
@ -300,7 +300,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** full_Label/* = n
{
// extra widget for non-staticbox option group (like for the frequently used parameters on the sidebar) should be wxALIGN_RIGHT
const auto v_sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(v_sizer, 1, wxEXPAND);
sizer->Add(v_sizer, option_set.size() == 1 ? 0 : 1, wxEXPAND);
v_sizer->Add(extra_widget(this->ctrl_parent()), 0, wxALIGN_RIGHT);
return;
}
@ -320,6 +320,17 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
return retval;
}
void OptionsGroup::clear_fields_except_of(const std::vector<std::string> left_fields)
{
auto it = m_fields.begin();
while (it != m_fields.end()) {
if (std::find(left_fields.begin(), left_fields.end(), it->first) == left_fields.end())
it = m_fields.erase(it);
else
it++;
}
}
void OptionsGroup::on_set_focus(const std::string& opt_key)
{
if (m_set_focus != nullptr)
@ -361,30 +372,10 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b
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);
}
}
change_opt_value(*m_config, opt_key, value, opt_index == -1 ? 0 : opt_index);
}
OptionsGroup::on_change_OG(opt_id, value); //!? Why doing this
OptionsGroup::on_change_OG(opt_id, value);
}
void ConfigOptionsGroup::back_to_initial_value(const std::string& opt_key)
@ -567,6 +558,34 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
boost::any ret;
wxString text_value = wxString("");
const ConfigOptionDef* opt = config.def()->get(opt_key);
if (opt->nullable)
{
switch (opt->type)
{
case coPercents:
case coFloats: {
if (config.option(opt_key)->is_nil())
ret = _(L("N/A"));
else {
double val = opt->type == coFloats ?
config.option<ConfigOptionFloatsNullable>(opt_key)->get_at(idx) :
config.option<ConfigOptionPercentsNullable>(opt_key)->get_at(idx);
ret = double_to_string(val); }
}
break;
case coBools:
ret = config.option<ConfigOptionBoolsNullable>(opt_key)->values[idx];
break;
case coInts:
ret = config.option<ConfigOptionIntsNullable>(opt_key)->get_at(idx);
break;
default:
break;
}
return ret;
}
switch (opt->type) {
case coFloatOrPercent:{
const auto &value = *config.option<ConfigOptionFloatOrPercent>(opt_key);

View file

@ -160,6 +160,14 @@ public:
m_show_modified_btns = show;
}
void clear_fields_except_of(const std::vector<std::string> left_fields);
void hide_labels() {
label_width = 0;
m_grid_sizer->SetCols(m_grid_sizer->GetEffectiveColsCount()-1);
static_cast<wxFlexGridSizer*>(m_grid_sizer)->AddGrowableCol(!extra_column ? 0 : 1);
}
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
column_t extra_clmn = nullptr) :
m_parent(_parent), title(title),
@ -244,6 +252,9 @@ public:
Option option = get_option(title, idx);
return OptionsGroup::create_single_option_line(option);
}
Line create_single_option_line(const Option& option) const {
return OptionsGroup::create_single_option_line(option);
}
void append_single_option_line(const Option& option) {
OptionsGroup::append_single_option_line(option);
}

File diff suppressed because it is too large Load diff

View file

@ -27,12 +27,18 @@ class ModelObject;
class Print;
class SLAPrint;
namespace UndoRedo {
class Stack;
struct Snapshot;
};
namespace GUI {
class MainFrame;
class ConfigOptionsGroup;
class ObjectManipulation;
class ObjectSettings;
class ObjectLayers;
class ObjectList;
class GLCanvas3D;
@ -93,6 +99,7 @@ public:
ObjectManipulation* obj_manipul();
ObjectList* obj_list();
ObjectSettings* obj_settings();
ObjectLayers* obj_layers();
wxScrolledWindow* scrolled_panel();
wxPanel* presets_panel();
@ -136,6 +143,7 @@ public:
void new_project();
void load_project();
void load_project(const wxString& filename);
void add_model();
void extract_config_from_project();
@ -144,9 +152,14 @@ public:
void load_files(const std::vector<std::string>& input_files, bool load_model = true, bool load_config = true);
void update();
void stop_jobs();
void select_view(const std::string& direction);
void select_view_3D(const std::string& name);
bool is_preview_shown() const;
bool is_preview_loaded() const;
bool is_view3D_shown() const;
// Called after the Preferences dialog is closed and the program settings are saved.
// Update the UI based on the current preferences.
void update_ui_from_settings();
@ -170,14 +183,32 @@ public:
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
bool has_toolpaths_to_export() const;
void export_toolpaths_to_obj() const;
void reslice();
void reslice_SLA_supports(const ModelObject &object);
void changed_object(int obj_idx);
void changed_objects(const std::vector<size_t>& object_idxs);
void schedule_background_process();
void schedule_background_process(bool schedule = true);
bool is_background_process_running() const;
void suppress_background_process(const bool stop_background_process) ;
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode();
void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name);
void undo();
void redo();
void undo_to(int selection);
void redo_to(int selection);
bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text);
void undo_redo_topmost_string_getter(const bool is_undo, std::string& out_text);
// For the memory statistics.
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
// Enter / leave the Gizmos specific Undo / Redo stack. To be used by the SLA support point editing gizmo.
void enter_gizmos_stack();
void leave_gizmos_stack();
void on_extruders_change(int extruders_count);
void on_config_change(const DynamicPrintConfig &config);
// On activating the parent window.
@ -199,7 +230,6 @@ public:
void copy_selection_to_clipboard();
void paste_from_clipboard();
bool can_paste_from_clipboard() const;
bool can_delete() const;
bool can_delete_all() const;
@ -211,16 +241,66 @@ public:
bool can_split_to_volumes() const;
bool can_arrange() const;
bool can_layers_editing() const;
bool can_copy() const;
bool can_paste() const;
bool can_paste_from_clipboard() const;
bool can_copy_to_clipboard() const;
bool can_undo() const;
bool can_redo() const;
void msw_rescale();
const Camera& get_camera() const;
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
class SuppressSnapshots
{
public:
SuppressSnapshots(Plater *plater) : m_plater(plater)
{
m_plater->suppress_snapshots();
}
~SuppressSnapshots()
{
m_plater->allow_snapshots();
}
private:
Plater *m_plater;
};
// ROII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
class TakeSnapshot
{
public:
TakeSnapshot(Plater *plater, const wxString &snapshot_name) : m_plater(plater)
{
m_plater->take_snapshot(snapshot_name);
m_plater->suppress_snapshots();
}
~TakeSnapshot()
{
m_plater->allow_snapshots();
}
private:
Plater *m_plater;
};
private:
struct priv;
std::unique_ptr<priv> p;
void suppress_snapshots();
void allow_snapshots();
friend class SuppressBackgroundProcessingUpdate;
};
class SuppressBackgroundProcessingUpdate
{
public:
SuppressBackgroundProcessingUpdate();
~SuppressBackgroundProcessingUpdate();
private:
bool m_was_running;
};
}}

View file

@ -28,7 +28,7 @@ void PreferencesDialog::build()
m_icon_size_sizer->ShowItems(boost::any_cast<bool>(value));
this->layout();
}
};
};
// TODO
// $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
@ -97,16 +97,6 @@ void PreferencesDialog::build()
option = Option (def,"show_incompatible_presets");
m_optgroup->append_single_option_line(option);
// TODO: remove?
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.set_default_value(new ConfigOptionBool{ app_config->get("use_legacy_opengl") == "1" });
option = Option (def,"use_legacy_opengl");
m_optgroup->append_single_option_line(option);
#if __APPLE__
def.label = L("Use Retina resolution for the 3D scene");
def.type = coBool;
@ -117,6 +107,13 @@ void PreferencesDialog::build()
m_optgroup->append_single_option_line(option);
#endif
def.label = L("Use perspective camera");
def.type = coBool;
def.tooltip = L("If enabled, use perspective camera. If not enabled, use orthographic camera.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_perspective_camera") == "1" });
option = Option(def, "use_perspective_camera");
m_optgroup->append_single_option_line(option);
def.label = L("Use custom size for toolbar icons");
def.type = coBool;
def.tooltip = L("If enabled, you can change size of toolbar icons manually.");
@ -143,8 +140,7 @@ void PreferencesDialog::build()
void PreferencesDialog::accept()
{
if (m_values.find("no_defaults") != m_values.end() ||
m_values.find("use_legacy_opengl") != m_values.end()) {
if (m_values.find("no_defaults") != m_values.end()) {
warning_catcher(this, wxString::Format(_(L("You need to restart %s to make the changes effective.")), SLIC3R_APP_NAME));
}

View file

@ -162,11 +162,11 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
if (from_pre_map != pre_family_model_map.end()) { model.family = from_pre_map->second; }
}
#if 0
// Remove SLA printers from the initial alpha.
if (model.technology == ptSLA)
continue;
// Remove SLA printers from the initial alpha.
if (model.technology == ptSLA)
continue;
#endif
section.second.get<std::string>("variants", "");
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)) {
@ -209,7 +209,7 @@ const std::string& Preset::suffix_modified()
void Preset::update_suffix_modified()
{
g_suffix_modified = (" (" + _(L("modified")) + ")").ToUTF8().data();
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.
@ -224,8 +224,8 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr
{
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : Preset::nozzle_options()) {
if (key == "default_filament_profile")
continue;
if (key == "default_filament_profile")
continue;
auto *opt = config.option(key, false);
assert(opt != nullptr);
assert(opt->is_vector());
@ -247,8 +247,8 @@ void Preset::normalize(DynamicPrintConfig &config)
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_prints" || key == "compatible_printers")
continue;
if (key == "compatible_prints" || key == "compatible_printers")
continue;
auto *opt = config.option(key, false);
/*assert(opt != nullptr);
assert(opt->is_vector());*/
@ -307,7 +307,7 @@ bool Preset::is_compatible_with_print(const Preset &active_print) const
}
}
return this->is_default || active_print.name.empty() || ! has_compatible_prints ||
std::find(compatible_prints->values.begin(), compatible_prints->values.end(), active_print.name) !=
std::find(compatible_prints->values.begin(), compatible_prints->values.end(), active_print.name) !=
compatible_prints->values.end();
}
@ -326,7 +326,7 @@ bool Preset::is_compatible_with_printer(const Preset &active_printer, const Dyna
}
}
return this->is_default || active_printer.name.empty() || ! has_compatible_printers ||
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.name) !=
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.name) !=
compatible_printers->values.end();
}
@ -334,9 +334,9 @@ 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()));
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);
}
@ -358,40 +358,40 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
}
const std::vector<std::string>& Preset::print_options()
{
{
static std::vector<std::string> s_opts {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "top_solid_layers", "bottom_solid_layers",
"extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "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", "top_fill_pattern", "bottom_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",
"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",
#ifdef HAS_PRESSURE_EQUALIZER
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
#endif /* HAS_PRESSURE_EQUALIZER */
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"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_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", "gcode_label_objects", "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",
"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", "gcode_label_objects", "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",
"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",
@ -400,36 +400,40 @@ const std::vector<std::string>& Preset::filament_options()
"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",
// Retract overrides
"filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"compatible_prints", "compatible_prints_condition", "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",
"bed_shape", "bed_custom_texture", "bed_custom_model", "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", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height",
"cooling_tube_length", "high_current_on_filament_swap", "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"
"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
// 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()
{
@ -438,14 +442,14 @@ const std::vector<std::string>& Preset::nozzle_options()
"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",
"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_print_options()
{
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
@ -461,6 +465,7 @@ const std::vector<std::string>& Preset::sla_print_options()
"support_pillar_widening_factor",
"support_base_diameter",
"support_base_height",
"support_base_safety_distance",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
@ -472,12 +477,17 @@ const std::vector<std::string>& Preset::sla_print_options()
"pad_wall_thickness",
"pad_wall_height",
"pad_max_merge_distance",
"pad_edge_radius",
// "pad_edge_radius",
"pad_wall_slope",
"output_filename_format",
"pad_object_gap",
"pad_zero_elevation",
"pad_object_connector_stride",
"pad_object_connector_width",
"pad_object_connector_penetration",
"output_filename_format",
"default_sla_print_profile",
"compatible_printers",
"compatible_printers_condition",
"compatible_printers_condition",
"inherits"
};
}
@ -485,16 +495,17 @@ const std::vector<std::string>& Preset::sla_print_options()
}
const std::vector<std::string>& Preset::sla_material_options()
{
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"initial_layer_height",
"exposure_time", "initial_exposure_time",
"exposure_time",
"initial_exposure_time",
"material_correction",
"material_notes",
"default_sla_material_profile",
"compatible_prints", "compatible_prints_condition",
"compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits"
};
}
@ -502,18 +513,22 @@ const std::vector<std::string>& Preset::sla_material_options()
}
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", "bed_custom_texture", "bed_custom_model", "max_print_height",
"bed_shape", "max_print_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
"display_mirror_x", "display_mirror_y",
"display_orientation",
"fast_tilt_time", "slow_tilt_time", "area_fill",
"relative_correction",
"absolute_correction",
"gamma_correction",
"min_exposure_time", "max_exposure_time",
"min_initial_exposure_time", "max_initial_exposure_time",
"print_host", "printhost_apikey", "printhost_cafile",
"printer_notes",
"inherits"
@ -528,7 +543,7 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
m_idx_selected(0),
m_bitmap_main_frame(new wxBitmap),
m_bitmap_add(new wxBitmap),
m_bitmap_cache(new GUI::BitmapCache)
m_bitmap_cache(new GUI::BitmapCache)
{
// Insert just the default preset.
this->add_default_preset(keys, defaults, default_name);
@ -541,8 +556,8 @@ PresetCollection::~PresetCollection()
m_bitmap_main_frame = nullptr;
delete m_bitmap_add;
m_bitmap_add = nullptr;
delete m_bitmap_cache;
m_bitmap_cache = nullptr;
delete m_bitmap_cache;
m_bitmap_cache = nullptr;
}
void PresetCollection::reset(bool delete_files)
@ -564,8 +579,8 @@ void PresetCollection::add_default_preset(const std::vector<std::string> &keys,
{
// Insert just the default preset.
m_presets.emplace_back(Preset(this->type(), preset_name, true));
m_presets.back().config.apply_only(defaults, keys.empty() ? defaults.keys() : keys);
m_presets.back().loaded = true;
m_presets.back().config.apply_only(defaults, keys.empty() ? defaults.keys() : keys);
m_presets.back().loaded = true;
++ m_num_default_presets;
}
@ -573,13 +588,13 @@ void PresetCollection::add_default_preset(const std::vector<std::string> &keys,
// 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();
boost::filesystem::path dir = boost::filesystem::canonical(boost::filesystem::path(dir_path) / subdir).make_preferred();
m_dir_path = dir.string();
std::string errors_cummulative;
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std::deque<Preset> presets_loaded;
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std::deque<Preset> presets_loaded;
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
if (Slic3r::is_ini_file(dir_entry)) {
std::string name = dir_entry.path().filename().string();
// Remove the .ini suffix.
@ -598,28 +613,28 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
DynamicPrintConfig config;
config.load_from_ini(preset.file);
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
const Preset &default_preset = this->default_preset_for(config);
const Preset &default_preset = this->default_preset_for(config);
preset.config = default_preset.config;
preset.config.apply(std::move(config));
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys = Preset::remove_invalid_keys(config, default_preset.config);
std::string incorrect_keys = Preset::remove_invalid_keys(config, default_preset.config);
if (! incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
preset.loaded = true;
} catch (const std::ifstream::failure &err) {
throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
throw std::runtime_error(std::string("The selected preset cannot be loaded: ") + preset.file + "\n\tReason: " + err.what());
} catch (const std::runtime_error &err) {
throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
throw std::runtime_error(std::string("Failed loading the preset file: ") + preset.file + "\n\tReason: " + err.what());
}
presets_loaded.emplace_back(preset);
presets_loaded.emplace_back(preset);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
}
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
m_presets.insert(m_presets.end(), std::make_move_iterator(presets_loaded.begin()), std::make_move_iterator(presets_loaded.end()));
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(first_visible_idx());
if (! errors_cummulative.empty())
@ -640,8 +655,8 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const Dyna
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_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits",
for (const char *key : { "compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits",
"print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile",
"printhost_apikey", "printhost_cafile" })
@ -652,7 +667,7 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const Dyna
// 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
// 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,
@ -670,7 +685,7 @@ Preset& PresetCollection::load_external_preset(
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);
bool found = it != m_presets.end() && it->name == original_name;
bool found = it != m_presets.end() && it->name == original_name;
if (found && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select)
@ -695,7 +710,7 @@ Preset& PresetCollection::load_external_preset(
suffix = " (" + std::to_string(idx) + ")";
} else {
if (idx == 0)
suffix = " (" + original_name + ")";
suffix = " (" + original_name + ")";
else
suffix = " (" + original_name + "-" + std::to_string(idx) + ")";
}
@ -740,8 +755,8 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
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.
// 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.
@ -751,15 +766,15 @@ void PresetCollection::save_current_preset(const std::string &new_name)
return;
// Overwriting an existing preset.
preset.config = std::move(m_edited_preset.config);
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
} else {
// Creating a new preset.
Preset &preset = *m_presets.insert(it, m_edited_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.file = this->path_from_name(new_name);
preset.vendor = nullptr;
if (preset.is_system) {
// Inheriting from a system preset.
@ -768,30 +783,30 @@ void PresetCollection::save_current_preset(const std::string &new_name)
// 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,
// 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;
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
}
// 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();
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
}
// 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();
}
bool PresetCollection::delete_current_preset()
{
const Preset &selected = this->get_selected_preset();
if (selected.is_default)
return false;
if (! selected.is_external && ! selected.is_system) {
// Erase the preset file.
boost::nowide::remove(selected.file.c_str());
}
if (selected.is_default)
return false;
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.
@ -799,9 +814,9 @@ bool PresetCollection::delete_current_preset()
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);
for (--new_selected_idx; new_selected_idx > 0 && !m_presets[new_selected_idx].is_visible; --new_selected_idx);
this->select_preset(new_selected_idx);
return true;
return true;
}
void PresetCollection::load_bitmap_default(wxWindow *window, const std::string &file_name)
@ -823,25 +838,39 @@ const Preset* PresetCollection::get_selected_preset_parent() const
if (this->get_selected_idx() == -1)
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr;
const std::string &inherits = this->get_edited_preset().inherits();
// const std::string &inherits = this->get_edited_preset().inherits();
// if (inherits.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
std::string inherits = this->get_edited_preset().inherits();
if (inherits.empty())
return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
{
if (this->get_selected_preset().is_system || this->get_selected_preset().is_default)
return &this->get_selected_preset();
if (this->get_selected_preset().is_external)
return nullptr;
inherits = m_type != Preset::Type::TYPE_PRINTER ? "- default -" :
this->get_edited_preset().printer_technology() == ptFFF ?
"- default FFF -" : "- default SLA -" ;
}
const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
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;
// 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 g_suffix_modified;
}
// Return a preset by its name. If the preset is active, a temporary copy is returned.
@ -851,7 +880,7 @@ Preset* PresetCollection::find_preset(const std::string &name, bool first_visibl
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()) :
return (it != m_presets.end() && it->name == key.name) ? &this->preset(it - m_presets.begin()) :
first_visible_if_not_found ? &this->first_visible() : nullptr;
}
@ -871,9 +900,9 @@ void PresetCollection::set_default_suppressed(bool default_suppressed)
{
if (m_default_suppressed != default_suppressed) {
m_default_suppressed = default_suppressed;
bool default_visible = ! default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++ i)
m_presets[i].is_visible = default_visible;
bool default_visible = ! default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++ i)
m_presets[i].is_visible = default_visible;
}
}
@ -906,7 +935,7 @@ size_t PresetCollection::update_compatible_internal(const Preset &active_printer
//void PresetCollection::delete_current_preset();
// Update the wxChoice UI component from this list of presets.
// Hide the
// Hide the
void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
{
if (ui == nullptr)
@ -915,12 +944,12 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
// Otherwise fill in the list from scratch.
ui->Freeze();
ui->Clear();
size_t selected_preset_item = 0;
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;
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;
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
* So set sizes for solid_colored icons used for filament preset
@ -932,87 +961,87 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
const int thin_space_icon_width = 4 * scale_f + 0.5f;
const int wide_space_icon_width = 6 * scale_f + 0.5f;
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = "";
if (!this->m_presets.front().is_visible)
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->set_label_marker(ui->Append(PresetCollection::separator(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) {
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(icon_width, icon_height) : *m_bitmap_incompatible);
// Paint the color bars.
bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height));
bmps.emplace_back(*m_bitmap_main_frame);
// Paint a lock at the system presets.
bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height));
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height));
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
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(icon_width, icon_height) : *m_bitmap_incompatible);
// Paint the color bars.
bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height));
bmps.emplace_back(*m_bitmap_main_frame);
// Paint a lock at the system presets.
bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height));
bmps.emplace_back((preset.is_system || preset.is_default) ? *m_bitmap_lock : m_bitmap_cache->mkclear(icon_width, icon_height));
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)
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->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
}
if (!nonsys_presets.empty())
{
}
if (!nonsys_presets.empty())
{
ui->set_label_marker(ui->Append(PresetCollection::separator(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;
}
}
if (m_type == Preset::TYPE_PRINTER) {
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 += "wide,";
bitmap_key += "add_printer";
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(m_bitmap_cache->mkclear(icon_width, icon_height));
// Paint the color bars.
bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height));
bmps.emplace_back(*m_bitmap_main_frame);
// Paint a lock at the system presets.
bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height));
bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_CONFIG_WIZARD);
}
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;
}
}
if (m_type == Preset::TYPE_PRINTER) {
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 += "wide,";
bitmap_key += "add_printer";
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(m_bitmap_cache->mkclear(icon_width, icon_height));
// Paint the color bars.
bmps.emplace_back(m_bitmap_cache->mkclear(thin_space_icon_width, icon_height));
bmps.emplace_back(*m_bitmap_main_frame);
// Paint a lock at the system presets.
bmps.emplace_back(m_bitmap_cache->mkclear(wide_space_icon_width, icon_height));
bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_CONFIG_WIZARD);
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->check_selection();
ui->Thaw();
@ -1027,7 +1056,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
return 0;
ui->Freeze();
ui->Clear();
size_t selected_preset_item = 0;
size_t selected_preset_item = 0;
/* It's supposed that standard size of an icon is 16px*16px for 100% scaled display.
* So set sizes for solid_colored(empty) icons used for preset
@ -1037,52 +1066,52 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
const int icon_height = 16 * scale_f + 0.5f;
const int icon_width = 16 * scale_f + 0.5f;
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->Append(PresetCollection::separator(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) {
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->Append(PresetCollection::separator(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(icon_width, icon_height));
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
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(icon_width, icon_height));
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(PresetCollection::separator(L("System presets")), wxNullBitmap);
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(PresetCollection::separator(L("System presets")), wxNullBitmap);
}
if (!nonsys_presets.empty())
{
ui->Append(PresetCollection::separator(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;
}
}
if (!nonsys_presets.empty())
{
ui->Append(PresetCollection::separator(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;
}
}
if (m_type == Preset::TYPE_PRINTER) {
wxBitmap *bmp = m_bitmap_cache->find("add_printer_tab");
if (bmp == nullptr) {
@ -1094,10 +1123,10 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
}
ui->Append(PresetCollection::separator("Add a new printer"), *bmp);
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
return selected_preset_item;
return selected_preset_item;
}
// Update a dirty floag of the current preset, update the labels of the UI component accordingly.
@ -1117,11 +1146,11 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
const Preset *preset = this->find_preset(preset_name, false);
// The old_label could be the "----- system presets ------" or the "------- user presets --------" separator.
// 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()));
}
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.
@ -1134,15 +1163,15 @@ bool PresetCollection::update_dirty_ui(wxBitmapComboBox *ui)
template<class T>
void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase &this_c)
{
const T* opt_init = static_cast<const T*>(other.option(opt_key));
const T* opt_cur = static_cast<const T*>(this_c.option(opt_key));
int opt_init_max_id = opt_init->values.size() - 1;
for (int i = 0; i < opt_cur->values.size(); i++)
{
int init_id = i <= opt_init_max_id ? i : 0;
if (opt_cur->values[i] != opt_init->values[init_id])
vec.emplace_back(opt_key + "#" + std::to_string(i));
}
const T* opt_init = static_cast<const T*>(other.option(opt_key));
const T* opt_cur = static_cast<const T*>(this_c.option(opt_key));
int opt_init_max_id = opt_init->values.size() - 1;
for (int i = 0; i < opt_cur->values.size(); i++)
{
int init_id = i <= opt_init_max_id ? i : 0;
if (opt_cur->values[i] != opt_init->values[init_id])
vec.emplace_back(opt_key + "#" + std::to_string(i));
}
}
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
@ -1176,10 +1205,10 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
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) {
if (edited != nullptr && reference != nullptr) {
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
deep_diff(edited->config, reference->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.
@ -1202,19 +1231,19 @@ Preset& PresetCollection::select_preset(size_t idx)
idx = first_visible_idx();
m_idx_selected = idx;
m_edited_preset = m_presets[idx];
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible;
bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets;
for (size_t i = 0; i < m_num_default_presets; ++i)
m_presets[i].is_visible = default_visible;
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)
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 {
@ -1237,11 +1266,11 @@ bool PresetCollection::select_preset_by_name(const std::string &name_w_suffix, b
}
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)
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.
@ -1308,9 +1337,9 @@ std::vector<std::string> PresetCollection::system_preset_names() const
++ num;
std::vector<std::string> out;
out.reserve(num);
for (const Preset &preset : m_presets)
if (preset.is_system)
out.emplace_back(preset.name);
for (const Preset &preset : m_presets)
if (preset.is_system)
out.emplace_back(preset.name);
std::sort(out.begin(), out.end());
return out;
}
@ -1318,7 +1347,7 @@ std::vector<std::string> PresetCollection::system_preset_names() const
// 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");
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();
}
@ -1329,13 +1358,13 @@ void PresetCollection::clear_bitmap_cache()
wxString PresetCollection::separator(const std::string &label)
{
return wxString::FromUTF8(PresetCollection::separator_head()) + _(label) + wxString::FromUTF8(PresetCollection::separator_tail());
return wxString::FromUTF8(PresetCollection::separator_head()) + _(label) + wxString::FromUTF8(PresetCollection::separator_tail());
}
const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const
{
const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const
{
const ConfigOptionEnumGeneric *opt_printer_technology = config.opt<ConfigOptionEnumGeneric>("printer_technology");
return this->default_preset((opt_printer_technology == nullptr || opt_printer_technology->value == ptFFF) ? 0 : 1);
return this->default_preset((opt_printer_technology == nullptr || opt_printer_technology->value == ptFFF) ? 0 : 1);
}
const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model_id) const

View file

@ -8,7 +8,7 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/PrintConfig.hpp"
#include "slic3r/Utils/Semver.hpp"
#include "libslic3r/Semver.hpp"
class wxBitmap;
class wxBitmapComboBox;

View file

@ -72,6 +72,8 @@ PresetBundle::PresetBundle() :
this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values = { "" };
this->filaments.default_preset().compatible_printers_condition();
this->filaments.default_preset().inherits();
// Set all the nullable values to nils.
this->filaments.default_preset().config.null_nullables();
this->sla_materials.default_preset().config.optptr("sla_material_settings_id", true);
this->sla_materials.default_preset().compatible_printers_condition();
@ -761,8 +763,11 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
}
// Load the configs into this->filaments and make them active.
this->filament_presets.clear();
for (size_t i = 0; i < configs.size(); ++ i) {
this->filament_presets = std::vector<std::string>(configs.size());
// To avoid incorrect selection of the first filament preset (means a value of Preset->m_idx_selected)
// in a case when next added preset take a place of previosly selected preset,
// we should add presets from last to first
for (int i = (int)configs.size()-1; i >= 0; 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];
@ -781,13 +786,13 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
if (i == 0)
suffix[0] = 0;
else
sprintf(suffix, "%d", i);
sprintf(suffix, "%d", (int)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);
this->filament_presets[i] = loaded->name;
}
}
// 4) Load the project config values (the per extruder wipe matrix etc).
@ -837,7 +842,7 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
return preset_name_dst;
// Try to generate another name.
char buf[64];
sprintf(buf, " (%d)", i);
sprintf(buf, " (%d)", (int)i);
preset_name_dst = preset_name_src + buf + bundle_name;
}
}
@ -1366,7 +1371,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
continue;
c << std::endl << "[" << presets->section_name() << ":" << preset.name << "]" << std::endl;
for (const std::string &opt_key : preset.config.keys())
c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl;
c << opt_key << " = " << preset.config.opt_serialize(opt_key) << std::endl;
}
}
@ -1379,7 +1384,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
for (size_t i = 0; i < this->filament_presets.size(); ++ i) {
char suffix[64];
if (i > 0)
sprintf(suffix, "_%d", i);
sprintf(suffix, "_%d", (int)i);
else
suffix[0] = 0;
c << "filament" << suffix << " = " << this->filament_presets[i] << std::endl;

View file

@ -14,20 +14,20 @@
namespace Slic3r {
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
self(new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe,
id == -1? wxID_ANY : id)),
m_timer(new wxTimer(self)),
m_prog (new wxGauge(self,
wxGA_HORIZONTAL,
100,
wxDefaultPosition,
wxDefaultSize)),
m_cancelbutton(new wxButton(self,
-1,
_(L("Cancel")),
wxDefaultPosition,
wxDefaultSize))
ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id)
: self{new wxStatusBar(parent ? parent : GUI::wxGetApp().mainframe,
id == -1 ? wxID_ANY : id)}
, m_prog{new wxGauge(self,
wxGA_HORIZONTAL,
100,
wxDefaultPosition,
wxDefaultSize)}
, m_cancelbutton{new wxButton(self,
-1,
_(L("Cancel")),
wxDefaultPosition,
wxDefaultSize)}
, m_timer{new wxTimer(self)}
{
m_prog->Hide();
m_cancelbutton->Hide();
@ -168,6 +168,11 @@ void ProgressStatusBar::set_status_text(const char *txt)
this->set_status_text(wxString::FromUTF8(txt));
}
wxString ProgressStatusBar::get_status_text() const
{
return self->GetStatusText();
}
void ProgressStatusBar::show_cancel_button()
{
if(m_cancelbutton) m_cancelbutton->Show();

View file

@ -2,7 +2,9 @@
#define PROGRESSSTATUSBAR_HPP
#include <memory>
#include <string>
#include <functional>
#include <string>
class wxTimer;
class wxGauge;
@ -23,9 +25,9 @@ namespace Slic3r {
class ProgressStatusBar
{
wxStatusBar *self; // we cheat! It should be the base class but: perl!
wxTimer *m_timer;
wxGauge *m_prog;
wxButton *m_cancelbutton;
std::unique_ptr<wxTimer> m_timer;
public:
/// Cancel callback function type
@ -51,6 +53,7 @@ public:
void set_status_text(const wxString& txt);
void set_status_text(const std::string& txt);
void set_status_text(const char *txt);
wxString get_status_text() const;
// Temporary methods to satisfy Perl side
void show_cancel_button();

View file

@ -6,7 +6,8 @@
#include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectList.hpp"
#include "Gizmos/GLGizmoBase.hpp"
#include "slic3r/GUI/3DScene.hpp"
#include "3DScene.hpp"
#include "Camera.hpp"
#include <GL/glew.h>
@ -47,6 +48,26 @@ Selection::VolumeCache::VolumeCache(const Geometry::Transformation& volume_trans
{
}
bool Selection::Clipboard::is_sla_compliant() const
{
if (m_mode == Selection::Volume)
return false;
for (const ModelObject* o : m_model.objects)
{
if (o->is_multiparts())
return false;
for (const ModelVolume* v : o->volumes)
{
if (v->is_modifier())
return false;
}
}
return true;
}
Selection::Selection()
: m_volumes(nullptr)
, m_model(nullptr)
@ -79,14 +100,15 @@ void Selection::set_volumes(GLVolumePtrs* volumes)
update_valid();
}
bool Selection::init(bool useVBOs)
// Init shall be called from the OpenGL render function, so that the OpenGL context is initialized!
bool Selection::init()
{
if (!m_arrow.init(useVBOs))
if (!m_arrow.init())
return false;
m_arrow.set_scale(5.0 * Vec3d::Ones());
if (!m_curved_arrow.init(useVBOs))
if (!m_curved_arrow.init())
return false;
m_curved_arrow.set_scale(5.0 * Vec3d::Ones());
@ -119,11 +141,13 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
needs_reset |= as_single_selection && !is_any_modifier() && volume->is_modifier;
needs_reset |= is_any_modifier() && !volume->is_modifier;
if (needs_reset)
clear();
if (!already_contained || needs_reset)
{
wxGetApp().plater()->take_snapshot(_(L("Selection-Add")));
if (needs_reset)
clear();
if (!keep_instance_mode)
m_mode = volume->is_modifier ? Volume : Instance;
}
@ -142,7 +166,8 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection, bool chec
}
case Instance:
{
do_add_instance(volume->object_idx(), volume->instance_idx());
Plater::SuppressSnapshots suppress(wxGetApp().plater());
add_instance(volume->object_idx(), volume->instance_idx(), as_single_selection);
break;
}
}
@ -156,6 +181,11 @@ void Selection::remove(unsigned int volume_idx)
if (!m_valid || ((unsigned int)m_volumes->size() <= volume_idx))
return;
if (!contains_volume(volume_idx))
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove")));
GLVolume* volume = (*m_volumes)[volume_idx];
switch (m_mode)
@ -181,13 +211,20 @@ void Selection::add_object(unsigned int object_idx, bool as_single_selection)
if (!m_valid)
return;
std::vector<unsigned int> volume_idxs = get_volume_idxs_from_object(object_idx);
if ((!as_single_selection && contains_all_volumes(volume_idxs)) ||
(as_single_selection && matches(volume_idxs)))
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Add Object")));
// resets the current list if needed
if (as_single_selection)
clear();
m_mode = Instance;
do_add_object(object_idx);
do_add_volumes(volume_idxs);
update_type();
this->set_bounding_boxes_dirty();
@ -198,6 +235,8 @@ void Selection::remove_object(unsigned int object_idx)
if (!m_valid)
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Object")));
do_remove_object(object_idx);
update_type();
@ -209,13 +248,20 @@ void Selection::add_instance(unsigned int object_idx, unsigned int instance_idx,
if (!m_valid)
return;
std::vector<unsigned int> volume_idxs = get_volume_idxs_from_instance(object_idx, instance_idx);
if ((!as_single_selection && contains_all_volumes(volume_idxs)) ||
(as_single_selection && matches(volume_idxs)))
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Add Instance")));
// resets the current list if needed
if (as_single_selection)
clear();
m_mode = Instance;
do_add_instance(object_idx, instance_idx);
do_add_volumes(volume_idxs);
update_type();
this->set_bounding_boxes_dirty();
@ -226,6 +272,8 @@ void Selection::remove_instance(unsigned int object_idx, unsigned int instance_i
if (!m_valid)
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Instance")));
do_remove_instance(object_idx, instance_idx);
update_type();
@ -237,21 +285,20 @@ void Selection::add_volume(unsigned int object_idx, unsigned int volume_idx, int
if (!m_valid)
return;
std::vector<unsigned int> volume_idxs = get_volume_idxs_from_volume(object_idx, instance_idx, volume_idx);
if ((!as_single_selection && contains_all_volumes(volume_idxs)) ||
(as_single_selection && matches(volume_idxs)))
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Add Volume")));
// resets the current list if needed
if (as_single_selection)
clear();
m_mode = Volume;
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
GLVolume* v = (*m_volumes)[i];
if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx))
{
if ((instance_idx != -1) && (v->instance_idx() == instance_idx))
do_add_volume(i);
}
}
do_add_volumes(volume_idxs);
update_type();
this->set_bounding_boxes_dirty();
@ -262,6 +309,8 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx)
if (!m_valid)
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Volume")));
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
GLVolume* v = (*m_volumes)[i];
@ -273,11 +322,67 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx)
this->set_bounding_boxes_dirty();
}
void Selection::add_volumes(EMode mode, const std::vector<unsigned int>& volume_idxs, bool as_single_selection)
{
if (!m_valid)
return;
if ((!as_single_selection && contains_all_volumes(volume_idxs)) ||
(as_single_selection && matches(volume_idxs)))
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Add Volumes")));
// resets the current list if needed
if (as_single_selection)
clear();
m_mode = mode;
for (unsigned int i : volume_idxs)
{
if (i < (unsigned int)m_volumes->size())
do_add_volume(i);
}
update_type();
this->set_bounding_boxes_dirty();
}
void Selection::remove_volumes(EMode mode, const std::vector<unsigned int>& volume_idxs)
{
if (!m_valid)
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove Volumes")));
m_mode = mode;
for (unsigned int i : volume_idxs)
{
if (i < (unsigned int)m_volumes->size())
do_remove_volume(i);
}
update_type();
this->set_bounding_boxes_dirty();
}
void Selection::add_all()
{
if (!m_valid)
return;
unsigned int count = 0;
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
if (!(*m_volumes)[i]->is_wipe_tower)
++count;
}
if ((unsigned int)m_list.size() == count)
return;
wxGetApp().plater()->take_snapshot(_(L("Selection-Add All")));
m_mode = Instance;
clear();
@ -291,11 +396,47 @@ void Selection::add_all()
this->set_bounding_boxes_dirty();
}
void Selection::remove_all()
{
if (!m_valid)
return;
if (is_empty())
return;
// Not taking the snapshot with non-empty Redo stack will likely be more confusing than losing the Redo stack.
// Let's wait for user feedback.
// if (!wxGetApp().plater()->can_redo())
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove All")));
m_mode = Instance;
clear();
}
void Selection::set_deserialized(EMode mode, const std::vector<std::pair<size_t, size_t>> &volumes_and_instances)
{
if (! m_valid)
return;
m_mode = mode;
for (unsigned int i : m_list)
(*m_volumes)[i]->selected = false;
m_list.clear();
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i)
if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), (*m_volumes)[i]->geometry_id))
this->do_add_volume(i);
update_type();
this->set_bounding_boxes_dirty();
}
void Selection::clear()
{
if (!m_valid)
return;
if (m_list.empty())
return;
for (unsigned int i : m_list)
{
(*m_volumes)[i]->selected = false;
@ -308,6 +449,9 @@ void Selection::clear()
// resets the cache in the sidebar
wxGetApp().obj_manipul()->reset_cache();
// #et_FIXME fake KillFocus from sidebar
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
}
// Update the selection based on the new instance IDs.
@ -382,6 +526,57 @@ bool Selection::is_from_single_object() const
return (0 <= idx) && (idx < 1000);
}
bool Selection::is_sla_compliant() const
{
if (m_mode == Volume)
return false;
for (unsigned int i : m_list)
{
if ((*m_volumes)[i]->is_modifier)
return false;
}
return true;
}
bool Selection::contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const
{
for (unsigned int i : volume_idxs)
{
if (m_list.find(i) == m_list.end())
return false;
}
return true;
}
bool Selection::contains_any_volume(const std::vector<unsigned int>& volume_idxs) const
{
for (unsigned int i : volume_idxs)
{
if (m_list.find(i) != m_list.end())
return true;
}
return false;
}
bool Selection::matches(const std::vector<unsigned int>& volume_idxs) const
{
unsigned int count = 0;
for (unsigned int i : volume_idxs)
{
if (m_list.find(i) != m_list.end())
++count;
else
return false;
}
return count == (unsigned int)m_list.size();
}
bool Selection::requires_uniform_scale() const
{
if (is_single_full_instance() || is_single_modifier() || is_single_volume())
@ -749,6 +944,8 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
double s = std::min(sx, std::min(sy, sz));
if (s != 1.0)
{
wxGetApp().plater()->take_snapshot(_(L("Scale To Fit")));
TransformationType type;
type.set_world();
type.set_relative();
@ -757,12 +954,12 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
// apply scale
start_dragging();
scale(s * Vec3d::Ones(), type);
wxGetApp().plater()->canvas3D()->do_scale();
wxGetApp().plater()->canvas3D()->do_scale(L("")); // avoid storing another snapshot
// center selection on print bed
start_dragging();
translate(print_volume.center() - get_bounding_box().center());
wxGetApp().plater()->canvas3D()->do_move();
wxGetApp().plater()->canvas3D()->do_move(L("")); // avoid storing another snapshot
wxGetApp().obj_manipul()->set_dirty();
}
@ -1033,61 +1230,68 @@ void Selection::render_center(bool gizmo_is_dragging) const
}
#endif // ENABLE_RENDER_SELECTION_CENTER
void Selection::render_sidebar_hints(const std::string& sidebar_field) const
void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const
{
if (sidebar_field.empty())
return;
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
if (!boost::starts_with(sidebar_field, "layer"))
{
shader.start_using();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_LIGHTING));
}
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
const Vec3d& center = get_bounding_box().center();
if (is_single_full_instance() && ! wxGetApp().obj_manipul()->get_world_coordinates())
if (!boost::starts_with(sidebar_field, "layer"))
{
glsafe(::glTranslated(center(0), center(1), center(2)));
if (!boost::starts_with(sidebar_field, "position"))
const Vec3d& center = get_bounding_box().center();
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates())
{
Transform3d orient_matrix = Transform3d::Identity();
if (boost::starts_with(sidebar_field, "scale"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::starts_with(sidebar_field, "rotation"))
glsafe(::glTranslated(center(0), center(1), center(2)));
if (!boost::starts_with(sidebar_field, "position"))
{
if (boost::ends_with(sidebar_field, "x"))
Transform3d orient_matrix = Transform3d::Identity();
if (boost::starts_with(sidebar_field, "scale"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::ends_with(sidebar_field, "y"))
else if (boost::starts_with(sidebar_field, "rotation"))
{
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
if (rotation(0) == 0.0)
if (boost::ends_with(sidebar_field, "x"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else
orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
else if (boost::ends_with(sidebar_field, "y"))
{
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
if (rotation(0) == 0.0)
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else
orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
}
}
glsafe(::glMultMatrixd(orient_matrix.data()));
}
}
else if (is_single_volume() || is_single_modifier())
{
glsafe(::glTranslated(center(0), center(1), center(2)));
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
}
else if (is_single_volume() || is_single_modifier())
{
glsafe(::glTranslated(center(0), center(1), center(2)));
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
else
{
glsafe(::glTranslated(center(0), center(1), center(2)));
if (requires_local_axes())
else
{
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
glsafe(::glTranslated(center(0), center(1), center(2)));
if (requires_local_axes())
{
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
}
}
@ -1099,10 +1303,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
render_sidebar_scale_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "size"))
render_sidebar_size_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "layer"))
render_sidebar_layers_hints(sidebar_field);
glsafe(::glPopMatrix());
glsafe(::glDisable(GL_LIGHTING));
if (!boost::starts_with(sidebar_field, "layer"))
{
glsafe(::glDisable(GL_LIGHTING));
shader.stop_using();
}
}
bool Selection::requires_local_axes() const
@ -1123,10 +1333,10 @@ void Selection::copy_to_clipboard()
ModelObject* dst_object = m_clipboard.add_object();
dst_object->name = src_object->name;
dst_object->input_file = src_object->input_file;
dst_object->config = src_object->config;
static_cast<DynamicPrintConfig&>(dst_object->config) = static_cast<const DynamicPrintConfig&>(src_object->config);
dst_object->sla_support_points = src_object->sla_support_points;
dst_object->sla_points_status = src_object->sla_points_status;
dst_object->layer_height_ranges = src_object->layer_height_ranges;
dst_object->layer_config_ranges = src_object->layer_config_ranges; // #ys_FIXME_experiment
dst_object->layer_height_profile = src_object->layer_height_profile;
dst_object->origin_translation = src_object->origin_translation;
@ -1181,6 +1391,110 @@ void Selection::paste_from_clipboard()
}
}
std::vector<unsigned int> Selection::get_volume_idxs_from_object(unsigned int object_idx) const
{
std::vector<unsigned int> idxs;
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
if ((*m_volumes)[i]->object_idx() == object_idx)
idxs.push_back(i);
}
return idxs;
}
std::vector<unsigned int> Selection::get_volume_idxs_from_instance(unsigned int object_idx, unsigned int instance_idx) const
{
std::vector<unsigned int> idxs;
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
const GLVolume* v = (*m_volumes)[i];
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
idxs.push_back(i);
}
return idxs;
}
std::vector<unsigned int> Selection::get_volume_idxs_from_volume(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx) const
{
std::vector<unsigned int> idxs;
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
const GLVolume* v = (*m_volumes)[i];
if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx))
{
if ((instance_idx != -1) && (v->instance_idx() == instance_idx))
idxs.push_back(i);
}
}
return idxs;
}
std::vector<unsigned int> Selection::get_missing_volume_idxs_from(const std::vector<unsigned int>& volume_idxs) const
{
std::vector<unsigned int> idxs;
for (unsigned int i : m_list)
{
std::vector<unsigned int>::const_iterator it = std::find(volume_idxs.begin(), volume_idxs.end(), i);
if (it == volume_idxs.end())
idxs.push_back(i);
}
return idxs;
}
std::vector<unsigned int> Selection::get_unselected_volume_idxs_from(const std::vector<unsigned int>& volume_idxs) const
{
std::vector<unsigned int> idxs;
for (unsigned int i : volume_idxs)
{
if (m_list.find(i) == m_list.end())
idxs.push_back(i);
}
return idxs;
}
void Selection::toggle_instance_printable_state()
{
int instance_idx = get_instance_idx();
if (instance_idx == -1)
return;
int obj_idx = get_object_idx();
if ((0 <= obj_idx) && (obj_idx < (int)m_model->objects.size()))
{
ModelObject* model_object = m_model->objects[obj_idx];
if ((0 <= instance_idx) && (instance_idx < (int)model_object->instances.size()))
{
ModelInstance* instance = model_object->instances[instance_idx];
const bool printable = !instance->printable;
wxString snapshot_text = model_object->instances.size() == 1 ? wxString::Format("%s %s",
printable ? _(L("Set Printable")) : _(L("Set Unprintable")), model_object->name) :
printable ? _(L("Set Printable Instance")) : _(L("Set Unprintable Instance"));
wxGetApp().plater()->take_snapshot(snapshot_text);
instance->printable = printable;
for (GLVolume* volume : *m_volumes)
{
if ((volume->object_idx() == obj_idx) && (volume->instance_idx() == instance_idx))
volume->printable = instance->printable;
}
wxGetApp().obj_list()->update_printable_state(obj_idx, instance_idx);
}
}
}
void Selection::update_valid()
{
m_valid = (m_volumes != nullptr) && (m_model != nullptr);
@ -1427,22 +1741,11 @@ void Selection::do_add_volume(unsigned int volume_idx)
(*m_volumes)[volume_idx]->selected = true;
}
void Selection::do_add_instance(unsigned int object_idx, unsigned int instance_idx)
void Selection::do_add_volumes(const std::vector<unsigned int>& volume_idxs)
{
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
for (unsigned int i : volume_idxs)
{
GLVolume* v = (*m_volumes)[i];
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
do_add_volume(i);
}
}
void Selection::do_add_object(unsigned int object_idx)
{
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i)
{
GLVolume* v = (*m_volumes)[i];
if (v->object_idx() == object_idx)
if (i < (unsigned int)m_volumes->size())
do_add_volume(i);
}
}
@ -1672,6 +1975,78 @@ void Selection::render_sidebar_size_hints(const std::string& sidebar_field) cons
render_sidebar_scale_hints(sidebar_field);
}
void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const
{
static const double Margin = 10.0;
std::string field = sidebar_field;
// extract max_z
std::string::size_type pos = field.rfind("_");
if (pos == std::string::npos)
return;
double max_z = std::stod(field.substr(pos + 1));
// extract min_z
field = field.substr(0, pos);
pos = field.rfind("_");
if (pos == std::string::npos)
return;
double min_z = std::stod(field.substr(pos + 1));
// extract type
field = field.substr(0, pos);
pos = field.rfind("_");
if (pos == std::string::npos)
return;
int type = std::stoi(field.substr(pos + 1));
const BoundingBoxf3& box = get_bounding_box();
const float min_x = box.min(0) - Margin;
const float max_x = box.max(0) + Margin;
const float min_y = box.min(1) - Margin;
const float max_y = box.max(1) + Margin;
// view dependend order of rendering to keep correct transparency
bool camera_on_top = wxGetApp().plater()->get_camera().get_theta() <= 90.0f;
float z1 = camera_on_top ? min_z : max_z;
float z2 = camera_on_top ? max_z : min_z;
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
::glBegin(GL_QUADS);
if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2)))
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
else
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
::glVertex3f(min_x, min_y, z1);
::glVertex3f(max_x, min_y, z1);
::glVertex3f(max_x, max_y, z1);
::glVertex3f(min_x, max_y, z1);
glsafe(::glEnd());
::glBegin(GL_QUADS);
if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1)))
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
else
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
::glVertex3f(min_x, min_y, z2);
::glVertex3f(max_x, min_y, z2);
::glVertex3f(max_x, max_y, z2);
::glVertex3f(min_x, max_y, z2);
glsafe(::glEnd());
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
}
void Selection::render_sidebar_position_hint(Axis axis) const
{
m_arrow.set_color(AXES_COLOR[axis], 3);
@ -1890,30 +2265,79 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
void Selection::paste_volumes_from_clipboard()
{
int obj_idx = get_object_idx();
if ((obj_idx < 0) || ((int)m_model->objects.size() <= obj_idx))
#ifdef _DEBUG
check_model_ids_validity(*m_model);
#endif /* _DEBUG */
int dst_obj_idx = get_object_idx();
if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx))
return;
ModelObject* dst_object = m_model->objects[dst_obj_idx];
int dst_inst_idx = get_instance_idx();
if ((dst_inst_idx < 0) || ((int)dst_object->instances.size() <= dst_inst_idx))
return;
ModelObject* src_object = m_clipboard.get_object(0);
if (src_object != nullptr)
{
ModelObject* dst_object = m_model->objects[obj_idx];
ModelInstance* dst_instance = dst_object->instances[dst_inst_idx];
BoundingBoxf3 dst_instance_bb = dst_object->instance_bounding_box(dst_inst_idx);
Transform3d src_matrix = src_object->instances[0]->get_transformation().get_matrix(true);
Transform3d dst_matrix = dst_instance->get_transformation().get_matrix(true);
bool from_same_object = (src_object->input_file == dst_object->input_file) && src_matrix.isApprox(dst_matrix);
// used to keep relative position of multivolume selections when pasting from another object
BoundingBoxf3 total_bb;
ModelVolumePtrs volumes;
for (ModelVolume* src_volume : src_object->volumes)
{
ModelVolume* dst_volume = dst_object->add_volume(*src_volume);
dst_volume->set_new_unique_id();
double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
dst_volume->translate(offset, offset, 0.0);
if (from_same_object)
{
// // if the volume comes from the same object, apply the offset in world system
// double offset = wxGetApp().plater()->canvas3D()->get_size_proportional_to_max_bed_size(0.05);
// dst_volume->translate(dst_matrix.inverse() * Vec3d(offset, offset, 0.0));
}
else
{
// if the volume comes from another object, apply the offset as done when adding modifiers
// see ObjectList::load_generic_subobject()
total_bb.merge(dst_volume->mesh().bounding_box().transformed(src_volume->get_matrix()));
}
volumes.push_back(dst_volume);
#ifdef _DEBUG
check_model_ids_validity(*m_model);
#endif /* _DEBUG */
}
wxGetApp().obj_list()->paste_volumes_into_list(obj_idx, volumes);
// keeps relative position of multivolume selections
if (!from_same_object)
{
for (ModelVolume* v : volumes)
{
v->set_offset((v->get_offset() - total_bb.center()) + dst_matrix.inverse() * (Vec3d(dst_instance_bb.max(0), dst_instance_bb.min(1), dst_instance_bb.min(2)) + 0.5 * total_bb.size() - dst_instance->get_transformation().get_offset()));
}
}
wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes);
}
#ifdef _DEBUG
check_model_ids_validity(*m_model);
#endif /* _DEBUG */
}
void Selection::paste_objects_from_clipboard()
{
#ifdef _DEBUG
check_model_ids_validity(*m_model);
#endif /* _DEBUG */
std::vector<size_t> object_idxs;
const ModelObjectPtrs& src_objects = m_clipboard.get_objects();
for (const ModelObject* src_object : src_objects)
@ -1927,9 +2351,16 @@ void Selection::paste_objects_from_clipboard()
}
object_idxs.push_back(m_model->objects.size() - 1);
#ifdef _DEBUG
check_model_ids_validity(*m_model);
#endif /* _DEBUG */
}
wxGetApp().obj_list()->paste_objects_into_list(object_idxs);
#ifdef _DEBUG
check_model_ids_validity(*m_model);
#endif /* _DEBUG */
}
} // namespace GUI

View file

@ -11,8 +11,8 @@ typedef class GLUquadric GLUquadricObj;
#endif // ENABLE_RENDER_SELECTION_CENTER
namespace Slic3r {
class Shader;
namespace GUI {
class TransformationType
{
public:
@ -152,6 +152,8 @@ public:
void reset() { m_model.clear_objects(); }
bool is_empty() const { return m_model.objects.empty(); }
bool is_sla_compliant() const;
ModelObject* add_object() { return m_model.add_object(); }
ModelObject* get_object(unsigned int id) { return (id < (unsigned int)m_model.objects.size()) ? m_model.objects[id] : nullptr; }
const ModelObjectPtrs& get_objects() const { return m_model.objects; }
@ -169,7 +171,7 @@ private:
Vec3d dragging_center;
// Map from indices of ModelObject instances in Model::objects
// to a set of indices of ModelVolume instances in ModelObject::instances
// Here the index means a position inside the respective std::vector, not ModelID.
// Here the index means a position inside the respective std::vector, not ObjectID.
ObjectIdxsToInstanceIdxsMap content;
};
@ -210,7 +212,7 @@ public:
#endif // ENABLE_RENDER_SELECTION_CENTER
void set_volumes(GLVolumePtrs* volumes);
bool init(bool useVBOs);
bool init();
bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; }
@ -233,7 +235,14 @@ public:
void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true);
void remove_volume(unsigned int object_idx, unsigned int volume_idx);
void add_volumes(EMode mode, const std::vector<unsigned int>& volume_idxs, bool as_single_selection = true);
void remove_volumes(EMode mode, const std::vector<unsigned int>& volume_idxs);
void add_all();
void remove_all();
// To be called after Undo or Redo once the volumes are updated.
void set_deserialized(EMode mode, const std::vector<std::pair<size_t, size_t>> &volumes_and_instances);
// Update the selection based on the new instance IDs.
void instances_changed(const std::vector<size_t> &instance_ids_selected);
@ -257,8 +266,16 @@ public:
bool is_mixed() const { return m_type == Mixed; }
bool is_from_single_instance() const { return get_instance_idx() != -1; }
bool is_from_single_object() const;
bool is_sla_compliant() const;
bool contains_volume(unsigned int volume_idx) const { return m_list.find(volume_idx) != m_list.end(); }
// returns true if the selection contains all the given indices
bool contains_all_volumes(const std::vector<unsigned int>& volume_idxs) const;
// returns true if the selection contains at least one of the given indices
bool contains_any_volume(const std::vector<unsigned int>& volume_idxs) const;
// returns true if the selection contains all and only the given indices
bool matches(const std::vector<unsigned int>& volume_idxs) const;
bool requires_uniform_scale() const;
// Returns the the object id if the selection is from a single object, otherwise is -1
@ -299,7 +316,7 @@ public:
#if ENABLE_RENDER_SELECTION_CENTER
void render_center(bool gizmo_is_dragging) const;
#endif // ENABLE_RENDER_SELECTION_CENTER
void render_sidebar_hints(const std::string& sidebar_field) const;
void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const;
bool requires_local_axes() const;
@ -308,13 +325,25 @@ public:
const Clipboard& get_clipboard() const { return m_clipboard; }
// returns the list of idxs of the volumes contained into the object with the given idx
std::vector<unsigned int> get_volume_idxs_from_object(unsigned int object_idx) const;
// returns the list of idxs of the volumes contained into the instance with the given idxs
std::vector<unsigned int> get_volume_idxs_from_instance(unsigned int object_idx, unsigned int instance_idx) const;
// returns the idx of the volume corresponding to the volume with the given idxs
std::vector<unsigned int> get_volume_idxs_from_volume(unsigned int object_idx, unsigned int instance_idx, unsigned int volume_idx) const;
// returns the list of idxs of the volumes contained in the selection but not in the given list
std::vector<unsigned int> get_missing_volume_idxs_from(const std::vector<unsigned int>& volume_idxs) const;
// returns the list of idxs of the volumes contained in the given list but not in the selection
std::vector<unsigned int> get_unselected_volume_idxs_from(const std::vector<unsigned int>& volume_idxs) const;
void toggle_instance_printable_state();
private:
void update_valid();
void update_type();
void set_caches();
void do_add_volume(unsigned int volume_idx);
void do_add_instance(unsigned int object_idx, unsigned int instance_idx);
void do_add_object(unsigned int object_idx);
void do_add_volumes(const std::vector<unsigned int>& volume_idxs);
void do_remove_volume(unsigned int volume_idx);
void do_remove_instance(unsigned int object_idx, unsigned int instance_idx);
void do_remove_object(unsigned int object_idx);
@ -329,10 +358,13 @@ private:
void render_sidebar_rotation_hints(const std::string& sidebar_field) const;
void render_sidebar_scale_hints(const std::string& sidebar_field) const;
void render_sidebar_size_hints(const std::string& sidebar_field) const;
void render_sidebar_layers_hints(const std::string& sidebar_field) const;
void render_sidebar_position_hint(Axis axis) const;
void render_sidebar_rotation_hint(Axis axis) const;
void render_sidebar_scale_hint(Axis axis) const;
void render_sidebar_size_hint(Axis axis, double length) const;
public:
enum SyncRotationType {
// Do not synchronize rotation. Either not rotating at all, or rotating by world Z axis.
SYNC_ROTATION_NONE = 0,
@ -343,6 +375,8 @@ private:
};
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
void synchronize_unselected_volumes();
private:
void ensure_on_bed();
bool is_from_fully_selected_instance(unsigned int volume_idx) const;

View file

@ -2,6 +2,7 @@
#include "I18N.hpp"
#include "3DScene.hpp"
#include "GUI.hpp"
#include "../Utils/UndoRedo.hpp"
#include <string>
@ -10,6 +11,14 @@
#include "GUI_App.hpp"
#include "wxExtensions.hpp"
#ifdef _WIN32
// The standard Windows includes.
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <psapi.h>
#endif /* _WIN32 */
namespace Slic3r {
namespace GUI {
@ -36,6 +45,31 @@ std::string get_main_info(bool format_as_html)
"System Version: "
#endif
<< b_end << wxPlatformInfo::Get().GetOperatingSystemDescription() << line_end;
out << b_start << "Total RAM size [MB]: " << b_end << Slic3r::format_memsize_MB(Slic3r::total_physical_memory());
return out.str();
}
std::string get_mem_info(bool format_as_html)
{
std::stringstream out;
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";
std::string mem_info_str = log_memory_info(true);
std::istringstream mem_info(mem_info_str);
std::string value;
while (std::getline(mem_info, value, ':')) {
out << b_start << (value+": ") << b_end;
std::getline(mem_info, value, ';');
out << value << line_end;
}
const Slic3r::UndoRedo::Stack &stack = wxGetApp().plater()->undo_redo_stack_main();
out << b_start << "RAM size reserved for the Undo / Redo stack: " << b_end << Slic3r::format_memsize_MB(stack.get_memory_limit()) << line_end;
out << b_start << "RAM size occupied by the Undo / Redo stack: " << b_end << Slic3r::format_memsize_MB(stack.memsize()) << line_end << line_end;
return out.str();
}
@ -111,7 +145,7 @@ SysInfoDialog::SysInfoDialog()
"</font>"
"</body>"
"</html>", bgr_clr_str, text_clr_str, text_clr_str,
_3DScene::get_gl_info(true, true));
get_mem_info(true) + "<br>" + _3DScene::get_gl_info(true, true));
m_opengl_info_html->SetPage(text);
main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15);
}

File diff suppressed because it is too large Load diff

View file

@ -142,6 +142,12 @@ protected:
PresetDependencies m_compatible_printers;
PresetDependencies m_compatible_prints;
/* Indicates, that default preset or preset inherited from default is selected
* This value is used for a options color updating
* (use green color only for options, which values are equal to system values)
*/
bool m_is_default_preset {false};
ScalableButton* m_undo_btn;
ScalableButton* m_undo_to_sys_btn;
ScalableButton* m_question_btn;
@ -212,7 +218,7 @@ protected:
int m_em_unit;
// To avoid actions with no-completed Tab
bool m_complited { false };
ConfigOptionMode m_mode = comSimple;
ConfigOptionMode m_mode = comExpert; // to correct first Tab update_visibility() set mode to Expert
public:
PresetBundle* m_preset_bundle;
@ -331,6 +337,12 @@ class TabFilament : public Tab
{
ogStaticText* m_volumetric_speed_description_line;
ogStaticText* m_cooling_description_line;
void add_filament_overrides_page();
void update_filament_overrides_page();
void update_volumetric_flow_preset_hints();
std::map<std::string, wxCheckBox*> m_overrides_options;
public:
TabFilament(wxNotebook* parent) :
// Tab(parent, _(L("Filament Settings")), L("filament")) {}
@ -359,6 +371,7 @@ public:
wxButton* m_serial_test_btn = nullptr;
ScalableButton* m_print_host_test_btn = nullptr;
ScalableButton* m_printhost_browse_btn = nullptr;
ScalableButton* m_reset_to_filament_color = nullptr;
size_t m_extruders_count;
size_t m_extruders_count_old = 0;

View file

@ -55,7 +55,7 @@ MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_on
auto *link = new wxHyperlinkCtrl(this, wxID_ANY, _(L("Changelog && Download")), url_wx);
content_sizer->Add(link);
} else {
const auto lang_code = wxGetApp().current_language_code().ToStdString();
const auto lang_code = wxGetApp().current_language_code_safe().ToStdString();
const std::string url_log = (boost::format(URL_CHANGELOG) % lang_code).str();
const wxString url_log_wx = from_u8(url_log);
@ -100,7 +100,7 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates) :
content_sizer->Add(text);
content_sizer->AddSpacer(VERT_SPACING);
const auto lang_code = wxGetApp().current_language_code().ToStdString();
const auto lang_code = wxGetApp().current_language_code_safe().ToStdString();
auto *versions = new wxBoxSizer(wxVERTICAL);
for (const auto &update : updates) {

View file

@ -5,7 +5,7 @@
#include <unordered_map>
#include <vector>
#include "slic3r/Utils/Semver.hpp"
#include "libslic3r/Semver.hpp"
#include "MsgDialog.hpp"
class wxBoxSizer;

View file

@ -173,7 +173,7 @@ void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_
wxSize text_size = GetTextExtent(info);
auto info_str = new wxStaticText(page, wxID_ANY, info ,wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER);
info_str->Wrap(int(0.6*text_size.x));
sizer->Add( info_str, 0, wxALIGN_CENTER_HORIZONTAL | wxEXPAND);
sizer->Add( info_str, 0, wxEXPAND);
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);

File diff suppressed because it is too large Load diff

View file

@ -11,15 +11,19 @@
#include <wx/sizer.h>
#include <wx/slider.h>
#include <wx/menu.h>
#include <wx/wx.h>
#include <vector>
#include <set>
#include <functional>
namespace Slic3r {
enum class ModelVolumeType : int;
enum class ModelVolumeType : int;
};
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
#ifdef __WXMSW__
void msw_rescale_menu(wxMenu* menu);
#else /* __WXMSW__ */
@ -33,11 +37,14 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const
std::function<void(wxCommandEvent& event)> cb, const std::string& icon = "", wxEvtHandler* event_handler = nullptr,
std::function<bool()> const cb_condition = []() { return true; }, wxWindow* parent = nullptr);
wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description,
wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxString& string, const wxString& description,
const std::string& icon = "",
std::function<bool()> const cb_condition = []() { return true; }, wxWindow* parent = nullptr);
wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
wxMenuItem* append_menu_radio_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
wxMenuItem* append_menu_check_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, wxEvtHandler* event_handler);
class wxDialog;
@ -45,7 +52,7 @@ void edit_tooltip(wxString& tooltip);
void msw_buttons_rescale(wxDialog* dlg, const int em_unit, const std::vector<int>& btn_ids);
int em_unit(wxWindow* win);
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name,
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name,
const int px_cnt = 16, const bool is_horizontal = false, const bool grayscale = false);
class wxCheckListBoxComboPopup : public wxCheckListBox, public wxComboPopup
@ -89,23 +96,23 @@ public:
class wxDataViewTreeCtrlComboPopup: public wxDataViewTreeCtrl, public wxComboPopup
{
static const unsigned int DefaultWidth;
static const unsigned int DefaultHeight;
static const unsigned int DefaultItemHeight;
static const unsigned int DefaultWidth;
static const unsigned int DefaultHeight;
static const unsigned int DefaultItemHeight;
wxString m_text;
int m_cnt_open_items{0};
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 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; }
virtual void OnKeyEvent(wxKeyEvent& evt);
void OnDataViewTreeCtrlSelection(wxCommandEvent& evt);
void SetItemsCnt(int cnt) { m_cnt_open_items = cnt; }
};
@ -118,7 +125,7 @@ class DataViewBitmapText : public wxObject
public:
DataViewBitmapText( const wxString &text = wxEmptyString,
const wxBitmap& bmp = wxNullBitmap) :
m_text(text),
m_text(text),
m_bmp(bmp)
{ }
@ -155,16 +162,33 @@ DECLARE_VARIANT_OBJECT(DataViewBitmapText)
// ----------------------------------------------------------------------------
// ObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
// ObjectDataViewModelNode: a node inside ObjectDataViewModel
// ----------------------------------------------------------------------------
enum ItemType {
itUndef = 0,
itObject = 1,
itVolume = 2,
itInstanceRoot = 4,
itInstance = 8,
itSettings = 16
itUndef = 0,
itObject = 1,
itVolume = 2,
itInstanceRoot = 4,
itInstance = 8,
itSettings = 16,
itLayerRoot = 32,
itLayer = 64,
};
enum ColumnNumber
{
colName = 0, // item name
colPrint , // printable property
colExtruder , // extruder selection
colEditing , // item editing
};
enum PrintIndicator
{
piUndef = 0, // no print indicator
piPrintable , // printable
piUnprintable , // unprintable
};
class ObjectDataViewModelNode;
@ -172,11 +196,12 @@ WX_DEFINE_ARRAY_PTR(ObjectDataViewModelNode*, MyObjectTreeModelNodePtrArray);
class ObjectDataViewModelNode
{
ObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children;
ObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children;
wxBitmap m_empty_bmp;
size_t m_volumes_cnt = 0;
std::vector< std::string > m_opt_categories;
t_layer_height_range m_layer_range = { 0.0f, 0.0f };
wxString m_name;
wxBitmap& m_bmp = m_empty_bmp;
@ -185,175 +210,166 @@ class ObjectDataViewModelNode
bool m_container = false;
wxString m_extruder = "default";
wxBitmap m_action_icon;
PrintIndicator m_printable {piUndef};
wxBitmap m_printable_icon;
std::string m_action_icon_name = "";
Slic3r::ModelVolumeType m_volume_type;
public:
ObjectDataViewModelNode(const wxString &name,
ObjectDataViewModelNode(const wxString &name,
const wxString& extruder):
m_parent(NULL),
m_name(name),
m_type(itObject),
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_action_icon();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
const wxBitmap& bmp,
const wxString& extruder,
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
const wxBitmap& bmp,
const wxString& extruder,
const int idx = -1 ) :
m_parent (parent),
m_name (sub_obj_name),
m_type (itVolume),
m_name (sub_obj_name),
m_type (itVolume),
m_idx (idx),
m_extruder (extruder)
{
m_bmp = bmp;
#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__
m_bmp = bmp;
set_action_icon();
init_container();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
const int idx = -1,
const wxString& extruder = wxEmptyString );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode()
{
// free all our children nodes
size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++)
{
ObjectDataViewModelNode *child = m_children[i];
delete child;
}
}
~ObjectDataViewModelNode()
{
// free all our children nodes
size_t count = m_children.GetCount();
for (size_t i = 0; i < count; i++)
{
ObjectDataViewModelNode *child = m_children[i];
delete child;
}
#ifndef NDEBUG
// Indicate that the object was deleted.
m_idx = -2;
#endif /* NDEBUG */
}
void init_container();
bool IsContainer() const
{
return m_container;
}
ObjectDataViewModelNode* GetParent()
{
return m_parent;
}
MyObjectTreeModelNodePtrArray& GetChildren()
{
return m_children;
}
ObjectDataViewModelNode* GetNthChild(unsigned int n)
{
return m_children.Item(n);
}
void Insert(ObjectDataViewModelNode* child, unsigned int n)
{
if (!m_container)
m_container = true;
m_children.Insert(child, n);
}
void Append(ObjectDataViewModelNode* child)
{
if (!m_container)
m_container = true;
m_children.Add(child);
}
void RemoveAllChildren()
{
if (GetChildCount() == 0)
return;
for (int id = int(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;
}
}
ObjectDataViewModelNode* GetParent()
{
assert(m_parent == nullptr || m_parent->valid());
return m_parent;
}
MyObjectTreeModelNodePtrArray& GetChildren()
{
return m_children;
}
ObjectDataViewModelNode* GetNthChild(unsigned int n)
{
return m_children.Item(n);
}
void Insert(ObjectDataViewModelNode* child, unsigned int n)
{
if (!m_container)
m_container = true;
m_children.Insert(child, n);
}
void Append(ObjectDataViewModelNode* child)
{
if (!m_container)
m_container = true;
m_children.Add(child);
}
void RemoveAllChildren()
{
if (GetChildCount() == 0)
return;
for (int id = int(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();
}
size_t GetChildCount() const
{
return m_children.GetCount();
}
bool SetValue(const wxVariant &variant, unsigned int col)
{
switch (col)
{
case 0:{
DataViewBitmapText data;
data << variant;
m_bmp = data.GetBitmap();
m_name = data.GetText();
return true;}
case 1:
m_extruder = variant.GetString();
return true;
case 2:
m_action_icon << variant;
return true;
default:
printf("MyObjectTreeModel::SetValue: wrong column");
}
return false;
}
bool SetValue(const wxVariant &variant, unsigned int col);
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
void SetIdx(const int& idx);
int GetIdx() const { return m_idx; }
t_layer_height_range GetLayerRange() const { return m_layer_range; }
PrintIndicator IsPrintable() const { return m_printable; }
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
{
// ! Don't overwrite other values because of equality of this values for all children --
m_name = from_node.m_name;
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
{
// ! Don't overwrite other values because of equality of this values for all children --
m_name = from_node.m_name;
m_bmp = from_node.m_bmp;
m_idx = from_node.m_idx;
m_extruder = from_node.m_extruder;
m_type = from_node.m_type;
}
}
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;
bool SwapChildrens(int frst_id, int scnd_id) {
if (GetChildCount() < 2 ||
frst_id < 0 || (size_t)frst_id >= GetChildCount() ||
scnd_id < 0 || (size_t)scnd_id >= GetChildCount())
return false;
ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
ObjectDataViewModelNode new_scnd = *GetNthChild(frst_id);
ObjectDataViewModelNode new_frst = *GetNthChild(scnd_id);
new_scnd.m_idx = m_children.Item(scnd_id)->m_idx;
new_frst.m_idx = m_children.Item(frst_id)->m_idx;
m_children.Item(frst_id)->AssignAllVal(new_frst);
m_children.Item(scnd_id)->AssignAllVal(new_scnd);
return true;
}
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_action_icon();
// Set action icons for node
void set_action_icon();
// Set printable icon for node
void set_printable_icon(PrintIndicator printable);
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
bool update_settings_digest(const std::vector<std::string>& categories);
int volume_type() const { return int(m_volume_type); }
void msw_rescale();
#ifndef NDEBUG
bool valid();
#endif /* NDEBUG */
bool invalid() const { return m_idx < -1; }
private:
friend class ObjectDataViewModel;
};
@ -367,7 +383,7 @@ wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
class ObjectDataViewModel :public wxDataViewModel
{
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap*> m_volume_bmps;
wxBitmap* m_warning_bmp;
@ -377,7 +393,7 @@ public:
ObjectDataViewModel();
~ObjectDataViewModel();
wxDataViewItem Add( const wxString &name,
wxDataViewItem Add( const wxString &name,
const int extruder,
const bool has_errors = false);
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
@ -388,83 +404,109 @@ public:
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll();
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, const std::vector<bool>& print_indicator);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder = 0,
const int index = -1);
wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll();
void DeleteChildren(wxDataViewItem& parent);
void DeleteVolumeChildren(wxDataViewItem& parent);
void DeleteSettings(const wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
int GetIdByItem(const wxDataViewItem& item) const;
wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetIdByItem(const wxDataViewItem& item) const;
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetObjectIdByItem(const wxDataViewItem& item) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const;
int GetInstanceIdByItem(const wxDataViewItem& item) const;
int GetLayerIdByItem(const wxDataViewItem& item) const;
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
int GetRowByItem(const wxDataViewItem& item) const;
bool IsEmpty() { return m_objects.empty(); }
bool InvalidItem(const wxDataViewItem& item);
// helper method for wxLog
// helper method for wxLog
wxString GetName(const wxDataViewItem &item) const;
wxString GetName(const wxDataViewItem &item) const;
wxBitmap& GetBitmap(const wxDataViewItem &item) const;
// helper methods to change the model
// 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 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,
virtual void GetValue( wxVariant &variant,
const wxDataViewItem &item,
unsigned int col) const override;
virtual bool SetValue( const wxVariant &variant,
const wxDataViewItem &item,
virtual bool SetValue( const wxVariant &variant,
const wxDataViewItem &item,
unsigned int col) override;
bool SetValue( const wxVariant &variant,
const int item_idx,
bool SetValue( const wxVariant &variant,
const int item_idx,
unsigned int col);
// For parent move child from cur_volume_id place to new_volume_id
// For parent move child from cur_volume_id place to new_volume_id
// Remaining items will moved up/down accordingly
wxDataViewItem ReorganizeChildren( const int cur_volume_id,
wxDataViewItem ReorganizeChildren( const int cur_volume_id,
const int new_volume_id,
const wxDataViewItem &parent);
virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
// get object item
wxDataViewItem GetTopParent(const wxDataViewItem &item) const;
virtual bool IsContainer(const wxDataViewItem &item) const override;
virtual unsigned int GetChildren(const wxDataViewItem &parent,
wxDataViewItemArray &array) const override;
virtual bool IsContainer(const wxDataViewItem &item) const override;
virtual unsigned int GetChildren(const wxDataViewItem &parent,
wxDataViewItemArray &array) const override;
void GetAllChildren(const wxDataViewItem &parent,wxDataViewItemArray &array) const;
// 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; }
// 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; }
ItemType GetItemType(const wxDataViewItem &item) const ;
wxDataViewItem GetItemByType( const wxDataViewItem &parent_item,
wxDataViewItem GetItemByType( const wxDataViewItem &parent_item,
ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item,
void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories);
bool IsPrintable(const wxDataViewItem &item) const;
void UpdateObjectPrintable(wxDataViewItem parent_item);
void UpdateInstancesPrintable(wxDataViewItem parent_item);
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; }
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
int subobj_idx = -1,
ItemType subobj_type = itInstance);
wxDataViewItem SetObjectPrintableState(PrintIndicator printable, wxDataViewItem obj_item);
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
// Rescale bitmaps for existing Items
void Rescale();
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
private:
wxDataViewItem AddRoot(const wxDataViewItem& parent_item, const ItemType root_type);
wxDataViewItem AddInstanceRoot(const wxDataViewItem& parent_item);
};
// ----------------------------------------------------------------------------
@ -506,12 +548,12 @@ public:
return false;
#else
return true;
#endif
#endif
}
wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
wxWindow* CreateEditorCtrl(wxWindow* parent,
wxRect labelRect,
const wxVariant& value) override;
bool GetValueFromEditorCtrl( wxWindow* ctrl,
bool GetValueFromEditorCtrl( wxWindow* ctrl,
wxVariant& value) override;
bool WasCanceled() const { return m_was_unusable_symbol; }
@ -528,88 +570,88 @@ private:
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)
{ }
// 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);
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);
rect.Deflate(2);
dc->DrawRoundedRectangle(rect, 5);
RenderText(m_value,
0, // no offset
wxRect(dc->GetTextExtent(m_value)).CentreIn(rect),
dc,
state);
return true;
}
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";
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;
}
return false;
}
virtual wxSize GetSize() const override/*wxOVERRIDE*/
{
return wxSize(60, 20);
}
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 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 GetValue(wxVariant &WXUNUSED(value)) const override/*wxOVERRIDE*/{ return true; }
virtual bool HasEditorCtrl() 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();
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;
}
return text;
}
virtual bool
GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/
{
wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
if (!text)
return false;
virtual bool
GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value) override/*wxOVERRIDE*/
{
wxTextCtrl* text = wxDynamicCast(ctrl, wxTextCtrl);
if (!text)
return false;
value = text->GetValue();
value = text->GetValue();
return true;
}
return true;
}
private:
wxString m_value;
wxString m_value;
};
@ -621,7 +663,7 @@ class ScalableBitmap
{
public:
ScalableBitmap() {};
ScalableBitmap( wxWindow *parent,
ScalableBitmap( wxWindow *parent,
const std::string& icon_name = "",
const int px_cnt = 16,
const bool is_horizontal = false);
@ -667,9 +709,9 @@ public:
DoubleSlider(
wxWindow *parent,
wxWindowID id,
int lowerValue,
int higherValue,
int minValue,
int lowerValue,
int higherValue,
int minValue,
int maxValue,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
@ -712,8 +754,8 @@ public:
EnableTickManipulation(false);
}
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
bool is_lower_at_min() const { return m_lower_value == m_min_value; }
bool is_higher_at_max() const { return m_higher_value == m_max_value; }
bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); }
@ -727,11 +769,12 @@ public:
void OnWheel(wxMouseEvent& event);
void OnKeyDown(wxKeyEvent &event);
void OnKeyUp(wxKeyEvent &event);
void OnChar(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);
@ -741,6 +784,7 @@ protected:
void draw_ticks(wxDC& dc);
void draw_colored_band(wxDC& dc);
void draw_one_layer_icon(wxDC& dc);
void draw_revert_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;
@ -782,6 +826,7 @@ private:
ScalableBitmap m_bmp_one_layer_lock_off;
ScalableBitmap m_bmp_one_layer_unlock_on;
ScalableBitmap m_bmp_one_layer_unlock_off;
ScalableBitmap m_bmp_revert;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
@ -795,9 +840,11 @@ private:
wxRect m_rect_higher_thumb;
wxRect m_rect_tick_action;
wxRect m_rect_one_layer_icon;
wxRect m_rect_revert_icon;
wxSize m_thumb_size;
int m_tick_icon_dim;
int m_lock_icon_dim;
int m_revert_icon_dim;
long m_style;
float m_label_koef = 1.0;
@ -834,24 +881,27 @@ public:
~LockButton() {}
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; }
bool IsLocked() const { return m_is_pushed; }
void SetLock(bool lock);
// create its own Enable/Disable functions to not really disabled button because of tooltip enabling
void enable() { m_disabled = false; }
void disable() { m_disabled = true; }
void msw_rescale();
protected:
void enter_button(const bool enter);
void update_button_bitmaps();
private:
bool m_is_pushed = false;
bool m_disabled = false;
ScalableBitmap m_bmp_lock_on;
ScalableBitmap m_bmp_lock_off;
ScalableBitmap m_bmp_unlock_on;
ScalableBitmap m_bmp_unlock_off;
ScalableBitmap m_bmp_lock_closed;
ScalableBitmap m_bmp_lock_closed_f;
ScalableBitmap m_bmp_lock_open;
ScalableBitmap m_bmp_lock_open_f;
};
@ -882,12 +932,16 @@ public:
~ScalableButton() {}
void SetBitmap_(const ScalableBitmap& bmp);
void SetBitmapDisabled_(const ScalableBitmap &bmp);
void msw_rescale();
private:
wxWindow* m_parent;
std::string m_current_icon_name = "";
std::string m_disabled_icon_name = "";
int m_width {-1}; // should be multiplied to em_unit
int m_height{-1}; // should be multiplied to em_unit
};
@ -932,7 +986,7 @@ private:
class ModeSizer : public wxFlexGridSizer
{
public:
ModeSizer( wxWindow *parent, int hgap = 10);
ModeSizer( wxWindow *parent, int hgap = 0);
~ModeSizer() {}
void SetMode(const /*ConfigOptionMode*/int mode);