Merge branch 'master' into fs_QuadricEdgeCollapse

This commit is contained in:
Filip Sykala 2021-08-02 09:04:15 +02:00
commit 59d02aea0a
160 changed files with 5982 additions and 5094 deletions

View file

@ -203,6 +203,8 @@ set(SLIC3R_GUI_SOURCES
GUI/ProjectDirtyStateManager.cpp
GUI/DesktopIntegrationDialog.cpp
GUI/DesktopIntegrationDialog.hpp
GUI/HintNotification.cpp
GUI/HintNotification.hpp
Utils/Http.cpp
Utils/Http.hpp
Utils/FixModelByWin10.cpp

View file

@ -20,6 +20,8 @@
#include <boost/log/trivial.hpp>
static const float GROUND_Z = -0.02f;
static const std::array<float, 4> DEFAULT_MODEL_COLOR = { 0.235f, 0.235f, 0.235f, 1.0f };
static const std::array<float, 4> PICKING_MODEL_COLOR = { 0.0f, 0.0f, 0.0f, 1.0f };
namespace Slic3r {
namespace GUI {
@ -211,8 +213,18 @@ Point Bed3D::point_projection(const Point& point) const
return m_polygon.point_projection(point);
}
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture) const
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes, bool show_texture)
{
render_internal(canvas, bottom, scale_factor, show_axes, show_texture, false);
}
void Bed3D::render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor)
{
render_internal(canvas, bottom, scale_factor, false, false, true);
}
void Bed3D::render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking)
{
float* factor = const_cast<float*>(&m_scale_factor);
*factor = scale_factor;
@ -222,11 +234,13 @@ void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor,
glsafe(::glEnable(GL_DEPTH_TEST));
m_model.set_color(-1, picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
switch (m_type)
{
case System: { render_system(canvas, bottom, show_texture); break; }
default:
case Custom: { render_custom(canvas, bottom, show_texture); break; }
case Custom: { render_custom(canvas, bottom, show_texture, picking); break; }
}
glsafe(::glDisable(GL_DEPTH_TEST));
@ -237,7 +251,7 @@ void Bed3D::calc_bounding_boxes() const
BoundingBoxf3* bounding_box = const_cast<BoundingBoxf3*>(&m_bounding_box);
*bounding_box = BoundingBoxf3();
for (const Vec2d& p : m_shape) {
bounding_box->merge({ p(0), p(1), 0.0 });
bounding_box->merge({ p.x(), p.y(), 0.0 });
}
BoundingBoxf3* extended_bounding_box = const_cast<BoundingBoxf3*>(&m_extended_bounding_box);
@ -264,16 +278,16 @@ void Bed3D::calc_triangles(const ExPolygon& poly)
void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
{
Polylines axes_lines;
for (coord_t x = bed_bbox.min(0); x <= bed_bbox.max(0); x += scale_(10.0)) {
for (coord_t x = bed_bbox.min.x(); x <= bed_bbox.max.x(); x += scale_(10.0)) {
Polyline line;
line.append(Point(x, bed_bbox.min(1)));
line.append(Point(x, bed_bbox.max(1)));
line.append(Point(x, bed_bbox.min.y()));
line.append(Point(x, bed_bbox.max.y()));
axes_lines.push_back(line);
}
for (coord_t y = bed_bbox.min(1); y <= bed_bbox.max(1); y += scale_(10.0)) {
for (coord_t y = bed_bbox.min.y(); y <= bed_bbox.max.y(); y += scale_(10.0)) {
Polyline line;
line.append(Point(bed_bbox.min(0), y));
line.append(Point(bed_bbox.max(0), y));
line.append(Point(bed_bbox.min.x(), y));
line.append(Point(bed_bbox.max.x(), y));
axes_lines.push_back(line);
}
@ -333,7 +347,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
if (m_texture_filename.empty()) {
texture->reset();
render_default(bottom);
render_default(bottom, false);
return;
}
@ -346,7 +360,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!temp_texture->load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
render_default(bottom);
render_default(bottom, false);
return;
}
canvas.request_extra_frame();
@ -354,7 +368,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
// starts generating the main texture, compression will run asynchronously
if (!texture->load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
render_default(bottom);
render_default(bottom, false);
return;
}
}
@ -362,7 +376,7 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (temp_texture->get_id() == 0 || temp_texture->get_source() != m_texture_filename) {
if (!temp_texture->load_from_file(m_texture_filename, false, GLTexture::None, false)) {
render_default(bottom);
render_default(bottom, false);
return;
}
canvas.request_extra_frame();
@ -370,12 +384,12 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
// starts generating the main texture, compression will run asynchronously
if (!texture->load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
render_default(bottom);
render_default(bottom, false);
return;
}
}
else {
render_default(bottom);
render_default(bottom, false);
return;
}
}
@ -388,7 +402,6 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
temp_texture->reset();
canvas.request_extra_frame();
}
if (m_triangles.get_vertices_count() > 0) {
@ -470,7 +483,7 @@ void Bed3D::render_model() const
GLModel* model = const_cast<GLModel*>(&m_model);
if (model->get_filename() != m_model_filename && model->init_from_file(m_model_filename)) {
model->set_color(-1, m_model_color);
model->set_color(-1, DEFAULT_MODEL_COLOR);
// 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();
@ -495,10 +508,10 @@ void Bed3D::render_model() const
}
}
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking) const
{
if (m_texture_filename.empty() && m_model_filename.empty()) {
render_default(bottom);
render_default(bottom, picking);
return;
}
@ -509,7 +522,7 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) co
render_texture(bottom, canvas);
}
void Bed3D::render_default(bool bottom) const
void Bed3D::render_default(bool bottom, bool picking) const
{
const_cast<GLTexture*>(&m_texture)->reset();
@ -526,21 +539,23 @@ void Bed3D::render_default(bool bottom) const
if (!has_model && !bottom) {
// draw background
glsafe(::glDepthMask(GL_FALSE));
glsafe(::glColor4fv(m_model_color.data()));
glsafe(::glColor4fv(picking ? PICKING_MODEL_COLOR.data() : DEFAULT_MODEL_COLOR.data()));
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(1.5f * m_scale_factor));
if (has_model && !bottom)
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f));
else
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f));
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()));
if (!picking) {
// draw grid
glsafe(::glLineWidth(1.5f * m_scale_factor));
if (has_model && !bottom)
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 1.0f));
else
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.6f));
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));

View file

@ -84,7 +84,6 @@ private:
GLTexture m_temp_texture;
GLModel m_model;
Vec3d m_model_offset{ Vec3d::Zero() };
std::array<float, 4> m_model_color{ 0.235f, 0.235f, 0.235f, 1.0f };
unsigned int m_vbo_id{ 0 };
Axes m_axes;
@ -110,19 +109,23 @@ public:
Point point_projection(const Point& point) const;
void render(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture) const;
bool show_axes, bool show_texture);
void render_for_picking(GLCanvas3D& canvas, bool bottom, float scale_factor);
private:
void calc_bounding_boxes() const;
void calc_triangles(const ExPolygon& poly);
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
std::tuple<EType, std::string, std::string> detect_type(const Pointfs& shape) const;
void render_internal(GLCanvas3D& canvas, bool bottom, float scale_factor,
bool show_axes, bool show_texture, bool picking);
void render_axes() const;
void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) const;
void render_texture(bool bottom, GLCanvas3D& canvas) const;
void render_model() const;
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture) const;
void render_default(bool bottom) const;
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking) const;
void render_default(bool bottom, bool picking) const;
void reset();
};

View file

@ -9,9 +9,9 @@
#include "3DScene.hpp"
#include "GLShader.hpp"
#include "GUI_App.hpp"
#if ENABLE_ENVIRONMENT_MAP
#if ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
#include "Plater.hpp"
#endif // ENABLE_ENVIRONMENT_MAP
#endif // ENABLE_ENVIRONMENT_MAP || ENABLE_SINKING_CONTOURS
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
@ -23,9 +23,11 @@
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/AppConfig.hpp"
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
#include "libslic3r/PresetBundle.hpp"
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
#include "libslic3r/ClipperUtils.hpp"
#if ENABLE_SINKING_CONTOURS
#include "libslic3r/Tesselate.hpp"
#endif // ENABLE_SINKING_CONTOURS
#include <stdio.h>
#include <stdlib.h>
@ -286,6 +288,74 @@ void GLIndexedVertexArray::render(
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
#if ENABLE_SINKING_CONTOURS
const float GLVolume::SinkingContours::HalfWidth = 0.25f;
void GLVolume::SinkingContours::render()
{
update();
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_shift.x(), m_shift.y(), m_shift.z()));
m_model.render();
glsafe(::glPopMatrix());
}
void GLVolume::SinkingContours::update()
{
if (m_parent.is_sinking() && !m_parent.is_below_printbed()) {
const BoundingBoxf3& box = m_parent.transformed_convex_hull_bounding_box();
if (!m_old_box.size().isApprox(box.size()) || m_old_box.min.z() != box.min.z()) {
m_old_box = box;
m_shift = Vec3d::Zero();
const TriangleMesh& mesh = GUI::wxGetApp().plater()->model().objects[m_parent.object_idx()]->volumes[m_parent.volume_idx()]->mesh();
assert(mesh.has_shared_vertices());
m_model.reset();
GUI::GLModel::InitializationData init_data;
MeshSlicingParams slicing_params;
slicing_params.trafo = m_parent.world_matrix();
Polygons polygons = union_(slice_mesh(mesh.its, 0.0f, slicing_params));
for (Polygon& polygon : polygons) {
if (polygon.is_clockwise())
polygon.reverse();
Polygons outer_polys = offset(polygon, float(scale_(HalfWidth)));
assert(outer_polys.size() == 1);
if (outer_polys.empty())
// no outer contour, skip
continue;
ExPolygon expoly(std::move(outer_polys.front()));
expoly.holes = offset(polygon, -float(scale_(HalfWidth)));
polygons_reverse(expoly.holes);
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::Triangles;
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(expoly);
for (const Vec3d& v : triangulation) {
entity.positions.emplace_back(v.cast<float>() + Vec3f(0.0f, 0.0f, 0.015f)); // add a small positive z to avoid z-fighting
entity.normals.emplace_back(Vec3f::UnitZ());
const size_t positions_count = entity.positions.size();
if (positions_count % 3 == 0) {
entity.indices.emplace_back(positions_count - 3);
entity.indices.emplace_back(positions_count - 2);
entity.indices.emplace_back(positions_count - 1);
}
}
init_data.entities.emplace_back(entity);
}
m_model.init_from(init_data);
}
else
m_shift = box.center() - m_old_box.center();
}
else
m_model.reset();
}
#endif // ENABLE_SINKING_CONTOURS
const std::array<float, 4> GLVolume::SELECTED_COLOR = { 0.0f, 1.0f, 0.0f, 1.0f };
const std::array<float, 4> GLVolume::HOVER_SELECT_COLOR = { 0.4f, 0.9f, 0.1f, 1.0f };
const std::array<float, 4> GLVolume::HOVER_DESELECT_COLOR = { 1.0f, 0.75f, 0.75f, 1.0f };
@ -306,6 +376,9 @@ GLVolume::GLVolume(float r, float g, float b, float a)
: m_transformed_bounding_box_dirty(true)
, m_sla_shift_z(0.0)
, m_transformed_convex_hull_bounding_box_dirty(true)
#if ENABLE_SINKING_CONTOURS
, m_sinking_contours(*this)
#endif // ENABLE_SINKING_CONTOURS
// geometry_id == 0 -> invalid
, geometry_id(std::pair<size_t, size_t>(0, 0))
, extruder_id(0)
@ -323,6 +396,9 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, force_transparent(false)
, force_native_color(false)
, force_neutral_color(false)
#if ENABLE_SINKING_CONTOURS
, force_sinking_contours(false)
#endif // ENABLE_SINKING_CONTOURS
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
@ -342,16 +418,10 @@ void GLVolume::set_render_color(const std::array<float, 4>& rgba)
void GLVolume::set_render_color()
{
#if ENABLE_ALLOW_NEGATIVE_Z
bool outside = is_outside || is_below_printbed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
if (force_native_color || force_neutral_color) {
#if ENABLE_ALLOW_NEGATIVE_Z
if (outside && shader_outside_printer_detection_enabled)
#else
if (is_outside && shader_outside_printer_detection_enabled)
#endif // ENABLE_ALLOW_NEGATIVE_Z
set_render_color(OUTSIDE_COLOR);
else {
if (force_native_color)
@ -366,18 +436,10 @@ void GLVolume::set_render_color()
else if (hover == HS_Deselect)
set_render_color(HOVER_DESELECT_COLOR);
else if (selected)
#if ENABLE_ALLOW_NEGATIVE_Z
set_render_color(outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR);
#else
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR);
#endif // ENABLE_ALLOW_NEGATIVE_Z
else if (disabled)
set_render_color(DISABLED_COLOR);
#if ENABLE_ALLOW_NEGATIVE_Z
else if (outside && shader_outside_printer_detection_enabled)
#else
else if (is_outside && shader_outside_printer_detection_enabled)
#endif // ENABLE_ALLOW_NEGATIVE_Z
set_render_color(OUTSIDE_COLOR);
else
set_render_color(color);
@ -520,14 +582,9 @@ void GLVolume::render() const
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
bool GLVolume::is_sla_pad() const { return this->composite_id.volume_id == -int(slaposPad); }
#if ENABLE_ALLOW_NEGATIVE_Z
bool GLVolume::is_sinking() const
{
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
if (is_modifier || GUI::wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
#else
if (is_modifier)
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
return false;
const BoundingBoxf3& box = transformed_convex_hull_bounding_box();
return box.min.z() < SINKING_Z_THRESHOLD && box.max.z() >= SINKING_Z_THRESHOLD;
@ -537,7 +594,13 @@ bool GLVolume::is_below_printbed() const
{
return transformed_convex_hull_bounding_box().max(2) < 0.0;
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
#if ENABLE_SINKING_CONTOURS
void GLVolume::render_sinking_contours()
{
m_sinking_contours.render();
}
#endif // ENABLE_SINKING_CONTOURS
std::vector<int> GLVolumeCollection::load_object(
const ModelObject *model_object,
@ -774,6 +837,68 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
if (disable_cullface)
glsafe(::glDisable(GL_CULL_FACE));
#if ENABLE_SINKING_CONTOURS
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color();
// render sinking contours of non-hovered volumes
if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
volume.first->hover == GLVolume::HS_None && !volume.first->force_sinking_contours) {
shader->stop_using();
volume.first->render_sinking_contours();
shader->start_using();
}
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
shader->set_uniform("uniform_color", volume.first->render_color);
shader->set_uniform("z_range", m_z_range, 2);
shader->set_uniform("clipping_plane", m_clipping_plane, 4);
shader->set_uniform("print_box.min", m_print_box_min, 3);
shader->set_uniform("print_box.max", m_print_box_max, 3);
shader->set_uniform("print_box.actived", volume.first->shader_outside_printer_detection_enabled);
shader->set_uniform("print_box.volume_world_matrix", volume.first->world_matrix());
shader->set_uniform("slope.actived", m_slope.active && !volume.first->is_modifier && !volume.first->is_wipe_tower);
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
shader->set_uniform("slope.normal_z", m_slope.normal_z);
#if ENABLE_ENVIRONMENT_MAP
unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();
bool use_environment_texture = environment_texture_id > 0 && GUI::wxGetApp().app_config->get("use_environment_map") == "1";
shader->set_uniform("use_environment_tex", use_environment_texture);
if (use_environment_texture)
glsafe(::glBindTexture(GL_TEXTURE_2D, environment_texture_id));
#endif // ENABLE_ENVIRONMENT_MAP
glcheck();
volume.first->render();
#if ENABLE_ENVIRONMENT_MAP
if (use_environment_texture)
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
#endif // ENABLE_ENVIRONMENT_MAP
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
}
for (GLVolumeWithIdAndZ& volume : to_render) {
// render sinking contours of hovered/displaced volumes
if (volume.first->is_sinking() && !volume.first->is_below_printbed() &&
(volume.first->hover != GLVolume::HS_None || volume.first->force_sinking_contours)) {
shader->stop_using();
glsafe(::glDepthFunc(GL_ALWAYS));
volume.first->render_sinking_contours();
glsafe(::glDepthFunc(GL_LESS));
shader->start_using();
}
}
#else
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
@ -813,6 +938,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
#endif // ENABLE_SINKING_CONTOURS
if (disable_cullface)
glsafe(::glEnable(GL_CULL_FACE));
@ -879,8 +1005,8 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, b
if (opt == nullptr)
return false;
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
const BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min.x()), unscale<double>(bed_box_2D.min.y()), 0.0), Vec3d(unscale<double>(bed_box_2D.max.x()), unscale<double>(bed_box_2D.max.y()), config->opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
print_volume.min(2) = -1e10;
print_volume.min(0) -= BedEpsilon;

View file

@ -8,6 +8,10 @@
#include "libslic3r/Utils.hpp"
#include "libslic3r/Geometry.hpp"
#if ENABLE_SINKING_CONTOURS
#include "GLModel.hpp"
#endif // ENABLE_SINKING_CONTOURS
#include <functional>
#define HAS_GLSAFE
@ -250,6 +254,9 @@ public:
enum EHoverState : unsigned char
{
HS_None,
#if ENABLE_SINKING_CONTOURS
HS_Hover,
#endif // ENABLE_SINKING_CONTOURS
HS_Select,
HS_Deselect
};
@ -262,7 +269,7 @@ private:
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.
BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the transformed bounding box.
@ -274,6 +281,26 @@ private:
// Whether or not is needed to recalculate the transformed convex hull bounding box.
bool m_transformed_convex_hull_bounding_box_dirty;
#if ENABLE_SINKING_CONTOURS
class SinkingContours
{
static const float HalfWidth;
GLVolume& m_parent;
GUI::GLModel m_model;
BoundingBoxf3 m_old_box;
Vec3d m_shift{ Vec3d::Zero() };
public:
SinkingContours(GLVolume& volume) : m_parent(volume) {}
void render();
private:
void update();
};
SinkingContours m_sinking_contours;
#endif // ENABLE_SINKING_CONTOURS
public:
// Color of the triangles / quads held by this volume.
std::array<float, 4> color;
@ -334,7 +361,11 @@ public:
bool force_native_color : 1;
// Whether or not render this volume in neutral
bool force_neutral_color : 1;
};
#if ENABLE_SINKING_CONTOURS
// Whether or not to force rendering of sinking contours
bool force_sinking_contours : 1;
#endif // ENABLE_SINKING_CONTOURS
};
// Is mouse or rectangle selection over this object to select/deselect it ?
EHoverState hover;
@ -459,10 +490,11 @@ public:
bool is_sla_support() const;
bool is_sla_pad() const;
#if ENABLE_ALLOW_NEGATIVE_Z
bool is_sinking() const;
bool is_below_printbed() const;
#endif // ENABLE_ALLOW_NEGATIVE_Z
#if ENABLE_SINKING_CONTOURS
void render_sinking_contours();
#endif // ENABLE_SINKING_CONTOURS
// Return an estimate of the memory consumed by this class.
size_t cpu_memory_used() const {

View file

@ -155,19 +155,15 @@ void BackgroundSlicingProcess::process_fff()
if (! m_export_path.empty()) {
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_export_began_id));
#if ENABLE_GCODE_WINDOW
// let the gcode window to unmap the temporary .gcode file (m_temp_output_path)
// because the scripts may want to modify it
GUI::wxGetApp().plater()->stop_mapping_gcode_window();
#endif // ENABLE_GCODE_WINDOW
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(m_temp_output_path, m_fff_print->full_print_config());
#if ENABLE_GCODE_WINDOW
// let the gcode window to reload and remap the temporary .gcode file (m_temp_output_path)
GUI::wxGetApp().plater()->start_mapping_gcode_window();
#endif // ENABLE_GCODE_WINDOW
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.

View file

@ -40,6 +40,7 @@
#include "slic3r/Utils/PresetUpdater.hpp"
#include "format.hpp"
#include "MsgDialog.hpp"
#include "libslic3r/libslic3r.h"
#if defined(__linux__) && defined(__WXGTK3__)
#define wxLinux_gtk3 true
@ -65,6 +66,7 @@ bool Bundle::load(fs::path source_path, bool ais_in_resources, bool ais_prusa_bu
std::string path_string = source_path.string();
auto [config_substitutions, presets_loaded] = preset_bundle->load_configbundle(path_string, PresetBundle::LoadConfigBundleAttribute::LoadSystem);
UNUSED(config_substitutions);
// No substitutions shall be reported when loading a system config bundle, no substitutions are allowed.
assert(config_substitutions.empty());
auto first_vendor = preset_bundle->vendors.begin();
@ -1604,25 +1606,17 @@ ConfigWizardIndex::ConfigWizardIndex(wxWindow *parent)
, item_hover(NO_ITEM)
, last_page((size_t)-1)
{
#ifndef __WXOSX__
SetDoubleBuffered(true);// SetDoubleBuffered exists on Win and Linux/GTK, but is missing on OSX
#endif //__WXOSX__
SetMinSize(bg.bmp().GetSize());
const wxSize size = GetTextExtent("m");
em_w = size.x;
em_h = size.y;
// Add logo bitmap.
// This could be done in on_paint() along with the index labels, but I've found it tricky
// to get the bitmap rendered well on all platforms with transparent background.
// In some cases it didn't work at all. And so wxStaticBitmap is used here instead,
// because it has all the platform quirks figured out.
auto *sizer = new wxBoxSizer(wxVERTICAL);
logo = new wxStaticBitmap(this, wxID_ANY, bg.bmp());
sizer->AddStretchSpacer();
sizer->Add(logo);
SetSizer(sizer);
logo_height = logo->GetBitmap().GetHeight();
Bind(wxEVT_PAINT, &ConfigWizardIndex::on_paint, this);
Bind(wxEVT_SIZE, [this](wxEvent& e) { e.Skip(); Refresh(); });
Bind(wxEVT_MOTION, &ConfigWizardIndex::on_mouse_move, this);
Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent &evt) {
@ -1767,6 +1761,12 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
y += yinc;
index_width = std::max(index_width, (int)x + text_size.x);
}
//draw logo
if (int y = size.y - bg.GetBmpHeight(); y>=0) {
dc.DrawBitmap(bg.bmp(), 0, y, false);
index_width = std::max(index_width, bg.GetBmpWidth() + em_w / 2);
}
if (GetMinSize().x < index_width) {
CallAfter([this, index_width]() {
@ -1774,11 +1774,6 @@ void ConfigWizardIndex::on_paint(wxPaintEvent & evt)
Refresh();
});
}
if ((int)y + logo_height > size.GetHeight())
logo->Hide();
else
logo->Show();
}
void ConfigWizardIndex::on_mouse_move(wxMouseEvent &evt)
@ -1804,7 +1799,6 @@ void ConfigWizardIndex::msw_rescale()
bg.msw_rescale();
SetMinSize(bg.bmp().GetSize());
logo->SetBitmap(bg.bmp());
bullet_black.msw_rescale();
bullet_blue.msw_rescale();

View file

@ -512,15 +512,12 @@ private:
ScalableBitmap bullet_black;
ScalableBitmap bullet_blue;
ScalableBitmap bullet_white;
wxStaticBitmap* logo;
std::vector<Item> items;
size_t item_active;
ssize_t item_hover;
size_t last_page;
int logo_height;
int item_height() const { return std::max(bullet_black.bmp().GetSize().GetHeight(), em_w) + em_w; }
void on_paint(wxPaintEvent &evt);

View file

@ -739,16 +739,8 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer
return size_t(it - m_layers_values.begin());
};
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (m_draw_mode == dmSequentialGCodeView) {
return (Slic3r::GUI::get_app_config()->get("seq_top_gcode_indices") == "1") ?
wxString::Format("%lu", static_cast<unsigned long>(m_alternate_values[value])) :
wxString::Format("%lu", static_cast<unsigned long>(m_values[value]));
}
#else
if (m_draw_mode == dmSequentialGCodeView)
return wxString::Format("%lu", static_cast<unsigned long>(m_values[value]));
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
return wxString::Format("%lu", static_cast<unsigned long>(m_alternate_values[value]));
else {
if (label_type == ltEstimatedTime) {
if (m_is_wipe_tower) {
@ -1556,6 +1548,9 @@ void Control::OnMotion(wxMouseEvent& event)
event.Skip();
// Set tooltips with information for each icon
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (GUI::wxGetApp().is_editor())
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
this->SetToolTip(get_tooltip(tick));
if (action) {
@ -2062,6 +2057,10 @@ void Control::auto_color_change()
break;
if (prev_area - cur_area > delta_area) {
// Check percent of the area decrease.
// Ignore it, if this value is less than 10%
if (cur_area / prev_area > 0.9)
continue;
int tick = get_tick_from_value(layer->print_z);
if (tick >= 0 && !m_ticks.has_tick(tick)) {
if (m_mode == SingleExtruder) {
@ -2180,7 +2179,6 @@ static std::string get_custom_code(const std::string& code_in, double height)
wxTextEntryDialogStyle | wxTE_MULTILINE);
upgrade_text_entry_dialog(&dlg);
#if ENABLE_VALIDATE_CUSTOM_GCODE
bool valid = true;
std::string value;
do {
@ -2191,12 +2189,6 @@ static std::string get_custom_code(const std::string& code_in, double height)
valid = GUI::Tab::validate_custom_gcode("Custom G-code", value);
} while (!valid);
return value;
#else
if (dlg.ShowModal() != wxID_OK)
return "";
return into_u8(dlg.GetValue());
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
}
static std::string get_pause_print_msg(const std::string& msg_in, double height)

View file

@ -223,9 +223,7 @@ public:
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
void SetSliderValues(const std::vector<double>& values);
void ChangeOneLayerLock();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
void SetSliderAlternateValues(const std::vector<double>& values) { m_alternate_values = values; }
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
Info GetTicksValues() const;
void SetTicksValues(const Info &custom_gcode_per_print_z);
@ -409,9 +407,7 @@ private:
std::vector<std::string> m_extruder_colors;
std::string m_print_obj_idxs;
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
std::vector<double> m_alternate_values;
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
// control's view variables
wxCoord SLIDER_MARGIN; // margin around slider

View file

@ -23,9 +23,7 @@
#include <GL/glew.h>
#include <boost/log/trivial.hpp>
#if ENABLE_GCODE_WINDOW
#include <boost/algorithm/string/split.hpp>
#endif // ENABLE_GCODE_WINDOW
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/fstream.hpp>
#include <wx/progdlg.h>
@ -123,9 +121,7 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
case EMoveType::Custom_GCode:
case EMoveType::Retract:
case EMoveType::Unretract:
#if ENABLE_SEAMS_VISUALIZATION
case EMoveType::Seam:
#endif // ENABLE_SEAMS_VISUALIZATION
case EMoveType::Extrude: {
// use rounding to reduce the number of generated paths
return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
@ -269,7 +265,6 @@ void GCodeViewer::SequentialView::Marker::render() const
ImGui::PopStyleVar();
}
#if ENABLE_GCODE_WINDOW
void GCodeViewer::SequentialView::GCodeWindow::load_gcode()
{
if (m_filename.empty())
@ -487,7 +482,6 @@ void GCodeViewer::SequentialView::render(float legend_height) const
bottom -= wxGetApp().plater()->get_view_toolbar().get_height();
gcode_window.render(legend_height, bottom, static_cast<uint64_t>(gcode_ids[current.last]));
}
#endif // ENABLE_GCODE_WINDOW
const std::vector<GCodeViewer::Color> GCodeViewer::Extrusion_Role_Colors {{
{ 0.75f, 0.75f, 0.75f }, // erNone
@ -511,9 +505,7 @@ const std::vector<GCodeViewer::Color> GCodeViewer::Extrusion_Role_Colors {{
const std::vector<GCodeViewer::Color> GCodeViewer::Options_Colors {{
{ 0.803f, 0.135f, 0.839f }, // Retractions
{ 0.287f, 0.679f, 0.810f }, // Unretractions
#if ENABLE_SEAMS_VISUALIZATION
{ 0.900f, 0.900f, 0.900f }, // Seams
#endif // ENABLE_SEAMS_VISUALIZATION
{ 0.758f, 0.744f, 0.389f }, // ToolChanges
{ 0.856f, 0.582f, 0.546f }, // ColorChanges
{ 0.322f, 0.942f, 0.512f }, // PausePrints
@ -556,20 +548,12 @@ GCodeViewer::GCodeViewer()
case EMoveType::Pause_Print:
case EMoveType::Custom_GCode:
case EMoveType::Retract:
#if ENABLE_SEAMS_VISUALIZATION
case EMoveType::Unretract:
case EMoveType::Seam: {
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
buffer.vertices.format = VBuffer::EFormat::Position;
break;
}
#else
case EMoveType::Unretract: {
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
buffer.vertices.format = VBuffer::EFormat::Position;
break;
}
#endif // ENABLE_SEAMS_VISUALIZATION
case EMoveType::Wipe:
case EMoveType::Extrude: {
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Triangle;
@ -599,10 +583,13 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
// release gpu memory, if used
reset();
#if ENABLE_GCODE_WINDOW
m_sequential_view.gcode_window.set_filename(gcode_result.filename);
m_sequential_view.gcode_window.load_gcode();
#endif // ENABLE_GCODE_WINDOW
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (wxGetApp().is_gcode_viewer())
m_custom_gcode_per_print_z = gcode_result.custom_gcode_per_print_z;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
load_toolpaths(gcode_result);
@ -761,9 +748,10 @@ void GCodeViewer::reset()
m_layers_z_range = { 0, 0 };
m_roles = std::vector<ExtrusionRole>();
m_print_statistics.reset();
#if ENABLE_GCODE_WINDOW
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_sequential_view.gcode_window.reset();
#endif // ENABLE_GCODE_WINDOW
#if ENABLE_GCODE_VIEWER_STATISTICS
m_statistics.reset_all();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -783,18 +771,11 @@ void GCodeViewer::render() const
case EMoveType::Pause_Print:
case EMoveType::Custom_GCode:
case EMoveType::Retract:
#if ENABLE_SEAMS_VISUALIZATION
case EMoveType::Unretract:
case EMoveType::Seam: {
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
break;
}
#else
case EMoveType::Unretract: {
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
break;
}
#endif // ENABLE_SEAMS_VISUALIZATION
case EMoveType::Wipe:
case EMoveType::Extrude: {
buffer.shader = "gouraud_light";
@ -832,20 +813,12 @@ void GCodeViewer::render() const
glsafe(::glEnable(GL_DEPTH_TEST));
render_toolpaths();
render_shells();
#if ENABLE_GCODE_WINDOW
float legend_height = 0.0f;
render_legend(legend_height);
#else
render_legend();
#endif // ENABLE_GCODE_WINDOW
SequentialView* sequential_view = const_cast<SequentialView*>(&m_sequential_view);
if (sequential_view->current.last != sequential_view->endpoints.last) {
sequential_view->marker.set_world_position(sequential_view->current_position);
#if ENABLE_GCODE_WINDOW
sequential_view->render(legend_height);
#else
sequential_view->marker.render();
#endif // ENABLE_GCODE_WINDOW
}
#if ENABLE_GCODE_VIEWER_STATISTICS
render_statistics();
@ -927,9 +900,7 @@ unsigned int GCodeViewer::get_options_visibility_flags() const
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Wipe), is_toolpath_move_type_visible(EMoveType::Wipe));
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Retractions), is_toolpath_move_type_visible(EMoveType::Retract));
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Unretractions), is_toolpath_move_type_visible(EMoveType::Unretract));
#if ENABLE_SEAMS_VISUALIZATION
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::Seams), is_toolpath_move_type_visible(EMoveType::Seam));
#endif // ENABLE_SEAMS_VISUALIZATION
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::ToolChanges), is_toolpath_move_type_visible(EMoveType::Tool_change));
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::ColorChanges), is_toolpath_move_type_visible(EMoveType::Color_change));
flags = set_flag(flags, static_cast<unsigned int>(Preview::OptionType::PausePrints), is_toolpath_move_type_visible(EMoveType::Pause_Print));
@ -950,9 +921,7 @@ void GCodeViewer::set_options_visibility_from_flags(unsigned int flags)
set_toolpath_move_type_visible(EMoveType::Wipe, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Wipe)));
set_toolpath_move_type_visible(EMoveType::Retract, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Retractions)));
set_toolpath_move_type_visible(EMoveType::Unretract, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Unretractions)));
#if ENABLE_SEAMS_VISUALIZATION
set_toolpath_move_type_visible(EMoveType::Seam, is_flag_set(static_cast<unsigned int>(Preview::OptionType::Seams)));
#endif // ENABLE_SEAMS_VISUALIZATION
set_toolpath_move_type_visible(EMoveType::Tool_change, is_flag_set(static_cast<unsigned int>(Preview::OptionType::ToolChanges)));
set_toolpath_move_type_visible(EMoveType::Color_change, is_flag_set(static_cast<unsigned int>(Preview::OptionType::ColorChanges)));
set_toolpath_move_type_visible(EMoveType::Pause_Print, is_flag_set(static_cast<unsigned int>(Preview::OptionType::PausePrints)));
@ -1122,7 +1091,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
fclose(fp);
}
#if ENABLE_GCODE_WINDOW
void GCodeViewer::start_mapping_gcode_window()
{
m_sequential_view.gcode_window.load_gcode();
@ -1132,7 +1100,6 @@ void GCodeViewer::stop_mapping_gcode_window()
{
m_sequential_view.gcode_window.stop_mapping_file();
}
#endif // ENABLE_GCODE_WINDOW
void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
{
@ -1430,11 +1397,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
// for the gcode viewer we need to take in account all moves to correctly size the printbed
m_paths_bounding_box.merge(move.position.cast<double>());
else {
#if ENABLE_START_GCODE_VISUALIZATION
if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f)
#else
if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f)
#endif // ENABLE_START_GCODE_VISUALIZATION
m_paths_bounding_box.merge(move.position.cast<double>());
}
}
@ -1443,12 +1406,10 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
m_max_bounding_box = m_paths_bounding_box;
m_max_bounding_box.merge(m_paths_bounding_box.max + m_sequential_view.marker.get_bounding_box().size()[2] * Vec3d::UnitZ());
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_sequential_view.gcode_ids.clear();
for (const GCodeProcessor::MoveVertex& move : gcode_result.moves) {
m_sequential_view.gcode_ids.push_back(move.gcode_id);
}
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
std::vector<MultiVertexBuffer> vertices(m_buffers.size());
std::vector<MultiIndexBuffer> indices(m_buffers.size());
@ -2188,9 +2149,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
case EMoveType::Custom_GCode: { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::CustomGCodes)]; break; }
case EMoveType::Retract: { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::Retractions)]; break; }
case EMoveType::Unretract: { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::Unretractions)]; break; }
#if ENABLE_SEAMS_VISUALIZATION
case EMoveType::Seam: { color = Options_Colors[static_cast<unsigned int>(EOptionsColors::Seams)]; break; }
#endif // ENABLE_SEAMS_VISUALIZATION
case EMoveType::Extrude: {
if (!top_layer_only ||
m_sequential_view.current.last == global_endpoints.last ||
@ -2594,34 +2553,22 @@ void GCodeViewer::render_shells() const
// glsafe(::glDepthMask(GL_TRUE));
}
#if ENABLE_GCODE_WINDOW
void GCodeViewer::render_legend(float& legend_height) const
#else
void GCodeViewer::render_legend() const
#endif // ENABLE_GCODE_WINDOW
{
if (!m_legend_enabled)
return;
#if ENABLE_SCROLLABLE_LEGEND
const Size cnv_size = wxGetApp().plater()->get_current_canvas3D()->get_canvas_size();
#endif // ENABLE_SCROLLABLE_LEGEND
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_pos(0.0f, 0.0f, ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::SetNextWindowBgAlpha(0.6f);
#if ENABLE_SCROLLABLE_LEGEND
const float max_height = 0.75f * static_cast<float>(cnv_size.get_height());
const float child_height = 0.3333f * max_height;
ImGui::SetNextWindowSizeConstraints({ 0.0f, 0.0f }, { -1.0f, max_height });
imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove);
#else
imgui.begin(std::string("Legend"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
#endif // ENABLE_SCROLLABLE_LEGEND
enum class EItemType : unsigned char
{
@ -2632,30 +2579,22 @@ void GCodeViewer::render_legend() const
};
const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast<size_t>(m_time_estimate_mode)];
#if ENABLE_SCROLLABLE_LEGEND
bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType ||
(m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()));
#endif // ENABLE_SCROLLABLE_LEGEND
const float icon_size = ImGui::GetTextLineHeight();
const float percent_bar_size = 2.0f * ImGui::GetTextLineHeight();
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
#if ENABLE_SCROLLABLE_LEGEND
auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
#else
auto append_item = [this, draw_list, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
#endif // ENABLE_SCROLLABLE_LEGEND
bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array<float, 4>& offsets = { 0.0f, 0.0f, 0.0f, 0.0f },
double used_filament_m = 0.0, double used_filament_g = 0.0,
std::function<void()> callback = nullptr) {
if (!visible)
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.3333f);
#if ENABLE_SCROLLABLE_LEGEND
ImDrawList* draw_list = ImGui::GetWindowDrawList();
#endif // ENABLE_SCROLLABLE_LEGEND
ImVec2 pos = ImGui::GetCursorScreenPos();
switch (type) {
default:
@ -3008,8 +2947,11 @@ void GCodeViewer::render_legend() const
}
case EViewType::ColorPrint:
{
#if ENABLE_SCROLLABLE_LEGEND
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
#else
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
size_t total_items = 1;
for (unsigned char i : m_extruder_ids) {
total_items += color_print_ranges(i, custom_gcode_per_print_z).size();
@ -3020,9 +2962,6 @@ void GCodeViewer::render_legend() const
// add scrollable region, if needed
if (need_scrollable)
ImGui::BeginChild("color_prints", { -1.0f, child_height }, false);
#else
const std::vector<CustomGCode::Item>& custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
#endif // ENABLE_SCROLLABLE_LEGEND
if (m_extruders_count == 1) { // single extruder use case
const std::vector<std::pair<Color, std::pair<double, double>>> cp_values = color_print_ranges(0, custom_gcode_per_print_z);
const int items_cnt = static_cast<int>(cp_values.size());
@ -3073,10 +3012,8 @@ void GCodeViewer::render_legend() const
}
}
}
#if ENABLE_SCROLLABLE_LEGEND
if (need_scrollable)
ImGui::EndChild();
#endif // ENABLE_SCROLLABLE_LEGEND
break;
}
@ -3109,7 +3046,11 @@ void GCodeViewer::render_legend() const
auto generate_partial_times = [this, get_used_filament_from_volume](const TimesList& times, const std::vector<double>& used_filaments) {
PartialTimes items;
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().is_editor() ? wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes : m_custom_gcode_per_print_z;
#else
std::vector<CustomGCode::Item> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
int extruders_count = wxGetApp().extruders_edited_cnt();
std::vector<Color> last_color(extruders_count);
for (int i = 0; i < extruders_count; ++i) {
@ -3227,12 +3168,10 @@ void GCodeViewer::render_legend() const
ImGui::Spacing();
append_headers({ _u8L("Event"), _u8L("Remaining time"), _u8L("Duration"), _u8L("Used filament") }, offsets);
#if ENABLE_SCROLLABLE_LEGEND
const bool need_scrollable = static_cast<float>(partial_times.size()) * (icon_size + ImGui::GetStyle().ItemSpacing.y) > child_height;
if (need_scrollable)
// add scrollable region
ImGui::BeginChild("events", { -1.0f, child_height }, false);
#endif // ENABLE_SCROLLABLE_LEGEND
for (const PartialTime& item : partial_times) {
switch (item.type)
@ -3254,10 +3193,8 @@ void GCodeViewer::render_legend() const
}
}
#if ENABLE_SCROLLABLE_LEGEND
if (need_scrollable)
ImGui::EndChild();
#endif // ENABLE_SCROLLABLE_LEGEND
}
}
@ -3316,12 +3253,8 @@ void GCodeViewer::render_legend() const
available(EMoveType::Pause_Print) ||
available(EMoveType::Retract) ||
available(EMoveType::Tool_change) ||
#if ENABLE_SEAMS_VISUALIZATION
available(EMoveType::Unretract) ||
available(EMoveType::Seam);
#else
available(EMoveType::Unretract);
#endif // ENABLE_SEAMS_VISUALIZATION
};
auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) {
@ -3339,9 +3272,7 @@ void GCodeViewer::render_legend() const
// items
add_option(EMoveType::Retract, EOptionsColors::Retractions, _u8L("Retractions"));
add_option(EMoveType::Unretract, EOptionsColors::Unretractions, _u8L("Deretractions"));
#if ENABLE_SEAMS_VISUALIZATION
add_option(EMoveType::Seam, EOptionsColors::Seams, _u8L("Seams"));
#endif // ENABLE_SEAMS_VISUALIZATION
add_option(EMoveType::Tool_change, EOptionsColors::ToolChanges, _u8L("Tool changes"));
add_option(EMoveType::Color_change, EOptionsColors::ColorChanges, _u8L("Color changes"));
add_option(EMoveType::Pause_Print, EOptionsColors::PausePrints, _u8L("Print pauses"));
@ -3407,48 +3338,56 @@ void GCodeViewer::render_legend() const
}
// total estimated printing time section
#if ENABLE_SCROLLABLE_LEGEND
if (show_estimated_time) {
#else
if (time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType ||
(m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty()))) {
ImGui::Spacing();
#endif // ENABLE_SCROLLABLE_LEGEND
ImGui::Spacing();
std::string time_title = _u8L("Estimated printing times");
switch (m_time_estimate_mode)
{
case PrintEstimatedStatistics::ETimeMode::Normal: { time_title += " [" + _u8L("Normal mode") + "]:"; break; }
case PrintEstimatedStatistics::ETimeMode::Stealth: { time_title += " [" + _u8L("Stealth mode") + "]:"; break; }
default: { assert(false); break; }
auto can_show_mode_button = [this](PrintEstimatedStatistics::ETimeMode mode) {
bool show = false;
if (m_print_statistics.modes.size() > 1 && m_print_statistics.modes[static_cast<size_t>(mode)].roles_times.size() > 0) {
for (size_t i = 0; i < m_print_statistics.modes.size(); ++i) {
if (i != static_cast<size_t>(mode) &&
m_print_statistics.modes[i].time > 0.0f &&
short_time(get_time_dhms(m_print_statistics.modes[static_cast<size_t>(mode)].time)) != short_time(get_time_dhms(m_print_statistics.modes[i].time))) {
show = true;
break;
}
}
}
return show;
};
if (can_show_mode_button(m_time_estimate_mode)) {
switch (m_time_estimate_mode)
{
case PrintEstimatedStatistics::ETimeMode::Normal: { time_title += " [" + _u8L("Normal mode") + "]"; break; }
case PrintEstimatedStatistics::ETimeMode::Stealth: { time_title += " [" + _u8L("Stealth mode") + "]"; break; }
default: { assert(false); break; }
}
}
imgui.title(time_title);
imgui.title(time_title + ":");
std::string first_str = _u8L("First layer");
std::string total_str = _u8L("Total");
float max_len = 10.0f + ImGui::GetStyle().ItemSpacing.x + std::max(ImGui::CalcTextSize(first_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x);
float max_len = 10.0f + ImGui::GetStyle().ItemSpacing.x;
if (time_mode.layers_times.empty())
max_len += ImGui::CalcTextSize(total_str.c_str()).x;
else
max_len += std::max(ImGui::CalcTextSize(first_str.c_str()).x, ImGui::CalcTextSize(total_str.c_str()).x);
imgui.text(first_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.layers_times.front())));
if (!time_mode.layers_times.empty()) {
imgui.text(first_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.layers_times.front())));
}
imgui.text(total_str + ":");
ImGui::SameLine(max_len);
imgui.text(short_time(get_time_dhms(time_mode.time)));
auto show_mode_button = [this, &imgui](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) {
bool show = false;
for (size_t i = 0; i < m_print_statistics.modes.size(); ++i) {
if (i != static_cast<size_t>(mode) &&
short_time(get_time_dhms(m_print_statistics.modes[static_cast<size_t>(mode)].time)) != short_time(get_time_dhms(m_print_statistics.modes[i].time))) {
show = true;
break;
}
}
if (show && m_print_statistics.modes[static_cast<size_t>(mode)].roles_times.size() > 0) {
auto show_mode_button = [this, &imgui, can_show_mode_button](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) {
if (can_show_mode_button(mode)) {
if (imgui.button(label)) {
*const_cast<PrintEstimatedStatistics::ETimeMode*>(&m_time_estimate_mode) = mode;
wxGetApp().plater()->get_current_canvas3D()->set_as_dirty();
@ -3470,9 +3409,7 @@ void GCodeViewer::render_legend() const
}
}
#if ENABLE_GCODE_WINDOW
legend_height = ImGui::GetCurrentWindow()->Size.y;
#endif // ENABLE_GCODE_WINDOW
imgui.end();
ImGui::PopStyleVar();

View file

@ -5,9 +5,7 @@
#include "libslic3r/GCode/GCodeProcessor.hpp"
#include "GLModel.hpp"
#if ENABLE_GCODE_WINDOW
#include <boost/iostreams/device/mapped_file.hpp>
#endif // ENABLE_GCODE_WINDOW
#include <cstdint>
#include <float.h>
@ -40,9 +38,7 @@ class GCodeViewer
{
Retractions,
Unretractions,
#if ENABLE_SEAMS_VISUALIZATION
Seams,
#endif // ENABLE_SEAMS_VISUALIZATION
ToolChanges,
ColorChanges,
PausePrints,
@ -520,7 +516,6 @@ public:
void render() const;
};
#if ENABLE_GCODE_WINDOW
class GCodeWindow
{
struct Line
@ -557,7 +552,6 @@ public:
void stop_mapping_file();
};
#endif // ENABLE_GCODE_WINDOW
struct Endpoints
{
@ -571,16 +565,10 @@ public:
Endpoints last_current;
Vec3f current_position{ Vec3f::Zero() };
Marker marker;
#if ENABLE_GCODE_WINDOW
GCodeWindow gcode_window;
#endif // ENABLE_GCODE_WINDOW
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
std::vector<unsigned int> gcode_ids;
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
#if ENABLE_GCODE_WINDOW
void render(float legend_height) const;
#endif // ENABLE_GCODE_WINDOW
};
enum class EViewType : unsigned char
@ -628,6 +616,10 @@ private:
GCodeProcessor::Result::SettingsIds m_settings_ids;
std::array<SequentialRangeCap, 2> m_sequential_range_caps;
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item> m_custom_gcode_per_print_z;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
public:
GCodeViewer();
~GCodeViewer() { reset(); }
@ -673,11 +665,14 @@ public:
void export_toolpaths_to_obj(const char* filename) const;
#if ENABLE_GCODE_WINDOW
void start_mapping_gcode_window();
void stop_mapping_gcode_window();
void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
#endif // ENABLE_GCODE_WINDOW
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_custom_gcode_per_print_z; }
size_t get_extruders_count() { return m_extruders_count; }
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
private:
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
@ -685,11 +680,7 @@ private:
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths() const;
void render_shells() const;
#if ENABLE_GCODE_WINDOW
void render_legend(float& legend_height) const;
#else
void render_legend() const;
#endif // ENABLE_GCODE_WINDOW
#if ENABLE_GCODE_VIEWER_STATISTICS
void render_statistics() const;
#endif // ENABLE_GCODE_VIEWER_STATISTICS

View file

@ -169,17 +169,10 @@ void GLCanvas3D::LayersEditing::set_config(const DynamicPrintConfig* config)
void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
{
const ModelObject *model_object_new = (object_id >= 0) ? model.objects[object_id] : nullptr;
#if ENABLE_ALLOW_NEGATIVE_Z
// Maximum height of an object changes when the object gets rotated or scaled.
// Changing maximum height of an object will invalidate the layer heigth editing profile.
// m_model_object->bounding_box() is cached, therefore it is cheap even if this method is called frequently.
const float new_max_z = (model_object_new == nullptr) ? 0.0f : static_cast<float>(model_object_new->bounding_box().max.z());
#else
// Maximum height of an object changes when the object gets rotated or scaled.
// Changing maximum height of an object will invalidate the layer heigth editing profile.
// m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
float new_max_z = (model_object_new == nullptr) ? 0.f : model_object_new->raw_bounding_box().size().z();
#endif // ENABLE_ALLOW_NEGATIVE_Z
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
m_layer_height_profile.clear();
@ -261,7 +254,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
float widget_align = ImGui::GetCursorPosX();
ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
m_adaptive_quality = std::clamp(m_adaptive_quality, 0.0f, 1.f);
ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f");
imgui.slider_float("", &m_adaptive_quality, 0.0f, 1.f, "%.2f");
ImGui::Separator();
if (imgui.button(_L("Smooth")))
@ -799,7 +792,7 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons
for (const Polygon& poly : polygons) {
triangles_count += poly.points.size() - 2;
}
size_t vertices_count = 3 * triangles_count;
const size_t vertices_count = 3 * triangles_count;
if (m_render_fill) {
GLModel::InitializationData fill_data;
@ -810,13 +803,13 @@ void GLCanvas3D::SequentialPrintClearance::set_polygons(const Polygons& polygons
entity.normals.reserve(vertices_count);
entity.indices.reserve(vertices_count);
ExPolygons polygons_union = union_ex(polygons);
const ExPolygons polygons_union = union_ex(polygons);
for (const ExPolygon& poly : polygons_union) {
std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly, false);
const std::vector<Vec3d> triangulation = triangulate_expolygon_3d(poly);
for (const Vec3d& v : triangulation) {
entity.positions.emplace_back(v.cast<float>() + Vec3f(0.0f, 0.0f, 0.0125f)); // add a small positive z to avoid z-fighting
entity.normals.emplace_back(Vec3f::UnitZ());
size_t positions_count = entity.positions.size();
const size_t positions_count = entity.positions.size();
if (positions_count % 3 == 0) {
entity.indices.emplace_back(positions_count - 3);
entity.indices.emplace_back(positions_count - 2);
@ -907,6 +900,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RENDER_TIMER, wxTimerEvent/*RenderTimerEvent*/);
wxDEFINE_EVENT(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, wxTimerEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent);
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
@ -1119,7 +1114,6 @@ int GLCanvas3D::check_volumes_outside_state() const
return (int)state;
}
#if ENABLE_GCODE_WINDOW
void GLCanvas3D::start_mapping_gcode_window()
{
m_gcode_viewer.start_mapping_gcode_window();
@ -1129,7 +1123,6 @@ void GLCanvas3D::stop_mapping_gcode_window()
{
m_gcode_viewer.stop_mapping_gcode_window();
}
#endif // ENABLE_GCODE_WINDOW
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
{
@ -1416,10 +1409,6 @@ void GLCanvas3D::render()
if (!is_initialized() && !init())
return;
#if ENABLE_RENDER_STATISTICS
auto start_time = std::chrono::high_resolution_clock::now();
#endif // ENABLE_RENDER_STATISTICS
if (wxGetApp().plater()->get_bed().get_shape().empty()) {
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
post_event(SimpleEvent(EVT_GLCANVAS_UPDATE_BED_SHAPE));
@ -1514,19 +1503,12 @@ void GLCanvas3D::render()
// draw overlays
_render_overlays();
#if ENABLE_RENDER_STATISTICS
if (wxGetApp().plater()->is_render_statistic_dialog_visible()) {
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
imgui.text("Last frame:");
imgui.text("FPS (SwapBuffers() calls per second):");
ImGui::SameLine();
int64_t average = m_render_stats.get_average();
imgui.text(std::to_string(average));
ImGui::SameLine();
imgui.text("ms");
imgui.text("FPS:");
ImGui::SameLine();
imgui.text(std::to_string((average == 0) ? 0 : static_cast<int>(1000.0f / static_cast<float>(average))));
imgui.text(std::to_string(m_render_stats.get_fps_and_reset_if_needed()));
ImGui::Separator();
imgui.text("Compressed textures:");
ImGui::SameLine();
@ -1536,7 +1518,6 @@ void GLCanvas3D::render()
imgui.text(std::to_string(OpenGLManager::get_gl_info().get_max_tex_size()));
imgui.end();
}
#endif // ENABLE_RENDER_STATISTICS
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
if (wxGetApp().is_editor() && wxGetApp().plater()->is_view3D_shown())
@ -1583,11 +1564,7 @@ void GLCanvas3D::render()
wxGetApp().imgui()->render();
m_canvas->SwapBuffers();
#if ENABLE_RENDER_STATISTICS
auto end_time = std::chrono::high_resolution_clock::now();
m_render_stats.add_frame(std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count());
#endif // ENABLE_RENDER_STATISTICS
m_render_stats.increment_fps_counter();
}
void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type)
@ -1625,14 +1602,17 @@ void GLCanvas3D::delete_selected()
m_selection.erase();
}
void GLCanvas3D::ensure_on_bed(unsigned int object_idx)
void GLCanvas3D::ensure_on_bed(unsigned int object_idx, bool allow_negative_z)
{
if (allow_negative_z)
return;
typedef std::map<std::pair<int, int>, double> InstancesToZMap;
InstancesToZMap instances_min_z;
for (GLVolume* volume : m_volumes.volumes) {
if (volume->object_idx() == (int)object_idx && !volume->is_modifier) {
double min_z = volume->transformed_convex_hull_bounding_box().min(2);
double min_z = volume->transformed_convex_hull_bounding_box().min.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_min_z.find(instance);
if (it == instances_min_z.end())
@ -2189,6 +2169,10 @@ void GLCanvas3D::bind_event_handlers()
m_canvas->Bind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this);
m_canvas->Bind(wxEVT_TIMER, &GLCanvas3D::on_timer, this);
m_canvas->Bind(EVT_GLCANVAS_RENDER_TIMER, &GLCanvas3D::on_render_timer, this);
m_toolbar_highlighter.set_timer_owner(m_canvas, 0);
m_canvas->Bind(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_toolbar_highlighter.blink(); });
m_gizmo_highlighter.set_timer_owner(m_canvas, 0);
m_canvas->Bind(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, [this](wxTimerEvent&) { m_gizmo_highlighter.blink(); });
m_canvas->Bind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this);
m_canvas->Bind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this);
@ -2419,10 +2403,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; }
case 'B':
case 'b': { zoom_to_bed(); break; }
#if ENABLE_GCODE_WINDOW
case 'C':
case 'c': { m_gcode_viewer.toggle_gcode_window_visibility(); m_dirty = true; request_extra_frame(); break; }
#endif // ENABLE_GCODE_WINDOW
case 'E':
case 'e': { m_labels.show(!m_labels.is_shown()); m_dirty = true; break; }
case 'G':
@ -2449,8 +2431,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
case 'O':
case 'o': { _update_camera_zoom(-1.0); break; }
#if ENABLE_RENDER_PICKING_PASS
case 'P':
case 'p': {
case 'T':
case 't': {
m_show_picking_texture = !m_show_picking_texture;
m_dirty = true;
break;
@ -2603,15 +2585,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
{
if (!m_gizmos.on_key(evt)) {
if (evt.GetEventType() == wxEVT_KEY_UP) {
#if ENABLE_RENDER_STATISTICS
if (evt.ShiftDown() && evt.ControlDown() && keyCode == WXK_SPACE) {
wxGetApp().plater()->toggle_render_statistic_dialog();
m_dirty = true;
}
if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) {
#else
if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) {
#endif // ENABLE_RENDER_STATISTICS
// Enable switching between 3D and Preview with Tab
// m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux
post_event(SimpleEvent(EVT_GLCANVAS_TAB));
@ -2969,6 +2947,20 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
return;
}
#if ENABLE_SINKING_CONTOURS
for (GLVolume* volume : m_volumes.volumes) {
volume->force_sinking_contours = false;
}
auto show_sinking_contours = [this]() {
const Selection::IndicesList& idxs = m_selection.get_volume_idxs();
for (unsigned int idx : idxs) {
m_volumes.volumes[idx]->force_sinking_contours = true;
}
m_dirty = true;
};
#endif // ENABLE_SINKING_CONTOURS
if (m_gizmos.on_mouse(evt)) {
if (wxWindow::FindFocus() != m_canvas)
// Grab keyboard focus for input in gizmo dialogs.
@ -2993,6 +2985,21 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
default: { break; }
}
}
#if ENABLE_SINKING_CONTOURS
else if (evt.Dragging()) {
switch (m_gizmos.get_current_type())
{
case GLGizmosManager::EType::Move:
case GLGizmosManager::EType::Scale:
case GLGizmosManager::EType::Rotate:
{
show_sinking_contours();
break;
}
default: { break; }
}
}
#endif // ENABLE_SINKING_CONTOURS
return;
}
@ -3302,6 +3309,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else
evt.Skip();
#if ENABLE_SINKING_CONTOURS
if (m_moving)
show_sinking_contours();
#endif // ENABLE_SINKING_CONTOURS
#ifdef __WXMSW__
if (on_enter_workaround)
m_mouse.position = Vec2d(-1., -1.);
@ -3405,37 +3417,22 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
wipe_tower_origin = v->get_volume_offset();
}
#if ENABLE_ALLOW_NEGATIVE_Z
// Fixes flying instances
#else
// Fixes sinking/flying instances
#endif // ENABLE_ALLOW_NEGATIVE_Z
for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first];
#if ENABLE_ALLOW_NEGATIVE_Z
const double shift_z = m->get_instance_min_z(i.second);
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
if (current_printer_technology() == ptSLA || shift_z > SINKING_Z_THRESHOLD) {
#else
if (shift_z > 0.0) {
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
const Vec3d shift(0.0, 0.0, -shift_z);
#else
const Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
#endif // ENABLE_ALLOW_NEGATIVE_Z
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
#if ENABLE_ALLOW_NEGATIVE_Z
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
}
#if ENABLE_ALLOW_NEGATIVE_Z
// if the selection is not valid to allow for layer editing after the move, we need to turn off the tool if it is running
// similar to void Plater::priv::selection_changed()
if (!wxGetApp().plater()->can_layers_editing() && is_layers_editing_enabled())
post_event(SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING));
#endif // ENABLE_ALLOW_NEGATIVE_Z
if (object_moved)
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
@ -3456,7 +3453,6 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
if (!snapshot_type.empty())
wxGetApp().plater()->take_snapshot(_(snapshot_type));
#if ENABLE_ALLOW_NEGATIVE_Z
// stores current min_z of instances
std::map<std::pair<int, int>, double> min_zs;
if (!snapshot_type.empty()) {
@ -3467,7 +3463,6 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
}
}
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
std::set<std::pair<int, int>> done; // keeps track of modified instances
@ -3505,19 +3500,15 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
// Fixes sinking/flying instances
for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first];
#if ENABLE_ALLOW_NEGATIVE_Z
double shift_z = m->get_instance_min_z(i.second);
const double shift_z = m->get_instance_min_z(i.second);
// leave sinking instances as sinking
if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) {
Vec3d shift(0.0, 0.0, -shift_z);
#else
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
#endif // ENABLE_ALLOW_NEGATIVE_Z
const Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
#if ENABLE_ALLOW_NEGATIVE_Z
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
}
if (!done.empty())
@ -3534,7 +3525,6 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
if (!snapshot_type.empty())
wxGetApp().plater()->take_snapshot(_(snapshot_type));
#if ENABLE_ALLOW_NEGATIVE_Z
// stores current min_z of instances
std::map<std::pair<int, int>, double> min_zs;
if (!snapshot_type.empty()) {
@ -3545,7 +3535,6 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
}
}
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
std::set<std::pair<int, int>> done; // keeps track of modified instances
@ -3580,19 +3569,14 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
// Fixes sinking/flying instances
for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first];
#if ENABLE_ALLOW_NEGATIVE_Z
double shift_z = m->get_instance_min_z(i.second);
// leave sinking instances as sinking
if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) {
Vec3d shift(0.0, 0.0, -shift_z);
#else
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
#endif // ENABLE_ALLOW_NEGATIVE_Z
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
#if ENABLE_ALLOW_NEGATIVE_Z
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
}
if (!done.empty())
@ -3618,14 +3602,24 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
if (!snapshot_type.empty())
wxGetApp().plater()->take_snapshot(_(snapshot_type));
// stores current min_z of instances
std::map<std::pair<int, int>, double> min_zs;
if (!snapshot_type.empty()) {
for (int i = 0; i < static_cast<int>(m_model->objects.size()); ++i) {
const ModelObject* obj = m_model->objects[i];
for (int j = 0; j < static_cast<int>(obj->instances.size()); ++j) {
min_zs[{ i, j }] = obj->instance_bounding_box(j).min.z();
}
}
}
std::set<std::pair<int, int>> done; // keeps track of modified instances
Selection::EMode selection_mode = m_selection.get_mode();
for (const GLVolume* v : m_volumes.volumes)
{
for (const GLVolume* v : m_volumes.volumes) {
int object_idx = v->object_idx();
if ((object_idx < 0) || ((int)m_model->objects.size() <= object_idx))
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
continue;
int instance_idx = v->instance_idx();
@ -3635,8 +3629,7 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
// Mirror instances/volumes
ModelObject* model_object = m_model->objects[object_idx];
if (model_object != nullptr)
{
if (model_object != nullptr) {
if (selection_mode == Selection::Instance)
model_object->instances[instance_idx]->set_mirror(v->get_instance_mirror());
else if (selection_mode == Selection::Volume)
@ -3647,12 +3640,16 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
}
// Fixes sinking/flying instances
for (const std::pair<int, int>& i : done)
{
for (const std::pair<int, int>& i : done) {
ModelObject* m = m_model->objects[i.first];
Vec3d shift(0.0, 0.0, -m->get_instance_min_z(i.second));
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
double shift_z = m->get_instance_min_z(i.second);
// leave sinking instances as sinking
if (min_zs.empty() || min_zs.find({ i.first, i.second })->second >= SINKING_Z_THRESHOLD || shift_z > SINKING_Z_THRESHOLD) {
Vec3d shift(0.0, 0.0, -shift_z);
m_selection.translate(i.first, i.second, shift);
m->translate_instance(i.second, shift);
}
wxGetApp().obj_list()->update_info_items(static_cast<size_t>(i.first));
}
post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
@ -3881,6 +3878,15 @@ void GLCanvas3D::update_sequential_clearance()
set_sequential_print_clearance_polygons(polygons);
}
bool GLCanvas3D::is_object_sinking(int object_idx) const
{
for (const GLVolume* v : m_volumes.volumes) {
if (v->object_idx() == object_idx && v->is_sinking())
return true;
}
return false;
}
bool GLCanvas3D::_is_shown_on_screen() const
{
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
@ -4425,6 +4431,29 @@ bool GLCanvas3D::_init_main_toolbar()
m_main_toolbar.set_enabled(false);
return true;
}
// init arrow
BackgroundTexture::Metadata arrow_data;
arrow_data.filename = "toolbar_arrow.png";
// arrow_data.filename = "toolbar_arrow.svg";
//arrow_data.left = 16;
//arrow_data.top = 16;
//arrow_data.right = 16;
//arrow_data.bottom = 16;
arrow_data.left = 0;
arrow_data.top = 0;
arrow_data.right = 0;
arrow_data.bottom = 0;
if (!m_main_toolbar.init_arrow(arrow_data))
{
BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture.";
}
if (!m_gizmos.init_arrow(arrow_data))
{
BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture.";
}
// m_main_toolbar.set_layout_type(GLToolbar::Layout::Vertical);
m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal);
@ -4744,13 +4773,11 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
if (m_canvas == nullptr && m_context == nullptr)
return;
#if ENABLE_SCROLLABLE_LEGEND
const std::array<unsigned int, 2> new_size = { w, h };
if (m_old_size == new_size)
return;
m_old_size = new_size;
#endif // ENABLE_SCROLLABLE_LEGEND
auto *imgui = wxGetApp().imgui();
imgui->set_display_size(static_cast<float>(w), static_cast<float>(h));
@ -4761,9 +4788,7 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
imgui->set_scaling(font_size, m_canvas->GetContentScaleFactor(), 1.0f);
#endif
#if ENABLE_SCROLLABLE_LEGEND
this->request_extra_frame();
#endif // ENABLE_SCROLLABLE_LEGEND
// ensures that this canvas is current
_set_current();
@ -4842,21 +4867,30 @@ void GLCanvas3D::_picking_pass()
if (m_camera_clipping_plane.is_active())
::glDisable(GL_CLIP_PLANE0);
_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward());
m_gizmos.render_current_gizmo_for_picking_pass();
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
int volume_id = -1;
int gizmo_id = -1;
GLubyte color[4] = { 0, 0, 0, 0 };
const Size& cnv_size = get_canvas_size();
bool inside = 0 <= m_mouse.position(0) && m_mouse.position(0) < cnv_size.get_width() && 0 <= m_mouse.position(1) && m_mouse.position(1) < cnv_size.get_height();
if (inside) {
glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color));
if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3])
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
volume_id = color[0] + (color[1] << 8) + (color[2] << 16);
if (picking_checksum_alpha_channel(color[0], color[1], color[2]) == color[3]) {
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
// we reserve color = (0,0,0) for occluders (as the printbed)
// volumes' id are shifted by 1
// see: _render_volumes_for_picking()
volume_id = color[0] + (color[1] << 8) + (color[2] << 16) - 1;
// gizmos' id are instead properly encoded by the color
gizmo_id = color[0] + (color[1] << 8) + (color[2] << 16);
}
}
if (0 <= volume_id && volume_id < (int)m_volumes.volumes.size()) {
// do not add the volume id if any gizmo is active and CTRL is pressed
@ -4865,7 +4899,7 @@ void GLCanvas3D::_picking_pass()
m_gizmos.set_hover_id(-1);
}
else
m_gizmos.set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1);
m_gizmos.set_hover_id(inside && (unsigned int)gizmo_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - gizmo_id) : -1);
_update_volumes_hover_state();
}
@ -4888,6 +4922,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_volumes_for_picking();
_render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward());
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
@ -4906,7 +4941,10 @@ void GLCanvas3D::_rectangular_selection_picking_pass()
std::array<GLubyte, 4> data;
// Only non-interpolated colors are valid, those have their lowest three bits zeroed.
bool valid() const { return picking_checksum_alpha_channel(data[0], data[1], data[2]) == data[3]; }
int id() const { return data[0] + (data[1] << 8) + (data[2] << 16); }
// we reserve color = (0,0,0) for occluders (as the printbed)
// volumes' id are shifted by 1
// see: _render_volumes_for_picking()
int id() const { return data[0] + (data[1] << 8) + (data[2] << 16) - 1; }
};
std::vector<Pixel> frame(px_count);
@ -5028,6 +5066,17 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes)
wxGetApp().plater()->get_bed().render(*this, bottom, scale_factor, show_axes, show_texture);
}
void GLCanvas3D::_render_bed_for_picking(bool bottom)
{
float scale_factor = 1.0;
#if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL
wxGetApp().plater()->get_bed().render_for_picking(*this, bottom, scale_factor);
}
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
#else
@ -5270,50 +5319,30 @@ void GLCanvas3D::_render_volumes_for_picking() const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
#if ENABLE_ALLOW_NEGATIVE_Z
auto* shader = wxGetApp().get_shader("picking");
if (!shader)
return;
#endif // ENABLE_ALLOW_NEGATIVE_Z
// do not cull backfaces to show broken geometry, if any
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
#if ENABLE_ALLOW_NEGATIVE_Z
shader->start_using();
shader->set_uniform("viewed_from_top", wxGetApp().plater()->get_camera().is_looking_downward());
#endif // ENABLE_ALLOW_NEGATIVE_Z
const Transform3d& view_matrix = wxGetApp().plater()->get_camera().get_view_matrix();
for (size_t type = 0; type < 2; ++ type) {
GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, (type == 0) ? GLVolumeCollection::ERenderType::Opaque : GLVolumeCollection::ERenderType::Transparent, view_matrix);
for (const GLVolumeWithIdAndZ& volume : to_render)
if (!volume.first->disabled && (volume.first->composite_id.volume_id >= 0 || m_render_sla_auxiliaries)) {
// Object picking mode. Render the object with a color encoding the object index.
unsigned int id = volume.second.first;
unsigned int r = (id & (0x000000FF << 0)) << 0;
// we reserve color = (0,0,0) for occluders (as the printbed)
// so we shift volumes' id by 1 to get the proper color
unsigned int id = 1 + volume.second.first;
unsigned int r = (id & (0x000000FF << 0)) << 0;
unsigned int g = (id & (0x000000FF << 8)) >> 8;
unsigned int b = (id & (0x000000FF << 16)) >> 16;
unsigned int a = picking_checksum_alpha_channel(r, g, b);
#if ENABLE_ALLOW_NEGATIVE_Z
std::array<float, 4> color = { (float)r * INV_255, (float)g * INV_255, (float)b * INV_255, (float)a * INV_255 };
shader->set_uniform("uniform_color", color);
shader->set_uniform("world_matrix", volume.first->world_matrix());
#else
glsafe(::glColor4f((GLfloat)r * INV_255, (GLfloat)g * INV_255, (GLfloat)b * INV_255, (GLfloat)a * INV_255));
#endif // ENABLE_ALLOW_NEGATIVE_Z
volume.first->render();
}
}
#if ENABLE_ALLOW_NEGATIVE_Z
shader->stop_using();
#endif // ENABLE_ALLOW_NEGATIVE_Z
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
@ -5339,6 +5368,11 @@ void GLCanvas3D::_render_gizmos_overlay()
#endif /* __WXMSW__ */
m_gizmos.render_overlay();
if (m_gizmo_highlighter.m_render_arrow)
{
m_gizmos.render_arrow(*this, m_gizmo_highlighter.m_gizmo_type);
}
}
void GLCanvas3D::_render_main_toolbar()
@ -5356,6 +5390,10 @@ void GLCanvas3D::_render_main_toolbar()
m_main_toolbar.set_position(top, left);
m_main_toolbar.render(*this);
if (m_toolbar_highlighter.m_render_arrow)
{
m_main_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item);
}
}
void GLCanvas3D::_render_undoredo_toolbar()
@ -5639,6 +5677,11 @@ void GLCanvas3D::_update_volumes_hover_state()
}
}
}
#if ENABLE_SINKING_CONTOURS
else if (volume.selected)
volume.hover = GLVolume::HS_Hover;
#endif // ENABLE_SINKING_CONTOURS
}
}
@ -6518,6 +6561,24 @@ bool GLCanvas3D::_deactivate_collapse_toolbar_items()
return false;
}
void GLCanvas3D::highlight_toolbar_item(const std::string& item_name)
{
GLToolbarItem* item = m_main_toolbar.get_item(item_name);
if (!item)
item = m_undoredo_toolbar.get_item(item_name);
if (!item || !item->is_visible())
return;
m_toolbar_highlighter.init(item, this);
}
void GLCanvas3D::highlight_gizmo(const std::string& gizmo_name)
{
GLGizmosManager::EType gizmo = m_gizmos.get_gizmo_from_name(gizmo_name);
if(gizmo == GLGizmosManager::EType::Undefined)
return;
m_gizmo_highlighter.init(&m_gizmos, gizmo, this);
}
const Print* GLCanvas3D::fff_print() const
{
return (m_process == nullptr) ? nullptr : m_process->fff_print();
@ -6537,10 +6598,119 @@ void GLCanvas3D::WipeTowerInfo::apply_wipe_tower() const
wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg);
}
void GLCanvas3D::RenderTimer::Notify()
void GLCanvas3D::RenderTimer::Notify()
{
wxPostEvent((wxEvtHandler*)GetOwner(), RenderTimerEvent( EVT_GLCANVAS_RENDER_TIMER, *this));
}
void GLCanvas3D::ToolbarHighlighterTimer::Notify()
{
wxPostEvent((wxEvtHandler*)GetOwner(), ToolbarHighlighterTimerEvent(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, *this));
}
void GLCanvas3D::GizmoHighlighterTimer::Notify()
{
wxPostEvent((wxEvtHandler*)GetOwner(), GizmoHighlighterTimerEvent(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, *this));
}
void GLCanvas3D::ToolbarHighlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
{
m_timer.SetOwner(owner, timerid);
}
void GLCanvas3D::ToolbarHighlighter::init(GLToolbarItem* toolbar_item, GLCanvas3D* canvas)
{
if (m_timer.IsRunning())
invalidate();
if (!toolbar_item || !canvas)
return;
m_timer.Start(300, false);
m_toolbar_item = toolbar_item;
m_canvas = canvas;
}
void GLCanvas3D::ToolbarHighlighter::invalidate()
{
m_timer.Stop();
if (m_toolbar_item) {
m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::NotHighlighted);
}
m_toolbar_item = nullptr;
m_blink_counter = 0;
m_render_arrow = false;
}
void GLCanvas3D::ToolbarHighlighter::blink()
{
if (m_toolbar_item) {
char state = m_toolbar_item->get_highlight();
if (state != (char)GLToolbarItem::EHighlightState::HighlightedShown)
m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedShown);
else
m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedHidden);
m_render_arrow = !m_render_arrow;
m_canvas->set_as_dirty();
}
else
invalidate();
if ((++m_blink_counter) >= 11)
invalidate();
}
void GLCanvas3D::GizmoHighlighter::set_timer_owner(wxEvtHandler* owner, int timerid/* = wxID_ANY*/)
{
m_timer.SetOwner(owner, timerid);
}
void GLCanvas3D::GizmoHighlighter::init(GLGizmosManager* manager, GLGizmosManager::EType gizmo, GLCanvas3D* canvas)
{
if (m_timer.IsRunning())
invalidate();
if (!gizmo || !canvas)
return;
m_timer.Start(300, false);
m_gizmo_manager = manager;
m_gizmo_type = gizmo;
m_canvas = canvas;
}
void GLCanvas3D::GizmoHighlighter::invalidate()
{
m_timer.Stop();
if (m_gizmo_manager) {
m_gizmo_manager->set_highlight(GLGizmosManager::EType::Undefined, false);
}
m_gizmo_manager = nullptr;
m_gizmo_type = GLGizmosManager::EType::Undefined;
m_blink_counter = 0;
m_render_arrow = false;
}
void GLCanvas3D::GizmoHighlighter::blink()
{
if (m_gizmo_manager) {
if (m_blink_counter % 2 == 0)
m_gizmo_manager->set_highlight(m_gizmo_type, true);
else
m_gizmo_manager->set_highlight(m_gizmo_type, false);
m_render_arrow = !m_render_arrow;
m_canvas->set_as_dirty();
}
else
invalidate();
if ((++m_blink_counter) >= 11)
invalidate();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -93,6 +93,43 @@ private:
wxTimer* m_timer;
};
class ToolbarHighlighterTimerEvent : public wxEvent
{
public:
ToolbarHighlighterTimerEvent(wxEventType type, wxTimer& timer)
: wxEvent(timer.GetId(), type),
m_timer(&timer)
{
SetEventObject(timer.GetOwner());
}
int GetInterval() const { return m_timer->GetInterval(); }
wxTimer& GetTimer() const { return *m_timer; }
virtual wxEvent* Clone() const { return new ToolbarHighlighterTimerEvent(*this); }
virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_TIMER; }
private:
wxTimer* m_timer;
};
class GizmoHighlighterTimerEvent : public wxEvent
{
public:
GizmoHighlighterTimerEvent(wxEventType type, wxTimer& timer)
: wxEvent(timer.GetId(), type),
m_timer(&timer)
{
SetEventObject(timer.GetOwner());
}
int GetInterval() const { return m_timer->GetInterval(); }
wxTimer& GetTimer() const { return *m_timer; }
virtual wxEvent* Clone() const { return new GizmoHighlighterTimerEvent(*this); }
virtual wxEventCategory GetEventCategory() const { return wxEVT_CATEGORY_TIMER; }
private:
wxTimer* m_timer;
};
wxDECLARE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
@ -137,6 +174,8 @@ wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RELOAD_FROM_DISK, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RENDER_TIMER, wxTimerEvent/*RenderTimerEvent*/);
wxDECLARE_EVENT(EVT_GLCANVAS_TOOLBAR_HIGHLIGHTER_TIMER, wxTimerEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_GIZMO_HIGHLIGHTER_TIMER, wxTimerEvent);
class GLCanvas3D
{
@ -305,25 +344,27 @@ class GLCanvas3D
ObjectClashed
};
#if ENABLE_RENDER_STATISTICS
class RenderStats
{
std::queue<std::pair<int64_t, int64_t>> m_frames;
int64_t m_curr_total{ 0 };
private:
std::chrono::time_point<std::chrono::high_resolution_clock> m_measuring_start;
int m_fps_out = -1;
int m_fps_running = 0;
public:
void add_frame(int64_t frame) {
int64_t now = GLCanvas3D::timestamp_now();
if (!m_frames.empty() && now - m_frames.front().first > 1000) {
m_curr_total -= m_frames.front().second;
m_frames.pop();
void increment_fps_counter() { ++m_fps_running; }
int get_fps() { return m_fps_out; }
int get_fps_and_reset_if_needed() {
auto cur_time = std::chrono::high_resolution_clock::now();
int elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(cur_time-m_measuring_start).count();
if (elapsed_ms > 1000 || m_fps_out == -1) {
m_measuring_start = cur_time;
m_fps_out = int (1000. * m_fps_running / elapsed_ms);
m_fps_running = 0;
}
m_curr_total += frame;
m_frames.push({ now, frame });
return m_fps_out;
}
int64_t get_average() const { return m_frames.empty() ? 0 : m_curr_total / m_frames.size(); }
};
#endif // ENABLE_RENDER_STATISTICS
class Labels
{
@ -376,6 +417,16 @@ class GLCanvas3D
virtual void Notify() override;
};
class ToolbarHighlighterTimer : public wxTimer {
private:
virtual void Notify() override;
};
class GizmoHighlighterTimer : public wxTimer {
private:
virtual void Notify() override;
};
public:
enum ECursorType : unsigned char
{
@ -427,9 +478,7 @@ private:
Model* m_model;
BackgroundSlicingProcess *m_process;
#if ENABLE_SCROLLABLE_LEGEND
std::array<unsigned int, 2> m_old_size{ 0, 0 };
#endif // ENABLE_SCROLLABLE_LEGEND
// Screen is only refreshed from the OnIdle handler if it is dirty.
bool m_dirty;
@ -457,9 +506,7 @@ private:
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 };
int m_mouse_wheel{ 0 };
@ -519,6 +566,38 @@ private:
SequentialPrintClearance m_sequential_print_clearance;
bool m_sequential_print_clearance_first_displacement{ true };
struct ToolbarHighlighter
{
void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
void init(GLToolbarItem* toolbar_item, GLCanvas3D* canvas);
void blink();
void invalidate();
bool m_render_arrow{ false };
GLToolbarItem* m_toolbar_item{ nullptr };
private:
GLCanvas3D* m_canvas{ nullptr };
int m_blink_counter{ 0 };
ToolbarHighlighterTimer m_timer;
}
m_toolbar_highlighter;
struct GizmoHighlighter
{
void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY);
void init(GLGizmosManager* manager, GLGizmosManager::EType gizmo, GLCanvas3D* canvas);
void blink();
void invalidate();
bool m_render_arrow{ false };
GLGizmosManager::EType m_gizmo_type;
private:
GLGizmosManager* m_gizmo_manager{ nullptr };
GLCanvas3D* m_canvas{ nullptr };
int m_blink_counter{ 0 };
GizmoHighlighterTimer m_timer;
}
m_gizmo_highlighter;
public:
explicit GLCanvas3D(wxGLCanvas* canvas);
~GLCanvas3D();
@ -544,10 +623,8 @@ public:
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
#if ENABLE_GCODE_WINDOW
void start_mapping_gcode_window();
void stop_mapping_gcode_window();
#endif // ENABLE_GCODE_WINDOW
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);
@ -625,7 +702,7 @@ public:
void select_all();
void deselect_all();
void delete_selected();
void ensure_on_bed(unsigned int object_idx);
void ensure_on_bed(unsigned int object_idx, bool allow_negative_z);
bool is_gcode_legend_enabled() const { return m_gcode_viewer.is_legend_enabled(); }
GCodeViewer::EViewType get_gcode_view_type() const { return m_gcode_viewer.get_view_type(); }
@ -638,6 +715,10 @@ public:
void set_toolpath_view_type(GCodeViewer::EViewType type);
void set_volumes_z_range(const std::array<double, 2>& range);
void set_toolpaths_z_range(const std::array<unsigned int, 2>& range);
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<CustomGCode::Item>& get_custom_gcode_per_print_z() { return m_gcode_viewer.get_custom_gcode_per_print_z(); }
size_t get_gcode_extruders_count() { return m_gcode_viewer.get_extruders_count(); }
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
std::vector<int> load_object(const ModelObject& model_object, int obj_idx, std::vector<int> instance_idxs);
std::vector<int> load_object(const Model& model, int obj_idx);
@ -744,6 +825,9 @@ public:
void use_slope(bool use) { m_slope.use(use); }
void set_slope_normal_angle(float angle_in_deg) { m_slope.set_normal_angle(angle_in_deg); }
void highlight_toolbar_item(const std::string& item_name);
void highlight_gizmo(const std::string& gizmo_name);
ArrangeSettings get_arrange_settings() const {
const ArrangeSettings &settings = get_arrange_settings(this);
ArrangeSettings ret = settings;
@ -789,9 +873,9 @@ public:
const Print* fff_print() const;
const SLAPrint* sla_print() const;
#if ENABLE_SCROLLABLE_LEGEND
void reset_old_size() { m_old_size = { 0, 0 }; }
#endif // ENABLE_SCROLLABLE_LEGEND
bool is_object_sinking(int object_idx) const;
private:
bool _is_shown_on_screen() const;
@ -816,6 +900,7 @@ private:
void _rectangular_selection_picking_pass();
void _render_background() const;
void _render_bed(bool bottom, bool show_axes);
void _render_bed_for_picking(bool bottom);
#if ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
void _render_objects(GLVolumeCollection::ERenderType type);
#else

View file

@ -7,6 +7,9 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/Model.hpp"
#if ENABLE_SINKING_CONTOURS
#include "libslic3r/Polygon.hpp"
#endif // ENABLE_SINKING_CONTOURS
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -87,6 +90,35 @@ void GLModel::init_from(const TriangleMesh& mesh)
m_render_data.emplace_back(data);
}
#if ENABLE_SINKING_CONTOURS
void GLModel::init_from(const Polygons& polygons, float z)
{
auto append_polygon = [](const Polygon& polygon, float z, GUI::GLModel::InitializationData& data) {
if (!polygon.empty()) {
GUI::GLModel::InitializationData::Entity entity;
entity.type = GUI::GLModel::PrimitiveType::LineLoop;
// contour
entity.positions.reserve(polygon.size() + 1);
entity.indices.reserve(polygon.size() + 1);
unsigned int id = 0;
for (const Point& p : polygon) {
Vec3f position = unscale(p.x(), p.y(), 0.0).cast<float>();
position.z() = z;
entity.positions.emplace_back(position);
entity.indices.emplace_back(id++);
}
data.entities.emplace_back(entity);
}
};
InitializationData init_data;
for (const Polygon& polygon : polygons) {
append_polygon(polygon, z, init_data);
}
init_from(init_data);
}
#endif // ENABLE_SINKING_CONTOURS
bool GLModel::init_from_file(const std::string& filename)
{
if (!boost::filesystem::exists(filename))

View file

@ -9,6 +9,10 @@
namespace Slic3r {
class TriangleMesh;
#if ENABLE_SINKING_CONTOURS
class Polygon;
using Polygons = std::vector<Polygon>;
#endif // ENABLE_SINKING_CONTOURS
namespace GUI {
@ -58,6 +62,9 @@ namespace GUI {
void init_from(const InitializationData& data);
void init_from(const TriangleMesh& mesh);
#if ENABLE_SINKING_CONTOURS
void init_from(const Polygons& polygons, float z);
#endif // ENABLE_SINKING_CONTOURS
bool init_from_file(const std::string& filename);
// if entity_id == -1 set the color of all entities

View file

@ -50,10 +50,6 @@ std::pair<bool, std::string> GLShadersManager::init()
);
// used to render variable layers heights in 3d editor
valid &= append_shader("variable_layer_height", { "variable_layer_height.vs", "variable_layer_height.fs" });
#if ENABLE_ALLOW_NEGATIVE_Z
// used to render volumes during picking pass
valid &= append_shader("picking", { "picking.vs", "picking.fs" });
#endif // ENABLE_ALLOW_NEGATIVE_Z
return { valid, error };
}

View file

@ -61,6 +61,7 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat
, m_state(Normal)
, m_data(data)
, m_last_action_type(Undefined)
, m_highlight_state(NotHighlighted)
{
}
@ -91,7 +92,8 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b
assert((tex_width != 0) && (tex_height != 0));
GLTexture::Quad_UVs ret;
// tiles in the texture are spaced by 1 pixel
float icon_size_px = (float)(tex_width - 1) / (float)Num_States;
float icon_size_px = (float)(tex_width - 1) / ((float)Num_States + (float)Num_Rendered_Highlight_States);
char render_state = (m_highlight_state == NotHighlighted ? m_state : Num_States + m_highlight_state);
float inv_tex_width = 1.0f / (float)tex_width;
float inv_tex_height = 1.0f / (float)tex_height;
// tiles in the texture are spaced by 1 pixel
@ -99,7 +101,7 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b
float v_offset = 1.0f * inv_tex_height;
float du = icon_size_px * inv_tex_width;
float dv = icon_size_px * inv_tex_height;
float left = u_offset + (float)m_state * du;
float left = u_offset + (float)render_state * du;
float right = left + du - u_offset;
float top = v_offset + (float)m_data.sprite_id * dv;
float bottom = top + dv - v_offset;
@ -183,6 +185,24 @@ bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture)
return res;
}
bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
{
if (m_arrow_texture.texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/icons/";
bool res = false;
if (!arrow_texture.filename.empty())
res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
if (res)
m_arrow_texture.metadata = arrow_texture;
return res;
}
GLToolbar::Layout::EType GLToolbar::get_layout_type() const
{
return m_layout.type;
@ -419,6 +439,8 @@ void GLToolbar::render(const GLCanvas3D& parent)
}
}
bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent)
{
if (!m_enabled)
@ -869,6 +891,21 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D&
}
}
GLToolbarItem* GLToolbar::get_item(const std::string& item_name)
{
if (!m_enabled)
return nullptr;
for (GLToolbarItem* item : m_items)
{
if (item->get_name() == item_name)
{
return item;
}
}
return nullptr;
}
int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const
{
if (!m_enabled)
@ -1105,6 +1142,63 @@ void GLToolbar::render_background(float left, float top, float right, float bott
}
}
void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item)
{
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
float scaled_icons_size = m_layout.icons_size * factor;
float scaled_separator_size = m_layout.separator_size * factor;
float scaled_gap_size = m_layout.gap_size * factor;
float border = m_layout.border * factor;
float separator_stride = scaled_separator_size + scaled_gap_size;
float icon_stride = scaled_icons_size + scaled_gap_size;
float left = m_layout.left;
float top = m_layout.top - icon_stride;
for (const GLToolbarItem* item : m_items) {
if (!item->is_visible())
continue;
if (item->is_separator())
left += separator_stride;
else {
if (item->get_name() == highlighted_item->get_name())
break;
left += icon_stride;
}
}
left += border;
top -= separator_stride;
float right = left + scaled_icons_size;
unsigned int tex_id = m_arrow_texture.texture.get_id();
float tex_width = (float)m_icons_texture.get_width();
float tex_height = (float)m_icons_texture.get_height();
if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) {
float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
float internal_left = left + border - scaled_icons_size / 2; // add half scaled_icons_size for huge arrow
float internal_right = right - border + scaled_icons_size / 2;
float internal_top = top - border;
// bottom is not moving and should be calculated from arrow texture sides ratio
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } });
}
}
void GLToolbar::render_horizontal(const GLCanvas3D& parent)
{
unsigned int tex_id = m_icons_texture.get_id();
@ -1217,6 +1311,8 @@ bool GLToolbar::generate_icons_texture()
states.push_back({ 0, false }); // Hover
states.push_back({ 0, false }); // HoverPressed
states.push_back({ 2, false }); // HoverDisabled
states.push_back({ 0, false }); // HighlightedShown
states.push_back({ 2, false }); // HighlightedHidden
}
else {
states.push_back({ 1, false }); // Normal
@ -1225,6 +1321,8 @@ bool GLToolbar::generate_icons_texture()
states.push_back({ 0, false }); // Hover
states.push_back({ 1, true }); // HoverPressed
states.push_back({ 1, false }); // HoverDisabled
states.push_back({ 0, false }); // HighlightedShown
states.push_back({ 1, false }); // HighlightedHidden
}
unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale);

View file

@ -65,6 +65,14 @@ public:
Num_States
};
enum EHighlightState : unsigned char
{
HighlightedShown,
HighlightedHidden,
Num_Rendered_Highlight_States,
NotHighlighted
};
struct Data
{
struct Option
@ -104,13 +112,16 @@ private:
EState m_state;
Data m_data;
EActionType m_last_action_type;
EHighlightState m_highlight_state;
public:
GLToolbarItem(EType type, const Data& data);
EState get_state() const { return m_state; }
void set_state(EState state) { m_state = state; }
EHighlightState get_highlight() const { return m_highlight_state; }
void set_highlight(EHighlightState state) { m_highlight_state = state; }
const std::string& get_name() const { return m_data.name; }
const std::string& get_icon_filename() const { return m_data.icon_filename; }
const std::string& get_tooltip() const { return m_data.tooltip; }
@ -143,7 +154,6 @@ public:
bool update_enabled_state();
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
private:
void set_visible(bool visible) { m_data.visible = visible; }
@ -236,6 +246,7 @@ private:
GLTexture m_icons_texture;
bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
BackgroundTexture m_arrow_texture;
Layout m_layout;
ItemsList m_items;
@ -262,6 +273,8 @@ public:
bool init(const BackgroundTexture::Metadata& background_texture);
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
Layout::EType get_layout_type() const;
void set_layout_type(Layout::EType type);
Layout::EHorizontalOrientation get_horizontal_orientation() const { return m_layout.horizontal_orientation; }
@ -310,9 +323,11 @@ public:
bool update_items_state();
void render(const GLCanvas3D& parent);
void render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item);
bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent);
// get item pointer for highlighter timer
GLToolbarItem* get_item(const std::string& item_name);
private:
void calc_layout();
float get_width_horizontal() const;

View file

@ -661,6 +661,10 @@ void GUI_App::post_init()
this->mainframe->load_config(this->init_params->extra_config);
}
// show "Did you know" notification
if (app_config->get("show_hints") == "1")
plater_->get_notification_manager()->push_hint_notification();
// The extra CallAfter() is needed because of Mac, where this is the only way
// to popup a modal dialog on start without screwing combo boxes.
// This is ugly but I honestly found no better way to do it.
@ -950,12 +954,10 @@ bool GUI_App::on_init_inner()
else
load_current_presets();
#if ENABLE_PROJECT_DIRTY_STATE
if (plater_ != nullptr) {
plater_->reset_project_dirty_initial_presets();
plater_->update_project_dirty_from_presets();
}
#endif // ENABLE_PROJECT_DIRTY_STATE
mainframe->Show(true);
@ -1780,7 +1782,7 @@ void GUI_App::update_mode()
for (auto tab : tabs_list)
tab->update_mode();
plater()->update_object_menu();
plater()->update_menus();
plater()->canvas3D()->update_gizmos_on_off_state();
}
@ -1848,11 +1850,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
#endif
case ConfigMenuTakeSnapshot:
// Take a configuration snapshot.
#if ENABLE_PROJECT_DIRTY_STATE
if (check_and_save_current_preset_changes()) {
#else
if (check_unsaved_changes()) {
#endif // ENABLE_PROJECT_DIRTY_STATE
wxTextEntryDialog dlg(nullptr, _L("Taking configuration snapshot"), _L("Snapshot name"));
UpdateDlgDarkUI(&dlg);
@ -1868,11 +1866,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
}
break;
case ConfigMenuSnapshots:
#if ENABLE_PROJECT_DIRTY_STATE
if (check_and_save_current_preset_changes()) {
#else
if (check_unsaved_changes()) {
#endif // ENABLE_PROJECT_DIRTY_STATE
std::string on_snapshot;
if (Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
on_snapshot = app_config->get("on_snapshot");
@ -1908,11 +1902,7 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
PreferencesDialog dlg(mainframe);
dlg.ShowModal();
app_layout_changed = dlg.settings_layout_changed();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
if (dlg.seq_top_layer_only_changed())
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
this->plater_->refresh_print();
if (dlg.recreate_GUI()) {
@ -1985,7 +1975,43 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
menu->Append(local_menu, _L("&Configuration"));
}
#if ENABLE_PROJECT_DIRTY_STATE
void GUI_App::open_preferences(size_t open_on_tab)
{
bool app_layout_changed = false;
{
// the dialog needs to be destroyed before the call to recreate_GUI()
// or sometimes the application crashes into wxDialogBase() destructor
// so we put it into an inner scope
PreferencesDialog dlg(mainframe, open_on_tab);
dlg.ShowModal();
app_layout_changed = dlg.settings_layout_changed();
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
#else
if (dlg.seq_top_layer_only_changed())
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
this->plater_->refresh_print();
#ifdef _WIN32
if (is_editor()) {
if (app_config->get("associate_3mf") == "1")
associate_3mf_files();
if (app_config->get("associate_stl") == "1")
associate_stl_files();
}
else {
if (app_config->get("associate_gcode") == "1")
associate_gcode_files();
}
#endif // _WIN32
}
if (app_layout_changed) {
// hide full main_sizer for mainFrame
mainframe->GetSizer()->Show(false);
mainframe->update_layout();
mainframe->select_tab(size_t(0));
}
}
bool GUI_App::has_unsaved_preset_changes() const
{
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
@ -2027,28 +2053,12 @@ std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets(
}
return ret;
}
#endif // ENABLE_PROJECT_DIRTY_STATE
// 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.
#if ENABLE_PROJECT_DIRTY_STATE
bool GUI_App::check_and_save_current_preset_changes(const wxString& header)
{
if (this->plater()->model().objects.empty() && has_current_preset_changes()) {
#else
bool GUI_App::check_unsaved_changes(const wxString &header)
{
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
bool has_unsaved_changes = false;
for (Tab* tab : tabs_list)
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) {
has_unsaved_changes = true;
break;
}
if (has_unsaved_changes) {
#endif // ENABLE_PROJECT_DIRTY_STATE
UnsavedChangesDialog dlg(header);
if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
return false;

View file

@ -238,15 +238,11 @@ public:
void update_mode();
void add_config_menu(wxMenuBar *menu);
#if ENABLE_PROJECT_DIRTY_STATE
bool has_unsaved_preset_changes() const;
bool has_current_preset_changes() const;
void update_saved_preset_from_current_preset();
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
bool check_and_save_current_preset_changes(const wxString& header = wxString());
#else
bool check_unsaved_changes(const wxString& header = wxString());
#endif // ENABLE_PROJECT_DIRTY_STATE
bool check_print_host_queue();
bool checked_tab(Tab* tab);
void load_current_presets(bool check_printer_presets = true);
@ -256,6 +252,8 @@ public:
wxString current_language_code_safe() const;
bool is_localized() const { return m_wxLocale->GetLocale() != "English"; }
void open_preferences(size_t open_on_tab = 0);
virtual bool OnExceptionInMainLoop() override;
#ifdef __APPLE__

View file

@ -964,6 +964,12 @@ void MenuFactory::init(wxWindow* parent)
create_instance_menu();
}
void MenuFactory::update()
{
update_default_menu();
update_object_menu();
}
wxMenu* MenuFactory::default_menu()
{
return &m_default_menu;
@ -1088,6 +1094,14 @@ void MenuFactory::update_object_menu()
append_menu_items_add_volume(&m_object_menu);
}
void MenuFactory::update_default_menu()
{
const auto menu_item_id = m_default_menu.FindItem(_("Add Shape"));
if (menu_item_id != wxNOT_FOUND)
m_default_menu.Destroy(menu_item_id);
create_default_menu();
}
void MenuFactory::msw_rescale()
{
for (MenuWithSeparators* menu : { &m_object_menu, &m_sla_object_menu, &m_part_menu, &m_default_menu })

View file

@ -40,7 +40,9 @@ public:
~MenuFactory() = default;
void init(wxWindow* parent);
void update();
void update_object_menu();
void update_default_menu();
void msw_rescale();
void sys_color_changed();

View file

@ -9,9 +9,7 @@
#include "Plater.hpp"
#include "BitmapComboBox.hpp"
#include "GalleryDialog.hpp"
#if ENABLE_PROJECT_DIRTY_STATE
#include "MainFrame.hpp"
#endif // ENABLE_PROJECT_DIRTY_STATE
#include "OptionsGroup.hpp"
#include "Tab.hpp"
@ -20,6 +18,7 @@
#include "GLCanvas3D.hpp"
#include "Selection.hpp"
#include "format.hpp"
#include "NotificationManager.hpp"
#include <boost/algorithm/string.hpp>
#include <wx/progdlg.h>
@ -1672,9 +1671,7 @@ void ObjectList::load_shape_object(const std::string& type_name)
BoundingBoxf3 bb;
TriangleMesh mesh = create_mesh(type_name, bb);
load_mesh_object(mesh, _L("Shape") + "-" + _(type_name));
#if ENABLE_PROJECT_DIRTY_STATE
wxGetApp().mainframe->update_title();
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void ObjectList::load_shape_object_from_gallery()
@ -1705,13 +1702,9 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files
snapshot_label += ", " + wxString::FromUTF8(paths[i].filename().string().c_str());
take_snapshot(snapshot_label);
#if ENABLE_PROJECT_DIRTY_STATE
std::vector<size_t> res = wxGetApp().plater()->load_files(paths, true, false);
if (!res.empty())
wxGetApp().mainframe->update_title();
#else
load_files(paths, true, false);
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center)
@ -2386,15 +2379,28 @@ void ObjectList::part_selection_changed()
if (type == itInfo) {
InfoItemType info_type = m_objects_model->GetInfoItemType(item);
if (info_type != InfoItemType::VariableLayerHeight) {
switch (info_type)
{
case InfoItemType::VariableLayerHeight:
{
wxGetApp().plater()->toggle_layers_editing(true);
break;
}
case InfoItemType::CustomSupports:
case InfoItemType::CustomSeam:
case InfoItemType::MmuSegmentation:
{
GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports :
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
GLGizmosManager::EType::MmuSegmentation;
info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :
GLGizmosManager::EType::MmuSegmentation;
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
if (gizmos_mgr.get_current_type() != gizmo_type)
gizmos_mgr.open_gizmo(gizmo_type);
} else
wxGetApp().plater()->toggle_layers_editing(true);
break;
}
case InfoItemType::Sinking: { break; }
default: { break; }
}
}
}
else {
@ -2520,6 +2526,7 @@ void ObjectList::update_info_items(size_t obj_idx)
for (InfoItemType type : {InfoItemType::CustomSupports,
InfoItemType::CustomSeam,
InfoItemType::MmuSegmentation,
InfoItemType::Sinking,
InfoItemType::VariableLayerHeight}) {
wxDataViewItem item = m_objects_model->GetInfoItemByType(item_obj, type);
bool shows = item.IsOk();
@ -2542,12 +2549,20 @@ void ObjectList::update_info_items(size_t obj_idx)
should_show = printer_technology() == ptFFF
&& ! model_object->layer_height_profile.empty();
break;
case InfoItemType::Sinking:
{
should_show = printer_technology() == ptFFF &&
wxGetApp().plater()->canvas3D()->is_object_sinking(obj_idx);
break;
}
default: break;
}
if (! shows && should_show) {
m_objects_model->AddInfoChild(item_obj, type);
Expand(item_obj);
wxGetApp().notification_manager()->push_updated_item_info_notification(type);
}
else if (shows && ! should_show) {
Unselect(item);
@ -2685,7 +2700,7 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
if (obj->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(parent);
}
wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx);
wxGetApp().plater()->canvas3D()->ensure_on_bed(item->obj_idx, printer_technology() != ptSLA);
}
else
m_objects_model->Delete(m_objects_model->GetItemByInstanceId(item->obj_idx, item->sub_obj_idx));
@ -4060,7 +4075,7 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
wxGetApp().plater()->canvas3D()->ensure_on_bed(obj_idx);
wxGetApp().plater()->canvas3D()->ensure_on_bed(obj_idx, printer_technology() != ptSLA);
}
// update scene

View file

@ -30,28 +30,9 @@ const double ObjectManipulation::mm_to_in = 0.0393700787;
// volume in world coordinate system.
static double get_volume_min_z(const GLVolume& volume)
{
#if ENABLE_ALLOW_NEGATIVE_Z
return volume.transformed_convex_hull_bounding_box().min.z();
#else
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;
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
static choice_ctrl* create_word_local_combo(wxWindow *parent)
{
wxSize size(15 * wxGetApp().em_unit(), -1);
@ -358,7 +339,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
change_position_value(1, diff.y());
change_position_value(2, diff.z());
}
#if ENABLE_ALLOW_NEGATIVE_Z
else if (selection.is_single_full_instance()) {
const ModelObjectPtrs& objects = wxGetApp().model().objects;
const int idx = selection.get_object_idx();
@ -371,7 +351,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
}
}
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
});
editors_grid_sizer->Add(m_drop_to_bed_button);
@ -526,6 +505,7 @@ void ObjectManipulation::update_ui_from_settings()
#else
editor->SetBackgroundColour(m_use_colors ? wxColour(axes_color_back[axis_id]) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
#endif /* _WIN32 */
editor->Refresh();
if (++axis_id == 3)
axis_id = 0;
}
@ -702,10 +682,7 @@ void ObjectManipulation::update_reset_buttons_visibility()
if (selection.is_single_full_instance()) {
rotation = volume->get_instance_rotation();
scale = volume->get_instance_scaling_factor();
#if ENABLE_ALLOW_NEGATIVE_Z
min_z = wxGetApp().model().objects[volume->composite_id.object_id]->bounding_box().min.z();
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
else {
rotation = volume->get_volume_rotation();
@ -714,11 +691,7 @@ void ObjectManipulation::update_reset_buttons_visibility()
}
show_rotation = !rotation.isApprox(Vec3d::Zero());
show_scale = !scale.isApprox(Vec3d::Ones());
#if ENABLE_ALLOW_NEGATIVE_Z
show_drop_to_bed = std::abs(min_z) > SINKING_Z_THRESHOLD;
#else
show_drop_to_bed = (std::abs(min_z) > EPSILON);
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
wxGetApp().CallAfter([this, show_rotation, show_scale, show_drop_to_bed] {
@ -1122,6 +1095,8 @@ void ManipulationEditor::sys_color_changed(ObjectManipulation* parent)
{
if (!parent->use_colors())
wxGetApp().UpdateDarkUI(this);
else
SetForegroundColour(*wxBLACK);
}
double ManipulationEditor::get_value()

View file

@ -264,9 +264,7 @@ bool Preview::init(wxWindow* parent, Model* model)
get_option_type_string(OptionType::Wipe) + "|0|" +
get_option_type_string(OptionType::Retractions) + "|0|" +
get_option_type_string(OptionType::Unretractions) + "|0|" +
#if ENABLE_SEAMS_VISUALIZATION
get_option_type_string(OptionType::Seams) + "|0|" +
#endif // ENABLE_SEAMS_VISUALIZATION
get_option_type_string(OptionType::ToolChanges) + "|0|" +
get_option_type_string(OptionType::ColorChanges) + "|0|" +
get_option_type_string(OptionType::PausePrints) + "|0|" +
@ -639,11 +637,25 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
update_layers_slider_mode();
Plater* plater = wxGetApp().plater();
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
CustomGCode::Info ticks_info_from_model;
if (wxGetApp().is_editor())
ticks_info_from_model = plater->model().custom_gcode_per_print_z;
else {
ticks_info_from_model.mode = CustomGCode::Mode::SingleExtruder;
ticks_info_from_model.gcodes = m_canvas->get_custom_gcode_per_print_z();
}
#else
CustomGCode::Info& ticks_info_from_model = plater->model().custom_gcode_per_print_z;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
check_layers_slider_values(ticks_info_from_model.gcodes, layers_z);
//first of all update extruder colors to avoid crash, when we are switching printer preset from MM to SM
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config(wxGetApp().is_editor() ? nullptr : m_gcode_result));
#else
m_layers_slider->SetExtruderColors(plater->get_extruder_colors_from_plater_config());
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
m_layers_slider->SetSliderValues(layers_z);
assert(m_layers_slider->GetMinValue() == 0);
@ -701,8 +713,15 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
int i;
for (i = 1; i < int(0.3 * num_layers); ++ i) {
double cur_area = area(object->get_layer(i)->lslices);
if (cur_area != bottom_area && fabs(cur_area - bottom_area) > scale_(scale_(1)))
if (cur_area != bottom_area && fabs(cur_area - bottom_area) > scale_(scale_(1))) {
// but due to the elephant foot compensation, the first layer may be slightly smaller than the others
if (i == 1 && fabs(cur_area - bottom_area) / bottom_area < 0.1) {
// So, let process this case and use second layer as a bottom
bottom_area = cur_area;
continue;
}
break;
}
}
if (i < int(0.3 * num_layers))
continue;
@ -834,25 +853,17 @@ void Preview::update_moves_slider()
return;
std::vector<double> values(view.endpoints.last - view.endpoints.first + 1);
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
std::vector<double> alternate_values(view.endpoints.last - view.endpoints.first + 1);
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
unsigned int count = 0;
for (unsigned int i = view.endpoints.first; i <= view.endpoints.last; ++i) {
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
values[count] = static_cast<double>(i + 1);
if (view.gcode_ids[i] > 0)
alternate_values[count] = static_cast<double>(view.gcode_ids[i]);
++count;
#else
values[count++] = static_cast<double>(i + 1);
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
}
m_moves_slider->SetSliderValues(values);
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_moves_slider->SetSliderAlternateValues(alternate_values);
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_moves_slider->SetMaxValue(view.endpoints.last - view.endpoints.first);
m_moves_slider->SetSelectionSpan(view.current.first - view.endpoints.first, view.current.last - view.endpoints.first);
}
@ -906,6 +917,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
GCodeViewer::EViewType gcode_view_type = m_canvas->get_gcode_view_preview_type();
bool gcode_preview_data_valid = !m_gcode_result->moves.empty();
// Collect colors per extruder.
std::vector<std::string> colors;
std::vector<CustomGCode::Item> color_print_values = {};
@ -914,7 +926,14 @@ void Preview::load_print_as_fff(bool keep_z_range)
colors = wxGetApp().plater()->get_colors_for_color_print(m_gcode_result);
if (!gcode_preview_data_valid) {
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (wxGetApp().is_editor())
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
else
color_print_values = m_canvas->get_custom_gcode_per_print_z();
#else
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
colors.push_back("#808080"); // gray color for pause print or custom G-code
}
}
@ -923,9 +942,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
color_print_values.clear();
}
if (IsShown()) {
std::vector<double> zs;
std::vector<double> zs;
if (IsShown()) {
m_canvas->set_selected_extruder(0);
if (gcode_preview_data_valid) {
// Load the real G-code preview.
@ -936,7 +955,12 @@ void Preview::load_print_as_fff(bool keep_z_range)
Refresh();
zs = m_canvas->get_gcode_layers_zs();
m_loaded = true;
} else {
}
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
else if (wxGetApp().is_editor()) {
#else
else {
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
// Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values);
m_left_sizer->Hide(m_bottom_toolbar_panel);
@ -944,6 +968,33 @@ void Preview::load_print_as_fff(bool keep_z_range)
Refresh();
zs = m_canvas->get_volumes_print_zs(true);
}
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (!zs.empty() && !m_keep_current_preview_type) {
unsigned int number_extruders = wxGetApp().is_editor() ?
(unsigned int)print->extruders().size() :
m_canvas->get_gcode_extruders_count();
std::vector<Item> gcodes = wxGetApp().is_editor() ?
wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes :
m_canvas->get_custom_gcode_per_print_z();
const wxString choice = !gcodes.empty() ?
_L("Color Print") :
(number_extruders > 1) ? _L("Tool") : _L("Feature type");
int type = m_choice_view_type->FindString(choice);
if (m_choice_view_type->GetSelection() != type) {
if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) {
m_choice_view_type->SetSelection(type);
m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type));
if (wxGetApp().is_gcode_viewer()) {
m_keep_current_preview_type = true;
refresh_print();
}
}
}
}
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (zs.empty()) {
// all layers filtered out
hide_layers_slider();
@ -952,9 +1003,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
update_layers_slider(zs, keep_z_range);
}
unsigned int number_extruders = (unsigned int)print->extruders().size();
#if !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (!m_keep_current_preview_type) {
unsigned int number_extruders = (unsigned int)print->extruders().size();
const wxString choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes.empty() ?
_L("Color Print") :
(number_extruders > 1) ? _L("Tool") : _L("Feature type");
@ -967,6 +1018,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
}
}
}
#endif // !ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
}
void Preview::load_print_as_sla()
@ -1041,9 +1093,7 @@ wxString Preview::get_option_type_string(OptionType type) const
case OptionType::Wipe: { return _L("Wipe"); }
case OptionType::Retractions: { return _L("Retractions"); }
case OptionType::Unretractions: { return _L("Deretractions"); }
#if ENABLE_SEAMS_VISUALIZATION
case OptionType::Seams: { return _L("Seams"); }
#endif // ENABLE_SEAMS_VISUALIZATION
case OptionType::ToolChanges: { return _L("Tool changes"); }
case OptionType::ColorChanges: { return _L("Color changes"); }
case OptionType::PausePrints: { return _L("Print pauses"); }

View file

@ -121,9 +121,7 @@ public:
Wipe,
Retractions,
Unretractions,
#if ENABLE_SEAMS_VISUALIZATION
Seams,
#endif // ENABLE_SEAMS_VISUALIZATION
ToolChanges,
ColorChanges,
PausePrints,

View file

@ -219,9 +219,7 @@ static void add_default_image(wxImageList* img_list, bool is_system)
static fs::path get_dir(bool sys_dir)
{
if (sys_dir)
return fs::absolute(fs::path(sys_shapes_dir())).make_preferred();
return fs::absolute(fs::path(data_dir()) / "shapes").make_preferred();
return fs::absolute(fs::path(sys_dir ? sys_shapes_dir() : custom_shapes_dir())).make_preferred();
}
static std::string get_dir_path(bool sys_dir)

View file

@ -154,8 +154,8 @@ public:
void update(const UpdateData& data);
void render() const { m_tooltip.clear(); on_render(); }
void render_for_picking() const { on_render_for_picking(); }
void render() { m_tooltip.clear(); on_render(); }
void render_for_picking() { on_render_for_picking(); }
void render_input_window(float x, float y, float bottom_limit);
virtual std::string get_tooltip() const { return ""; }
@ -175,8 +175,8 @@ protected:
virtual void on_start_dragging() {}
virtual void on_stop_dragging() {}
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() = 0;
virtual void on_render_for_picking() = 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

View file

@ -16,7 +16,9 @@
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Model.hpp"
#if ENABLE_SINKING_CONTOURS
#include "libslic3r/TriangleMeshSlicer.hpp"
#endif // ENABLE_SINKING_CONTOURS
namespace Slic3r {
namespace GUI {
@ -82,14 +84,18 @@ void GLGizmoCut::on_update(const UpdateData& data)
set_cut_z(m_start_z + calc_projection(data.mouse_ray));
}
void GLGizmoCut::on_render() const
void GLGizmoCut::on_render()
{
BoundingBoxf3 box = bounding_box();
const BoundingBoxf3 box = bounding_box();
Vec3d plane_center = box.center();
plane_center.z() = m_cut_z;
m_max_z = box.max.z();
set_cut_z(m_cut_z);
#if ENABLE_SINKING_CONTOURS
update_contours();
#endif // ENABLE_SINKING_CONTOURS
const float min_x = box.min.x() - Margin;
const float max_x = box.max.x() + Margin;
const float min_y = box.min.y() - Margin;
@ -136,9 +142,17 @@ void GLGizmoCut::on_render() const
m_grabbers[0].render(m_hover_id == 0, (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0));
shader->stop_using();
#if ENABLE_SINKING_CONTOURS
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_cut_contours.shift.x(), m_cut_contours.shift.y(), m_cut_contours.shift.z()));
glsafe(::glLineWidth(2.0f));
m_cut_contours.contours.render();
glsafe(::glPopMatrix());
#endif // ENABLE_SINKING_CONTOURS
}
void GLGizmoCut::on_render_for_picking() const
void GLGizmoCut::on_render_for_picking()
{
glsafe(::glDisable(GL_DEPTH_TEST));
render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
@ -199,7 +213,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
perform_cut(m_parent.get_selection());
}
void GLGizmoCut::set_cut_z(double cut_z) const
void GLGizmoCut::set_cut_z(double cut_z)
{
// Clamp the plane to the object's bounding box
m_cut_z = std::clamp(cut_z, 0.0, m_max_z);
@ -261,5 +275,47 @@ BoundingBoxf3 GLGizmoCut::bounding_box() const
return ret;
}
#if ENABLE_SINKING_CONTOURS
void GLGizmoCut::update_contours()
{
const Selection& selection = m_parent.get_selection();
const GLVolume* first_glvolume = selection.get_volume(*selection.get_volume_idxs().begin());
const BoundingBoxf3& box = first_glvolume->transformed_convex_hull_bounding_box();
const int object_idx = selection.get_object_idx();
const int instance_idx = selection.get_instance_idx();
if (0.0 < m_cut_z && m_cut_z < m_max_z) {
if (m_cut_contours.cut_z != m_cut_z || m_cut_contours.object_idx != object_idx || m_cut_contours.instance_idx != instance_idx) {
m_cut_contours.cut_z = m_cut_z;
if (m_cut_contours.object_idx != object_idx) {
m_cut_contours.mesh = wxGetApp().plater()->model().objects[object_idx]->raw_mesh();
m_cut_contours.mesh.repair();
}
m_cut_contours.position = box.center();
m_cut_contours.shift = Vec3d::Zero();
m_cut_contours.object_idx = object_idx;
m_cut_contours.instance_idx = instance_idx;
m_cut_contours.contours.reset();
MeshSlicingParams slicing_params;
slicing_params.trafo = first_glvolume->get_instance_transformation().get_matrix();
const Polygons polys = slice_mesh(m_cut_contours.mesh.its, m_cut_z, slicing_params);
if (!polys.empty()) {
m_cut_contours.contours.init_from(polys, static_cast<float>(m_cut_z));
m_cut_contours.contours.set_color(-1, { 1.0f, 1.0f, 1.0f, 1.0f });
}
}
else if (box.center() != m_cut_contours.position) {
m_cut_contours.shift = box.center() - m_cut_contours.position;
}
}
else
m_cut_contours.contours.reset();
}
#endif // ENABLE_SINKING_CONTOURS
} // namespace GUI
} // namespace Slic3r

View file

@ -2,7 +2,10 @@
#define slic3r_GLGizmoCut_hpp_
#include "GLGizmoBase.hpp"
#if ENABLE_SINKING_CONTOURS
#include "slic3r/GUI/GLModel.hpp"
#include "libslic3r/TriangleMesh.hpp"
#endif // ENABLE_SINKING_CONTOURS
namespace Slic3r {
namespace GUI {
@ -13,8 +16,8 @@ class GLGizmoCut : public GLGizmoBase
static const double Margin;
static const std::array<float, 4> GrabberColor;
mutable double m_cut_z{ 0.0 };
mutable double m_max_z{ 0.0 };
double m_cut_z{ 0.0 };
double m_max_z{ 0.0 };
double m_start_z{ 0.0 };
Vec3d m_drag_pos;
Vec3d m_drag_center;
@ -22,11 +25,26 @@ class GLGizmoCut : public GLGizmoBase
bool m_keep_lower{ true };
bool m_rotate_lower{ false };
#if ENABLE_SINKING_CONTOURS
struct CutContours
{
TriangleMesh mesh;
GLModel contours;
double cut_z{ 0.0 };
Vec3d position{ Vec3d::Zero() };
Vec3d shift{ Vec3d::Zero() };
int object_idx{ -1 };
int instance_idx{ -1 };
};
CutContours m_cut_contours;
#endif // ENABLE_SINKING_CONTOURS
public:
GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
double get_cut_z() const { return m_cut_z; }
void set_cut_z(double cut_z) const;
void set_cut_z(double cut_z);
std::string get_tooltip() const override;
@ -39,14 +57,17 @@ protected:
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_render() const override;
virtual void on_render_for_picking() const override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
private:
void perform_cut(const Selection& selection);
double calc_projection(const Linef3& mouse_ray) const;
BoundingBoxf3 bounding_box() const;
#if ENABLE_SINKING_CONTOURS
void update_contours();
#endif // ENABLE_SINKING_CONTOURS
};
} // namespace GUI

View file

@ -152,6 +152,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
if (m_imgui->button(m_desc["enforce_button"], buttons_width, 0.f)) {
select_facets_by_angle(m_angle_threshold_deg, false);
m_angle_threshold_deg = 0.f;
m_parent.use_slope(false);
}
ImGui::SameLine(window_width - buttons_width);
if (m_imgui->button(m_desc["cancel"], buttons_width, 0.f)) {
@ -185,7 +186,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(cursor_slider_left);
ImGui::PushItemWidth(window_width - cursor_slider_left);
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
m_imgui->slider_float(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
@ -193,8 +194,6 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
// Manually inserted values aren't clamped by ImGui. Zero cursor size results in a crash.
m_cursor_radius = std::clamp(m_cursor_radius, CursorRadiusMin, CursorRadiusMax);
ImGui::AlignTextToFramePadding();
@ -251,7 +250,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left);
auto clp_dist = float(m_c->object_clipper()->get_position());
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) {
@ -347,7 +346,8 @@ void GLGizmoFdmSupports::update_from_model_object()
const TriangleMesh* mesh = &mv->mesh();
m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
m_triangle_selectors.back()->deserialize(mv->supported_facets.get_data());
// Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize().
m_triangle_selectors.back()->deserialize(mv->supported_facets.get_data(), false);
m_triangle_selectors.back()->request_update_render_data();
}
}

View file

@ -54,7 +54,7 @@ void GLGizmoFlatten::on_start_dragging()
}
}
void GLGizmoFlatten::on_render() const
void GLGizmoFlatten::on_render()
{
const Selection& selection = m_parent.get_selection();
@ -69,7 +69,7 @@ void GLGizmoFlatten::on_render() const
glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i) {
if (i == m_hover_id)
glsafe(::glColor4f(0.9f, 0.9f, 0.9f, 0.75f));
@ -86,7 +86,7 @@ void GLGizmoFlatten::on_render() const
glsafe(::glDisable(GL_BLEND));
}
void GLGizmoFlatten::on_render_for_picking() const
void GLGizmoFlatten::on_render_for_picking()
{
const Selection& selection = m_parent.get_selection();
@ -99,7 +99,7 @@ void GLGizmoFlatten::on_render_for_picking() const
glsafe(::glTranslatef(0.f, 0.f, selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z()));
glsafe(::glMultMatrixd(m.data()));
if (this->is_plane_update_necessary())
const_cast<GLGizmoFlatten*>(this)->update_planes();
update_planes();
for (int i = 0; i < (int)m_planes.size(); ++i) {
glsafe(::glColor4fv(picking_color_component(i).data()));
m_planes[i].vbo.render();

View file

@ -53,8 +53,8 @@ protected:
virtual std::string on_get_name() const override;
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_render() const override;
virtual void on_render_for_picking() const override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
virtual void on_set_state() override;
virtual CommonGizmosDataID on_get_requirements() const override;
};

View file

@ -61,7 +61,7 @@ void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&)
void GLGizmoHollow::on_render() const
void GLGizmoHollow::on_render()
{
const Selection& selection = m_parent.get_selection();
const CommonGizmosDataObjects::SelectionInfo* sel_info = m_c->selection_info();
@ -88,12 +88,12 @@ void GLGizmoHollow::on_render() const
}
void GLGizmoHollow::on_render_for_picking() const
void GLGizmoHollow::on_render_for_picking()
{
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
//#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);
@ -545,7 +545,7 @@ RENDER_AGAIN:
m_imgui->text(m_desc.at("offset"));
ImGui::SameLine(settings_sliders_left);
ImGui::PushItemWidth(window_width - settings_sliders_left);
ImGui::SliderFloat(" ", &offset, offset_min, offset_max, "%.1f mm");
m_imgui->slider_float(" ", &offset, offset_min, offset_max, "%.1f mm");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
@ -560,7 +560,7 @@ RENDER_AGAIN:
if (current_mode >= quality_mode) {
m_imgui->text(m_desc.at("quality"));
ImGui::SameLine(settings_sliders_left);
ImGui::SliderFloat(" ", &quality, quality_min, quality_max, "%.1f");
m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
@ -576,7 +576,7 @@ RENDER_AGAIN:
if (current_mode >= closing_d_mode) {
m_imgui->text(m_desc.at("closing_distance"));
ImGui::SameLine(settings_sliders_left);
ImGui::SliderFloat(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
@ -618,15 +618,19 @@ RENDER_AGAIN:
ImGui::Separator();
float diameter_upper_cap = 15.;
if (m_new_hole_radius > diameter_upper_cap)
m_new_hole_radius = diameter_upper_cap;
float diameter_upper_cap = 60.;
if (m_new_hole_radius * 2.f > diameter_upper_cap)
m_new_hole_radius = diameter_upper_cap / 2.f;
m_imgui->text(m_desc.at("hole_diameter"));
ImGui::SameLine(diameter_slider_left);
ImGui::PushItemWidth(window_width - diameter_slider_left);
float diam = 2.f * m_new_hole_radius;
ImGui::SliderFloat("", &diam, 1.f, diameter_upper_cap, "%.1f mm");
m_imgui->slider_float("", &diam, 1.f, 15.f, "%.1f mm", 1.f, false);
// Let's clamp the value (which could have been entered by keyboard) to a larger range
// than the slider. This allows entering off-scale values and still protects against
//complete non-sense.
diam = std::clamp(diam, 0.1f, diameter_upper_cap);
m_new_hole_radius = diam / 2.f;
bool clicked = ImGui::IsItemClicked();
bool edited = ImGui::IsItemEdited();
@ -634,7 +638,9 @@ RENDER_AGAIN:
m_imgui->text(m_desc["hole_depth"]);
ImGui::SameLine(diameter_slider_left);
ImGui::SliderFloat(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm");
m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false);
// Same as above:
m_new_hole_height = std::clamp(m_new_hole_height, 0.f, 100.f);
clicked |= ImGui::IsItemClicked();
edited |= ImGui::IsItemEdited();
@ -699,7 +705,7 @@ RENDER_AGAIN:
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left);
float clp_dist = m_c->object_clipper()->get_position();
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
// make sure supports are shown/hidden as appropriate

View file

@ -39,8 +39,8 @@ public:
private:
bool on_init() override;
void on_update(const UpdateData& data) override;
void on_render() const override;
void on_render_for_picking() const override;
void on_render() override;
void on_render_for_picking() override;
void render_points(const Selection& selection, bool picking = false) const;
void hollow_mesh(bool postpone_error_messages = false);

View file

@ -368,8 +368,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
// Manually inserted values aren't clamped by ImGui. Zero cursor size results in a crash.
m_cursor_radius = std::clamp(m_cursor_radius, CursorRadiusMin, CursorRadiusMax);
ImGui::Separator();
@ -427,7 +425,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - sliders_width);
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
m_imgui->slider_float(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
@ -480,7 +478,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
ImGui::SameLine(sliders_width);
ImGui::PushItemWidth(window_width - sliders_width);
auto clp_dist = float(m_c->object_clipper()->get_position());
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
@ -546,7 +544,8 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors()
int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0;
m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorMmGui>(*mesh, m_modified_extruders_colors, m_original_extruders_colors[size_t(extruder_idx)]));
m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data());
// Reset of TriangleSelector is done inside TriangleSelectorMmGUI's constructor, so we don't need it to perform it again in deserialize().
m_triangle_selectors.back()->deserialize(mv->mmu_segmentation_facets.get_data(), false);
m_triangle_selectors.back()->request_update_render_data();
}
m_original_volumes_extruder_idxs = get_extruder_id_for_volumes(*mo);

View file

@ -87,7 +87,7 @@ void GLGizmoMove3D::on_update(const UpdateData& data)
m_displacement.z() = calc_projection(data);
}
void GLGizmoMove3D::on_render() const
void GLGizmoMove3D::on_render()
{
const Selection& selection = m_parent.get_selection();
@ -151,7 +151,7 @@ void GLGizmoMove3D::on_render() const
}
}
void GLGizmoMove3D::on_render_for_picking() const
void GLGizmoMove3D::on_render_for_picking()
{
glsafe(::glDisable(GL_DEPTH_TEST));

View file

@ -39,8 +39,8 @@ protected:
virtual void on_start_dragging() override;
virtual void on_stop_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_render() const override;
virtual void on_render_for_picking() const override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
private:
double calc_projection(const UpdateData& data) const;

View file

@ -31,7 +31,6 @@ GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& ic
#if ENABLE_PROJECT_DIRTY_STATE
// port of 948bc382655993721d93d3b9fce9b0186fcfb211
void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
{
@ -66,29 +65,6 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
m_internal_stack_active = false;
}
}
#else
void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
{
if (activate && ! m_internal_stack_active) {
wxString str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS
? _L("Entering Paint-on supports")
: (get_painter_type() == PainterGizmoType::MMU_SEGMENTATION ? _L("Entering MMU segmentation") : _L("Entering Seam painting"));
Plater::TakeSnapshot(wxGetApp().plater(), str);
wxGetApp().plater()->enter_gizmos_stack();
m_internal_stack_active = true;
}
if (! activate && m_internal_stack_active) {
wxString str = get_painter_type() == PainterGizmoType::SEAM
? _L("Leaving Seam painting")
: (get_painter_type() == PainterGizmoType::MMU_SEGMENTATION ? _L("Leaving MMU segmentation") : _L("Leaving Paint-on supports"));
wxGetApp().plater()->leave_gizmos_stack();
Plater::TakeSnapshot(wxGetApp().plater(), str);
m_internal_stack_active = false;
}
}
#endif // ENABLE_PROJECT_DIRTY_STATE
void GLGizmoPainterBase::set_painter_gizmo_data(const Selection& selection)
{

View file

@ -66,8 +66,8 @@ class GLGizmoPainterBase : public GLGizmoBase
private:
ObjectID m_old_mo_id;
size_t m_old_volumes_size = 0;
void on_render() const override {}
void on_render_for_picking() const override {}
void on_render() override {}
void on_render_for_picking() override {}
public:
GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);

View file

@ -125,7 +125,7 @@ void GLGizmoRotate::on_update(const UpdateData& data)
m_angle = theta;
}
void GLGizmoRotate::on_render() const
void GLGizmoRotate::on_render()
{
if (!m_grabbers[0].enabled)
return;
@ -169,7 +169,7 @@ void GLGizmoRotate::on_render() const
glsafe(::glPopMatrix());
}
void GLGizmoRotate::on_render_for_picking() const
void GLGizmoRotate::on_render_for_picking()
{
const Selection& selection = m_parent.get_selection();
@ -483,17 +483,17 @@ void GLGizmoRotate3D::on_stop_dragging()
m_gizmos[m_hover_id].stop_dragging();
}
void GLGizmoRotate3D::on_render() const
void GLGizmoRotate3D::on_render()
{
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
if ((m_hover_id == -1) || (m_hover_id == 0))
if (m_hover_id == -1 || m_hover_id == 0)
m_gizmos[X].render();
if ((m_hover_id == -1) || (m_hover_id == 1))
if (m_hover_id == -1 || m_hover_id == 1)
m_gizmos[Y].render();
if ((m_hover_id == -1) || (m_hover_id == 2))
if (m_hover_id == -1 || m_hover_id == 2)
m_gizmos[Z].render();
}

View file

@ -55,8 +55,8 @@ protected:
std::string on_get_name() const override { return ""; }
void on_start_dragging() override;
void on_update(const UpdateData& data) override;
void on_render() const override;
void on_render_for_picking() const override;
void on_render() override;
void on_render_for_picking() override;
private:
void render_circle() const;
@ -124,10 +124,10 @@ protected:
g.update(data);
}
}
void on_render() const override;
void on_render_for_picking() const override
void on_render() override;
void on_render_for_picking() override
{
for (const GLGizmoRotate& g : m_gizmos) {
for (GLGizmoRotate& g : m_gizmos) {
g.render_for_picking();
}
}

View file

@ -115,7 +115,7 @@ void GLGizmoScale3D::on_update(const UpdateData& data)
do_scale_uniform(data);
}
void GLGizmoScale3D::on_render() const
void GLGizmoScale3D::on_render()
{
const Selection& selection = m_parent.get_selection();
@ -294,7 +294,7 @@ void GLGizmoScale3D::on_render() const
}
}
void GLGizmoScale3D::on_render_for_picking() const
void GLGizmoScale3D::on_render_for_picking()
{
glsafe(::glDisable(GL_DEPTH_TEST));
render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());

View file

@ -52,8 +52,8 @@ protected:
virtual bool on_is_activable() const override;
virtual void on_start_dragging() override;
virtual void on_update(const UpdateData& data) override;
virtual void on_render() const override;
virtual void on_render_for_picking() const override;
virtual void on_render() override;
virtual void on_render_for_picking() override;
private:
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;

View file

@ -140,7 +140,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(cursor_size_slider_left);
ImGui::PushItemWidth(window_width - cursor_size_slider_left);
ImGui::SliderFloat(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
m_imgui->slider_float(" ", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f");
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(max_tooltip_width);
@ -148,9 +148,6 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
// Manually inserted values aren't clamped by ImGui. Zero cursor size results in a crash.
m_cursor_radius = std::clamp(m_cursor_radius, CursorRadiusMin, CursorRadiusMax);
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_type"));
@ -203,7 +200,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left);
auto clp_dist = float(m_c->object_clipper()->get_position());
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
if (ImGui::IsItemHovered()) {
@ -259,7 +256,8 @@ void GLGizmoSeam::update_from_model_object()
const TriangleMesh* mesh = &mv->mesh();
m_triangle_selectors.emplace_back(std::make_unique<TriangleSelectorGUI>(*mesh));
m_triangle_selectors.back()->deserialize(mv->seam_facets.get_data());
// Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize().
m_triangle_selectors.back()->deserialize(mv->seam_facets.get_data(), false);
m_triangle_selectors.back()->request_update_render_data();
}
}

View file

@ -74,7 +74,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S
void GLGizmoSlaSupports::on_render() const
void GLGizmoSlaSupports::on_render()
{
ModelObject* mo = m_c->selection_info()->model_object();
const Selection& selection = m_parent.get_selection();
@ -101,7 +101,7 @@ void GLGizmoSlaSupports::on_render() const
}
void GLGizmoSlaSupports::on_render_for_picking() const
void GLGizmoSlaSupports::on_render_for_picking()
{
const Selection& selection = m_parent.get_selection();
//glsafe(::glEnable(GL_DEPTH_TEST));
@ -671,7 +671,7 @@ RENDER_AGAIN:
// - 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");
m_imgui->slider_float("", &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;
@ -731,7 +731,7 @@ RENDER_AGAIN:
float density = static_cast<const ConfigOptionInt*>(opts[0])->value;
float minimal_point_distance = static_cast<const ConfigOptionFloat*>(opts[1])->value;
ImGui::SliderFloat("", &minimal_point_distance, 0.f, 20.f, "%.f mm");
m_imgui->slider_float("", &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
@ -740,7 +740,7 @@ RENDER_AGAIN:
m_imgui->text(m_desc.at("points_density"));
ImGui::SameLine(settings_sliders_left);
ImGui::SliderFloat(" ", &density, 0.f, 200.f, "%.f %%");
m_imgui->slider_float(" ", &density, 0.f, 200.f, "%.f %%");
slider_clicked |= ImGui::IsItemClicked();
slider_edited |= ImGui::IsItemEdited();
slider_released |= ImGui::IsItemDeactivatedAfterEdit();
@ -801,7 +801,7 @@ RENDER_AGAIN:
ImGui::SameLine(clipping_slider_left);
ImGui::PushItemWidth(window_width - clipping_slider_left);
float clp_dist = m_c->object_clipper()->get_position();
if (ImGui::SliderFloat(" ", &clp_dist, 0.f, 1.f, "%.2f"))
if (m_imgui->slider_float(" ", &clp_dist, 0.f, 1.f, "%.2f"))
m_c->object_clipper()->set_position(clp_dist, true);
@ -890,11 +890,7 @@ void GLGizmoSlaSupports::on_set_state()
// data are not yet available, the CallAfter will postpone taking the
// snapshot until they are. No, it does not feel right.
wxGetApp().CallAfter([]() {
#if ENABLE_PROJECT_DIRTY_STATE
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo"));
#else
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned on"));
#endif // ENABLE_PROJECT_DIRTY_STATE
});
}
@ -922,11 +918,7 @@ void GLGizmoSlaSupports::on_set_state()
else {
// we are actually shutting down
disable_editing_mode(); // so it is not active next time the gizmo opens
#if ENABLE_PROJECT_DIRTY_STATE
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo"));
#else
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("SLA gizmo turned off"));
#endif // ENABLE_PROJECT_DIRTY_STATE
m_normal_cache.clear();
m_old_mo_id = -1;
}

View file

@ -70,8 +70,8 @@ public:
private:
bool on_init() override;
void on_update(const UpdateData& data) override;
void on_render() const override;
void on_render_for_picking() const override;
void on_render() override;
void on_render_for_picking() override;
void render_points(const Selection& selection, bool picking = false) const;
bool unsaved_changes() const;

View file

@ -125,10 +125,28 @@ bool GLGizmosManager::init()
m_current = Undefined;
m_hover = Undefined;
m_highlight = std::pair<EType, bool>(Undefined, false);
return true;
}
bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
{
if (m_arrow_texture.texture.get_id() != 0)
return true;
std::string path = resources_dir() + "/icons/";
bool res = false;
if (!arrow_texture.filename.empty())
res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
if (res)
m_arrow_texture.metadata = arrow_texture;
return res;
}
void GLGizmosManager::set_overlay_icon_size(float size)
{
if (m_layout.icons_size != size)
@ -975,6 +993,46 @@ void GLGizmosManager::render_background(float left, float top, float right, floa
}
}
void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_type) const
{
std::vector<size_t> selectable_idxs = get_selectable_idxs();
if (selectable_idxs.empty())
return;
float cnv_w = (float)m_parent.get_canvas_size().get_width();
float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom();
float height = get_scaled_total_height();
float zoomed_border = m_layout.scaled_border() * inv_zoom;
float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom;
float zoomed_top_y = (0.5f * height) * inv_zoom;
zoomed_top_x += zoomed_border;
zoomed_top_y -= zoomed_border;
float icons_size = m_layout.scaled_icons_size();
float zoomed_icons_size = icons_size * inv_zoom;
float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom;
for (size_t idx : selectable_idxs)
{
if (idx == highlighted_type) {
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
unsigned int tex_id = m_arrow_texture.texture.get_id();
float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;
float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height;
float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height;
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_top_uv }, { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv } });
break;
}
zoomed_top_y -= zoomed_stride_y;
}
}
void GLGizmosManager::do_render_overlay() const
{
std::vector<size_t> selectable_idxs = get_selectable_idxs();
@ -1014,7 +1072,7 @@ void GLGizmosManager::do_render_overlay() const
if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1))
return;
float du = (float)(tex_width - 1) / (4.0f * (float)tex_width); // 4 is the number of possible states if the icons
float du = (float)(tex_width - 1) / (6.0f * (float)tex_width); // 6 is the number of possible states if the icons
float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height);
// tiles in the texture are spaced by 1 pixel
@ -1026,9 +1084,9 @@ void GLGizmosManager::do_render_overlay() const
for (size_t idx : selectable_idxs)
{
GLGizmoBase* gizmo = m_gizmos[idx].get();
unsigned int sprite_id = gizmo->get_sprite_id();
int icon_idx = (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3));
// higlighted state needs to be decided first so its highlighting in every other state
int icon_idx = (m_highlight.first == idx ? (m_highlight.second ? 4 : 5) : (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3)));
float v_top = v_offset + sprite_id * dv;
float u_left = u_offset + icon_idx * du;
@ -1062,13 +1120,26 @@ GLGizmoBase* GLGizmosManager::get_current() const
return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get();
}
GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& gizmo_name) const
{
std::vector<size_t> selectable_idxs = get_selectable_idxs();
for (size_t idx = 0; idx < selectable_idxs.size(); ++idx)
{
std::string filename = m_gizmos[selectable_idxs[idx]]->get_icon_filename();
filename = filename.substr(0, filename.find_first_of('.'));
if (filename == gizmo_name)
return (GLGizmosManager::EType)selectable_idxs[idx];
}
return GLGizmosManager::EType::Undefined;
}
bool GLGizmosManager::generate_icons_texture() const
{
std::string path = resources_dir() + "/icons/";
std::vector<std::string> filenames;
for (size_t idx=0; idx<m_gizmos.size(); ++idx)
{
if (m_gizmos[idx] != nullptr)
if (m_gizmos[idx] != nullptr)
{
const std::string& icon_filename = m_gizmos[idx]->get_icon_filename();
if (!icon_filename.empty())
@ -1081,6 +1152,8 @@ bool GLGizmosManager::generate_icons_texture() const
states.push_back(std::make_pair(0, false)); // Hovered
states.push_back(std::make_pair(0, true)); // Selected
states.push_back(std::make_pair(2, false)); // Disabled
states.push_back(std::make_pair(0, false)); // HighlightedShown
states.push_back(std::make_pair(2, false)); // HighlightedHidden
unsigned int sprite_size_px = (unsigned int)m_layout.scaled_icons_size();
// // force even size

View file

@ -95,9 +95,11 @@ private:
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
BackgroundTexture m_arrow_texture;
Layout m_layout;
EType m_current;
EType m_hover;
std::pair<EType, bool> m_highlight; // bool true = higlightedShown, false = highlightedHidden
std::vector<size_t> get_selectable_idxs() const;
std::vector<size_t> get_activable_idxs() const;
@ -129,6 +131,8 @@ public:
bool init();
bool init_arrow(const BackgroundTexture::Metadata& arrow_texture);
template<class Archive>
void load(Archive& ar)
{
@ -183,6 +187,7 @@ public:
EType get_current_type() const { return m_current; }
GLGizmoBase* get_current() const;
EType get_gizmo_from_name(const std::string& gizmo_name) const;
bool is_running() const;
bool handle_shortcut(int key);
@ -221,6 +226,8 @@ public:
void render_overlay() const;
void render_arrow(const GLCanvas3D& parent, EType highlighted_type) const;
std::string get_tooltip() const;
bool on_mouse(wxMouseEvent& evt);
@ -233,8 +240,13 @@ public:
int get_selectable_icons_cnt() const { return get_selectable_idxs().size(); }
int get_shortcut_key(GLGizmosManager::EType) const;
// To end highlight set gizmo = undefined
void set_highlight(EType gizmo, bool highlight_shown) { m_highlight = std::pair<EType, bool>(gizmo, highlight_shown); }
bool get_highlight_state() const { return m_highlight.second; }
private:
void render_background(float left, float top, float right, float bottom, float border) const;
void do_render_overlay() const;
float get_scaled_total_height() const;

View file

@ -0,0 +1,673 @@
#include "HintNotification.hpp"
#include "ImGuiWrapper.hpp"
#include "format.hpp"
#include "I18N.hpp"
#include "GUI_ObjectList.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/PresetBundle.hpp"
#include <boost/filesystem.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <map>
namespace Slic3r {
namespace GUI {
const std::string BOLD_MARKER_START = "<b>";
const std::string BOLD_MARKER_END = "</b>";
const std::string HYPERTEXT_MARKER_START = "<a>";
const std::string HYPERTEXT_MARKER_END = "</a>";
namespace {
inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
{
if (fading_out)
ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
else
ImGui::PushStyleColor(idx, col);
}
// return true if NOT in disabled mode.
inline bool disabled_modes_check(const std::string& disabled_modes)
{
if (disabled_modes.empty())
return true;
// simple / advanced / expert
ConfigOptionMode config_mode = wxGetApp().get_mode();
std::string mode_name;
if (config_mode == ConfigOptionMode::comSimple) mode_name = "simple";
else if (config_mode == ConfigOptionMode::comAdvanced) mode_name = "advanced";
else if (config_mode == ConfigOptionMode::comExpert) mode_name = "expert";
if (!mode_name.empty() && disabled_modes.find(mode_name) != std::string::npos)
return false;
// fff / sla
const PrinterTechnology tech = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
if (tech == ptFFF) {
if (disabled_modes.find("FFF") != std::string::npos)
return false;
} else {
if (disabled_modes.find("SLA") != std::string::npos)
return false;
}
return true;
}
} //namespace
void HintDatabase::init()
{
load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini"));
const AppConfig* app_config = wxGetApp().app_config;
m_hint_id = std::atoi(app_config->get("last_hint").c_str());
m_initialized = true;
}
void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
{
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(path.string());
try {
pt::read_ini(ifs, tree);
}
catch (const boost::property_tree::ini_parser::ini_parser_error& err) {
throw Slic3r::RuntimeError(format("Failed loading hints file \"%1%\"\nError: \"%2%\" at line %3%", path, err.message(), err.line()).c_str());
}
for (const auto& section : tree) {
if (boost::starts_with(section.first, "hint:")) {
// create std::map with tree data
std::map<std::string, std::string> dict;
for (const auto& data : section.second) {
dict.emplace(data.first, data.second.data());
}
//unescaping a translating all texts
//unescape text1
std::string fulltext;
std::string text1;
std::string hypertext_text;
std::string follow_text;
std::string disabled_modes;
unescape_string_cstyle(_utf8(dict["text"]), fulltext);
// replace <b> and </b> for imgui markers
std::string marker_s(1, ImGui::ColorMarkerStart);
std::string marker_e(1, ImGui::ColorMarkerEnd);
// start marker
size_t marker_pos = fulltext.find(BOLD_MARKER_START);
while (marker_pos != std::string::npos) {
fulltext.replace(marker_pos, 3, marker_s);
marker_pos = fulltext.find(BOLD_MARKER_START, marker_pos);
}
// end marker
marker_pos = fulltext.find(BOLD_MARKER_END);
while (marker_pos != std::string::npos) {
fulltext.replace(marker_pos, 4, marker_e);
marker_pos = fulltext.find(BOLD_MARKER_END, marker_pos);
}
// divide fulltext
size_t hypertext_start = fulltext.find(HYPERTEXT_MARKER_START);
if (hypertext_start != std::string::npos) {
//hypertext exists
fulltext.erase(hypertext_start, HYPERTEXT_MARKER_START.size());
if (fulltext.find(HYPERTEXT_MARKER_START) != std::string::npos) {
// This must not happen - only 1 hypertext allowed
BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertexts: " << _utf8(dict["text"]);
continue;
}
size_t hypertext_end = fulltext.find(HYPERTEXT_MARKER_END);
if (hypertext_end == std::string::npos) {
// hypertext was not correctly ended
BOOST_LOG_TRIVIAL(error) << "Hint notification without hypertext end marker: " << _utf8(dict["text"]);
continue;
}
fulltext.erase(hypertext_end, HYPERTEXT_MARKER_END.size());
if (fulltext.find(HYPERTEXT_MARKER_END) != std::string::npos) {
// This must not happen - only 1 hypertext end allowed
BOOST_LOG_TRIVIAL(error) << "Hint notification with multiple hypertext end markers: " << _utf8(dict["text"]);
continue;
}
text1 = fulltext.substr(0, hypertext_start);
hypertext_text = fulltext.substr(hypertext_start, hypertext_end - hypertext_start);
follow_text = fulltext.substr(hypertext_end);
} else {
text1 = fulltext;
}
if (dict.find("disabled_modes") != dict.end()) {
disabled_modes = dict["disabled_modes"];
}
// create HintData
if (dict.find("hypertext_type") != dict.end()) {
//link to internet
if(dict["hypertext_type"] == "link") {
std::string hypertext_link = dict["hypertext_link"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, [hypertext_link]() { wxLaunchDefaultBrowser(hypertext_link); } };
m_loaded_hints.emplace_back(hint_data);
// highlight settings
} else if (dict["hypertext_type"] == "settings") {
std::string opt = dict["hypertext_settings_opt"];
Preset::Type type = static_cast<Preset::Type>(std::atoi(dict["hypertext_settings_type"].c_str()));
std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]);
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } };
m_loaded_hints.emplace_back(hint_data);
// open preferences
} else if(dict["hypertext_type"] == "preferences") {
int page = static_cast<Preset::Type>(std::atoi(dict["hypertext_preferences_page"].c_str()));
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, [page]() { wxGetApp().open_preferences(page); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "plater") {
std::string item = dict["hypertext_plater_item"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "gizmo") {
std::string item = dict["hypertext_gizmo_item"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, true, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } };
m_loaded_hints.emplace_back(hint_data);
}
else if (dict["hypertext_type"] == "gallery") {
HintData hint_data{ text1, hypertext_text, follow_text, disabled_modes, false, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } };
m_loaded_hints.emplace_back(hint_data);
}
} else {
// plain text without hypertext
HintData hint_data{ text1 };
m_loaded_hints.emplace_back(hint_data);
}
}
}
}
HintData* HintDatabase::get_hint(bool up)
{
if (! m_initialized) {
init();
//return false;
}
// shift id
m_hint_id = (up ? m_hint_id + 1 : (m_hint_id == 0 ? m_loaded_hints.size() - 1 : m_hint_id - 1));
m_hint_id %= m_loaded_hints.size();
AppConfig* app_config = wxGetApp().app_config;
app_config->set("last_hint", std::to_string(m_hint_id));
//data = &m_loaded_hints[m_hint_id];
/*
data.text = m_loaded_hints[m_hint_id].text;
data.hypertext = m_loaded_hints[m_hint_id].hypertext;
data.follow_text = m_loaded_hints[m_hint_id].follow_text;
data.callback = m_loaded_hints[m_hint_id].callback;
*/
return &m_loaded_hints[m_hint_id];
}
void NotificationManager::HintNotification::count_spaces()
{
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
std::string text;
text = ImGui::WarningMarker;
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
// no left button picture
//m_left_indentation = m_line_height;
m_window_width_offset = m_left_indentation + m_line_height * 3.f;// 5.5f; // no right arrow
m_window_width = m_line_height * 25;
}
void NotificationManager::HintNotification::count_lines()
{
std::string text = m_text1;
size_t last_end = 0;
m_lines_count = 0;
if (text.empty())
return;
m_endlines.clear();
while (last_end < text.length() - 1)
{
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset) {
//next line is ended by '/n'
m_endlines.push_back(next_hard_end);
last_end = next_hard_end + 1;
}
else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
// more than one line till end
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0 && next_space < text.length()) {
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
}
} else {
next_space = text.length();
}
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
letter_count++;
}
m_endlines.push_back(last_end + letter_count);
last_end += letter_count;
} else {
m_endlines.push_back(next_space);
last_end = next_space + 1;
}
}
else {
m_endlines.push_back(text.length());
last_end = text.length();
}
}
m_lines_count++;
}
int prev_end = m_endlines.size() > 1 ? m_endlines[m_endlines.size() - 2] : 0;
int size_of_last_line = ImGui::CalcTextSize(text.substr(prev_end, last_end - prev_end).c_str()).x;
// hypertext calculation
if (!m_hypertext.empty()) {
if (size_of_last_line + ImGui::CalcTextSize(m_hypertext.c_str()).x > m_window_width - m_window_width_offset) {
// hypertext on new line
size_of_last_line = ImGui::CalcTextSize((m_hypertext + " ").c_str()).x;
m_endlines.push_back(last_end);
m_lines_count++;
} else {
size_of_last_line += ImGui::CalcTextSize((m_hypertext + " ").c_str()).x;
}
}
if (!m_text2.empty()) {
text = m_text2;
last_end = 0;
m_endlines2.clear();
// if size_of_last_line too large to fit anything
size_t first_end = std::min(text.find_first_of('\n'), text.find_first_of(' '));
if (size_of_last_line >= m_window_width - m_window_width_offset - ImGui::CalcTextSize(text.substr(0, first_end).c_str()).x) {
m_endlines2.push_back(0);
size_of_last_line = 0;
}
while (last_end < text.length() - 1)
{
size_t next_hard_end = text.find_first_of('\n', last_end);
if (next_hard_end != std::string::npos && ImGui::CalcTextSize(text.substr(last_end, next_hard_end - last_end).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) {
//next line is ended by '/n'
m_endlines2.push_back(next_hard_end);
last_end = next_hard_end + 1;
}
else {
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset - size_of_last_line) {
// more than one line till end
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0) {
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
}
}
else {
next_space = text.length();
}
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset - size_of_last_line ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x + size_of_last_line < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset - size_of_last_line) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset - size_of_last_line) {
letter_count++;
}
m_endlines2.push_back(last_end + letter_count);
last_end += letter_count;
}
else {
m_endlines2.push_back(next_space);
last_end = next_space + 1;
}
}
else {
m_endlines2.push_back(text.length());
last_end = text.length();
}
}
if (size_of_last_line == 0) // if first line is continuation of previous text, do not add to line count.
m_lines_count++;
size_of_last_line = 0; // should countain value only for first line (with hypertext)
}
}
}
void NotificationManager::HintNotification::init()
{
// Do not init closing notification
if (is_finished())
return;
count_spaces();
count_lines();
m_multiline = true;
m_notification_start = GLCanvas3D::timestamp_now();
if (m_state == EState::Unknown)
m_state = EState::Shown;
}
void NotificationManager::HintNotification::set_next_window_size(ImGuiWrapper& imgui)
{
/*
m_window_height = m_multiline ?
(m_lines_count + 1.f) * m_line_height :
4.f * m_line_height;
m_window_height += 1 * m_line_height; // top and bottom
*/
m_window_height = std::max((m_lines_count + 1.f) * m_line_height, 4.f * m_line_height);
}
bool NotificationManager::HintNotification::on_text_click()
{
if (m_hypertext_callback != nullptr && (!m_runtime_disable || disabled_modes_check(m_disabled_modes)))
m_hypertext_callback();
return false;
}
void NotificationManager::HintNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
if (!m_has_hint_data) {
retrieve_data();
}
float x_offset = m_left_indentation;
int last_end = 0;
float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height :(m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2: m_line_height / 2));
float shift_y = m_line_height;
std::string line;
for (size_t i = 0; i < (m_multiline ? /*m_lines_count*/m_endlines.size() : 2); i++) {
line.clear();
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
if (i == 1 && m_endlines.size() > 2 && !m_multiline) {
// second line with "more" hypertext
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
line = line.substr(0, line.length() - 1);
}
line += "..";
} else {
// regural line
line = m_text1.substr(last_end, m_endlines[i] - last_end);
}
// first line is headline
if (i == 0) {
line = ImGui::ColorMarkerStart + line + ImGui::ColorMarkerEnd;
}
// Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line)
if (line.find_first_of(ImGui::ColorMarkerEnd) < line.find_first_of(ImGui::ColorMarkerStart)) {
line = ImGui::ColorMarkerStart + line;
}
last_end = m_endlines[i];
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
}
//hyperlink text
if (!m_multiline && m_lines_count > 2) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true);
} else if (!m_hypertext.empty()) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty()? "": " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext);
}
// text2
if (!m_text2.empty() && m_multiline) {
starting_y += (m_endlines.size() - 1) * shift_y;
last_end = 0;
for (size_t i = 0; i < (m_multiline ? m_endlines2.size() : 2); i++) {
if (i == 0) //first line X is shifted by hypertext
ImGui::SetCursorPosX(x_offset + ImGui::CalcTextSize((line + m_hypertext + (line.empty() ? " " : " ")).c_str()).x);
else
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
line.clear();
if (m_endlines2.size() > i && m_text2.size() >= m_endlines2[i]) {
// regural line
line = m_text2.substr(last_end, m_endlines2[i] - last_end);
// Add ImGui::ColorMarkerStart if there is ImGui::ColorMarkerEnd first (start was at prev line)
if (line.find_first_of(ImGui::ColorMarkerEnd) < line.find_first_of(ImGui::ColorMarkerStart)) {
line = ImGui::ColorMarkerStart + line;
}
last_end = m_endlines2[i];
if (m_text2.size() > m_endlines2[i])
last_end += (m_text2[m_endlines2[i]] == '\n' || m_text2[m_endlines2[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
}
}
}
void NotificationManager::HintNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::CloseNotifButton;
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - win_size.x / 10.f, win_pos.y),
ImVec2(win_pos.x, win_pos.y + win_size.y - 2 * m_line_height),
true))
{
button_text = ImGui::CloseNotifHoverButton;
}
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
m_close_b_w = button_size.y;
if (m_lines_count <= 3) {
m_close_b_y = win_size.y / 2 - button_size.y * 1.25f;
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
ImGui::SetCursorPosY(m_close_b_y);
} else {
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.75f);
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
}
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
close();
}
//invisible large button
ImGui::SetCursorPosX(win_size.x - m_line_height * 2.35f);
ImGui::SetCursorPosY(0);
if (imgui.button(" ", m_line_height * 2.125, win_size.y - 2 * m_line_height))
{
close();
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
render_right_arrow_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_logo(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
render_preferences_button(imgui, win_pos_x, win_pos_y);
}
void NotificationManager::HintNotification::render_preferences_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
std::string button_text;
button_text = ImGui::PreferencesButton;
//hover
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos_x - m_window_width / 10.f, win_pos_y + m_window_height - 2 * m_line_height + 1),
ImVec2(win_pos_x, win_pos_y + m_window_height),
true))
{
button_text = ImGui::PreferencesHoverButton;
}
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosX(m_window_width - m_line_height * 1.75f);
if (m_lines_count <= 3) {
ImGui::SetCursorPosY(m_close_b_y + m_close_b_w / 4.f * 7.f);
} else {
ImGui::SetCursorPosY(m_window_height - button_size.y - m_close_b_w / 4.f);
}
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
wxGetApp().open_preferences(2);
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
// preferences button is in place of minimize button
m_minimize_b_visible = true;
}
void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
// Used for debuging
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::RightArrowButton;
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosX(m_window_width - m_line_height * 3.f);
if (m_lines_count <= 3)
ImGui::SetCursorPosY(m_close_b_y + m_close_b_w / 4.f * 7.f);
else
ImGui::SetCursorPosY(m_window_height - button_size.y - m_close_b_w / 4.f);
if (imgui.button(button_text.c_str(), button_size.x * 0.8f, button_size.y * 1.f))
{
retrieve_data();
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
void NotificationManager::HintNotification::render_logo(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
button_text = ImGui::ErrorMarker;//LeftArrowButton;
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
ImGui::SetCursorPosX(0);
// shouldnt it render as text?
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
{
}
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
void NotificationManager::HintNotification::retrieve_data(size_t recursion_counter)
{
HintData* hint_data = HintDatabase::get_instance().get_hint(true);
if (hint_data != nullptr && !disabled_modes_check(hint_data->disabled_modes))
{
// Content for different user - retrieve another
size_t count = HintDatabase::get_instance().get_count();
if (count < recursion_counter) {
BOOST_LOG_TRIVIAL(error) << "Hint notification failed to load data due to recursion counter.";
} else {
retrieve_data(recursion_counter + 1);
}
return;
}
if(hint_data != nullptr)
{
NotificationData nd { NotificationType::DidYouKnowHint,
NotificationLevel::RegularNotification,
0,
hint_data->text,
hint_data->hypertext, nullptr,
hint_data->follow_text };
update(nd);
m_hypertext_callback = hint_data->callback;
m_disabled_modes = hint_data->disabled_modes;
m_runtime_disable = hint_data->runtime_disable;
m_has_hint_data = true;
}
}
} //namespace Slic3r
} //namespace GUI

View file

@ -0,0 +1,97 @@
#ifndef slic3r_GUI_HintNotification_hpp_
#define slic3r_GUI_HintNotification_hpp_
#include "NotificationManager.hpp"
namespace Slic3r {
namespace GUI {
// Database of hints updatable
struct HintData
{
std::string text;
std::string hypertext;
std::string follow_text;
std::string disabled_modes;
bool runtime_disable; // if true - hyperlink will check before every click if not in disabled mode
std::function<void(void)> callback{ nullptr };
};
class HintDatabase
{
public:
static HintDatabase& get_instance()
{
static HintDatabase instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return instance;
}
private:
HintDatabase()
: m_hint_id(0)
{}
public:
HintDatabase(HintDatabase const&) = delete;
void operator=(HintDatabase const&) = delete;
// return true if HintData filled;
HintData* get_hint(bool up = true);
size_t get_count() {
if (!m_initialized)
return 0;
return m_loaded_hints.size();
}
private:
void init();
void load_hints_from_file(const boost::filesystem::path& path);
size_t m_hint_id;
bool m_initialized { false };
std::vector<HintData> m_loaded_hints;
};
// Notification class - shows current Hint ("Did you know")
class NotificationManager::HintNotification : public NotificationManager::PopNotification
{
public:
HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler)
: PopNotification(n, id_provider, evt_handler)
{
retrieve_data();
}
virtual void init() override;
protected:
virtual void set_next_window_size(ImGuiWrapper& imgui) override;
virtual void count_spaces() override;
virtual void count_lines() override;
virtual bool on_text_click() override;
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y) override {}
void render_preferences_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y);
void render_right_arrow_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
void render_logo(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
void retrieve_data(size_t recursion_counter = 0);
bool m_has_hint_data { false };
std::function<void(void)> m_hypertext_callback;
std::string m_disabled_modes;
bool m_runtime_disable;
float m_close_b_y { 0 };
float m_close_b_w { 0 };
};
} //namespace Slic3r
} //namespace GUI
#endif //slic3r_GUI_HintNotification_hpp_

View file

@ -43,7 +43,11 @@ static const std::map<const char, std::string> font_icons = {
{ImGui::FilamentIconMarker , "spool" },
{ImGui::MaterialIconMarker , "resin" },
{ImGui::MinimalizeButton , "notification_minimalize" },
{ImGui::MinimalizeHoverButton , "notification_minimalize_hover" }
{ImGui::MinimalizeHoverButton , "notification_minimalize_hover" },
{ImGui::RightArrowButton , "notification_right" },
{ImGui::RightArrowHoverButton , "notification_right_hover" },
{ImGui::PreferencesButton , "notification_preferences" },
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
};
static const std::map<const char, std::string> font_icons_large = {
{ImGui::CloseNotifButton , "notification_close" },
@ -54,6 +58,12 @@ static const std::map<const char, std::string> font_icons_large = {
{ImGui::ErrorMarker , "notification_error" },
{ImGui::CancelButton , "notification_cancel" },
{ImGui::CancelHoverButton , "notification_cancel_hover" },
{ImGui::SinkingObjectMarker , "move" },
{ImGui::CustomSupportsMarker , "fdm_supports" },
{ImGui::CustomSeamMarker , "seam" },
{ImGui::MmuSegmentationMarker , "move" },
{ImGui::VarLayerHeightMarker , "layers" },
};
const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f };
@ -204,7 +214,8 @@ bool ImGuiWrapper::update_mouse_data(wxMouseEvent& evt)
unsigned buttons = (evt.LeftIsDown() ? 1 : 0) | (evt.RightIsDown() ? 2 : 0) | (evt.MiddleIsDown() ? 4 : 0);
m_mouse_buttons = buttons;
new_frame();
if (want_mouse())
new_frame();
return want_mouse();
}
@ -222,9 +233,6 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
if (key != 0) {
io.AddInputCharacter(key);
}
new_frame();
return want_keyboard() || want_text_input();
} else {
// Key up/down event
int key = evt.GetKeyCode();
@ -235,10 +243,11 @@ bool ImGuiWrapper::update_key_data(wxKeyEvent &evt)
io.KeyCtrl = evt.ControlDown();
io.KeyAlt = evt.AltDown();
io.KeySuper = evt.MetaDown();
new_frame();
return want_keyboard() || want_text_input();
}
bool ret = want_keyboard() || want_text_input();
if (ret)
new_frame();
return ret;
}
void ImGuiWrapper::new_frame()
@ -407,20 +416,23 @@ void ImGuiWrapper::text_colored(const ImVec4& color, const wxString& label)
this->text_colored(color, label_utf8.c_str());
}
bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/)
bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/)
{
return ImGui::SliderFloat(label, v, v_min, v_max, format, power);
bool ret = ImGui::SliderFloat(label, v, v_min, v_max, format, power);
if (clamp)
*v = std::clamp(*v, v_min, v_max);
return ret;
}
bool ImGuiWrapper::slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/)
bool ImGuiWrapper::slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/)
{
return this->slider_float(label.c_str(), v, v_min, v_max, format, power);
return this->slider_float(label.c_str(), v, v_min, v_max, format, power, clamp);
}
bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/)
bool ImGuiWrapper::slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format/* = "%.3f"*/, float power/* = 1.0f*/, bool clamp /*= true*/)
{
auto label_utf8 = into_u8(label);
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power);
return this->slider_float(label_utf8.c_str(), v, v_min, v_max, format, power, clamp);
}
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection)
@ -1118,6 +1130,11 @@ void ImGuiWrapper::init_style()
set_color(ImGuiCol_TabActive, COL_ORANGE_LIGHT);
set_color(ImGuiCol_TabUnfocused, COL_GREY_DARK);
set_color(ImGuiCol_TabUnfocusedActive, COL_GREY_LIGHT);
// Scrollbars
set_color(ImGuiCol_ScrollbarGrab, COL_ORANGE_DARK);
set_color(ImGuiCol_ScrollbarGrabHovered, COL_ORANGE_LIGHT);
set_color(ImGuiCol_ScrollbarGrabActive, COL_ORANGE_LIGHT);
}
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)

View file

@ -79,9 +79,12 @@ public:
void text_colored(const ImVec4& color, const char* label);
void text_colored(const ImVec4& color, const std::string& label);
void text_colored(const ImVec4& color, const wxString& label);
bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f);
// Float sliders: Manually inserted values aren't clamped by ImGui.Using this wrapper function does (when clamp==true).
bool slider_float(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
bool slider_float(const std::string& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
bool slider_float(const wxString& label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f, bool clamp = true);
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, int& mouse_wheel);
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,

View file

@ -221,9 +221,7 @@ void KBShortcutsDialog::fill_shortcuts()
{ "D", L("Horizontal slider - Move active thumb Right") },
{ "X", L("On/Off one layer mode of the vertical slider") },
{ "L", L("Show/Hide Legend and Estimated printing time") },
#if ENABLE_GCODE_WINDOW
{ "C", L("Show/Hide G-code window") },
#endif // ENABLE_GCODE_WINDOW
};
m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts });

View file

@ -213,7 +213,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
// declare events
Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) {
#if ENABLE_PROJECT_DIRTY_STATE
if (event.CanVeto() && m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(true)) {
// prevents to open the save dirty project dialog
event.Veto();
@ -226,9 +225,6 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
}
if (event.CanVeto() && !wxGetApp().check_and_save_current_preset_changes()) {
#else
if (event.CanVeto() && !wxGetApp().check_unsaved_changes()) {
#endif // ENABLE_PROJECT_DIRTY_STATE
event.Veto();
return;
}
@ -616,14 +612,9 @@ void MainFrame::update_title()
// m_plater->get_project_filename() produces file name including path, but excluding extension.
// Don't try to remove the extension, it would remove part of the file name after the last dot!
wxString project = from_path(into_path(m_plater->get_project_filename()).filename());
#if ENABLE_PROJECT_DIRTY_STATE
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
if (!dirty_marker.empty() || !project.empty())
title = dirty_marker + project + " - ";
#else
if (!project.empty())
title += (project + " - ");
#endif // ENABLE_PROJECT_DIRTY_STATE
}
std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID;
@ -675,14 +666,12 @@ void MainFrame::init_tabpanel()
#else
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) {
#endif
#if ENABLE_VALIDATE_CUSTOM_GCODE
if (int old_selection = e.GetOldSelection();
old_selection != wxNOT_FOUND && old_selection < static_cast<int>(m_tabpanel->GetPageCount())) {
Tab* old_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(old_selection));
if (old_tab)
old_tab->validate_custom_gcodes();
}
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
wxWindow* panel = m_tabpanel->GetCurrentPage();
Tab* tab = dynamic_cast<Tab*>(panel);
@ -825,7 +814,6 @@ bool MainFrame::can_start_new_project() const
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
}
#if ENABLE_PROJECT_DIRTY_STATE
bool MainFrame::can_save() const
{
return (m_plater != nullptr) && !m_plater->model().objects.empty() &&
@ -852,12 +840,6 @@ void MainFrame::save_project_as(const wxString& filename)
m_plater->reset_project_dirty_after_save();
}
}
#else
bool MainFrame::can_save() const
{
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
#endif // ENABLE_PROJECT_DIRTY_STATE
bool MainFrame::can_export_model() const
{
@ -1177,27 +1159,16 @@ void MainFrame::init_menubar_as_editor()
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
#if ENABLE_PROJECT_DIRTY_STATE
append_menu_item(fileMenu, wxID_ANY, _L("&Save Project") + "\tCtrl+S", _L("Save current project file"),
[this](wxCommandEvent&) { save_project(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#else
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"))); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#endif // ENABLE_PROJECT_DIRTY_STATE
#ifdef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Shift+S", _L("Save current project file as"),
#else
append_menu_item(fileMenu, wxID_ANY, _L("Save Project &as") + dots + "\tCtrl+Alt+S", _L("Save current project file as"),
#endif // __APPLE__
#if ENABLE_PROJECT_DIRTY_STATE
[this](wxCommandEvent&) { save_project_as(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save_as(); }, this);
#else
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#endif // ENABLE_PROJECT_DIRTY_STATE
fileMenu->AppendSeparator();
@ -1752,11 +1723,7 @@ void MainFrame::export_config()
// Load a config file containing a Print, Filament & Printer preset.
void MainFrame::load_config_file()
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes())
#else
if (!wxGetApp().check_unsaved_changes())
#endif // ENABLE_PROJECT_DIRTY_STATE
return;
wxFileDialog dlg(this, _L("Select configuration to load:"),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
@ -1791,11 +1758,7 @@ bool MainFrame::load_config_file(const std::string &path)
void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes())
#else
if (!wxGetApp().check_unsaved_changes())
#endif // ENABLE_PROJECT_DIRTY_STATE
return;
// validate current configuration in case it's dirty
auto err = wxGetApp().preset_bundle->full_config().validate();
@ -1827,11 +1790,7 @@ void MainFrame::export_configbundle(bool export_physical_printers /*= false*/)
// but that behavior was not documented and likely buggy.
void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool reset_user_profile*/)
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes())
#else
if (!wxGetApp().check_unsaved_changes())
#endif // ENABLE_PROJECT_DIRTY_STATE
return;
if (file.IsEmpty()) {
wxFileDialog dlg(this, _L("Select configuration to load:"),
@ -1985,7 +1944,7 @@ void MainFrame::select_tab(size_t tab/* = size_t(-1)*/)
m_main_sizer->Show(m_tabpanel, tab != 0);
// plater should be focused for correct navigation inside search window
if (tab == 0 && m_plater->canvas3D()->is_search_pressed())
if (tab == 0)
m_plater->SetFocus();
Layout();
}

View file

@ -91,9 +91,6 @@ class MainFrame : public DPIFrame
void on_value_changed(wxCommandEvent&);
bool can_start_new_project() const;
#if !ENABLE_PROJECT_DIRTY_STATE
bool can_save() const;
#endif // !ENABLE_PROJECT_DIRTY_STATE
bool can_export_model() const;
bool can_export_toolpaths() const;
bool can_export_supports() const;
@ -188,12 +185,10 @@ public:
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
void on_config_changed(DynamicPrintConfig* cfg) const ;
#if ENABLE_PROJECT_DIRTY_STATE
bool can_save() const;
bool can_save_as() const;
void save_project();
void save_project_as(const wxString& filename = wxString());
#endif // ENABLE_PROJECT_DIRTY_STATE
void add_to_recent_projects(const wxString& filename);
void technology_changed();

View file

@ -50,7 +50,7 @@ void ButtonsListCtrl::OnPaint(wxPaintEvent&)
const wxColour& selected_btn_bg = Slic3r::GUI::wxGetApp().get_color_selected_btn_bg();
const wxColour& default_btn_bg = Slic3r::GUI::wxGetApp().get_highlight_default_clr();
const wxColour& btn_marker_color = Slic3r::GUI::wxGetApp().get_color_hovered_btn_label();
for (int idx = 0; idx < m_pageButtons.size(); idx++) {
for (int idx = 0; idx < int(m_pageButtons.size()); idx++) {
wxButton* btn = m_pageButtons[idx];
btn->SetBackgroundColour(idx == m_selection ? selected_btn_bg : default_btn_bg);

View file

@ -1,10 +1,12 @@
#include "NotificationManager.hpp"
#include "HintNotification.hpp"
#include "GUI.hpp"
#include "ImGuiWrapper.hpp"
#include "PrintHostDialogs.hpp"
#include "wxExtensions.hpp"
#include "ObjectDataViewModel.hpp"
#include "libslic3r/Config.hpp"
#include "../Utils/PrintHost.hpp"
#include "libslic3r/Config.hpp"
@ -30,7 +32,37 @@ wxDEFINE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClicke
wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent);
namespace Notifications_Internal{
const NotificationManager::NotificationData NotificationManager::basic_notifications[] = {
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
[](wxEvtHandler* evnthndlr) {
if (evnthndlr != nullptr)
wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));
return true;
}
},
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
_u8L("You have just added a G-code for color change, but its value is empty.\n"
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
_u8L("This model doesn't allow to automatically add the color changes") },
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Desktop integration was successful.") },
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Desktop integration failed.") },
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Undo desktop integration was successful.") },
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Undo desktop integration failed.") },
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
};
namespace {
/* // not used?
ImFont* add_default_font(float pixel_size)
{
ImGuiIO& io = ImGui::GetIO();
@ -41,8 +73,8 @@ namespace Notifications_Internal{
ImFont* font = io.Fonts->AddFontDefault(&config);
return font;
}
static inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
*/
inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
{
if (fading_out)
ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
@ -129,8 +161,8 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n,
m_data (n)
, m_id_provider (id_provider)
, m_text1 (n.text1)
, m_hypertext (n.hypertext)
, m_text2 (n.text2)
, m_hypertext (n.hypertext)
, m_text2 (n.text2)
, m_evt_handler (evt_handler)
, m_notification_start (GLCanvas3D::timestamp_now())
{}
@ -184,8 +216,8 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
// color change based on fading out
if (m_state == EState::FadingOut) {
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
fading_pop = true;
}
@ -219,20 +251,20 @@ bool NotificationManager::PopNotification::push_background_color()
{
if (m_is_gray) {
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
if (m_data.level == NotificationLevel::ErrorNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
if (m_data.level == NotificationLevel::WarningNotification) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
backcolor.y += 0.15f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
return false;
@ -259,6 +291,9 @@ void NotificationManager::PopNotification::count_lines()
size_t last_end = 0;
m_lines_count = 0;
if (text.empty())
return;
m_endlines.clear();
while (last_end < text.length() - 1)
{
@ -272,9 +307,9 @@ void NotificationManager::PopNotification::count_lines()
// find next suitable endline
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
// more than one line till end
int next_space = text.find_first_of(' ', last_end);
size_t next_space = text.find_first_of(' ', last_end);
if (next_space > 0 && next_space < text.length()) {
int next_space_candidate = text.find_first_of(' ', next_space + 1);
size_t next_space_candidate = text.find_first_of(' ', next_space + 1);
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
next_space = next_space_candidate;
next_space_candidate = text.find_first_of(' ', next_space + 1);
@ -283,7 +318,9 @@ void NotificationManager::PopNotification::count_lines()
next_space = text.length();
}
// when one word longer than line.
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset) {
if (ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x > m_window_width - m_window_width_offset ||
ImGui::CalcTextSize(text.substr(last_end, next_space - last_end).c_str()).x < (m_window_width - m_window_width_offset) / 4 * 3
) {
float width_of_a = ImGui::CalcTextSize("a").x;
int letter_count = (int)((m_window_width - m_window_width_offset) / width_of_a);
while (last_end + letter_count < text.size() && ImGui::CalcTextSize(text.substr(last_end, letter_count).c_str()).x < m_window_width - m_window_width_offset) {
@ -312,6 +349,9 @@ void NotificationManager::PopNotification::count_lines()
m_lines_count++;
}
}
// m_text_2 (text after hypertext) is not used for regular notifications right now.
// its caluculation is in HintNotification::count_lines()
}
void NotificationManager::PopNotification::init()
@ -339,105 +379,45 @@ void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& im
void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ImVec2 win_size(win_size_x, win_size_y);
float x_offset = m_left_indentation;
std::string fulltext = m_text1 + m_hypertext; //+ m_text2;
ImVec2 text_size = ImGui::CalcTextSize(fulltext.c_str());
// text posistions are calculated by lines count
// large texts has "more" button or are displayed whole
// smaller texts are divided as one liners and two liners
if (m_lines_count > 2) {
if (m_multiline) {
int last_end = 0;
float starting_y = m_line_height/2;
float shift_y = m_line_height;
std::string line;
float x_offset = m_left_indentation;
int last_end = 0;
float starting_y = (m_lines_count == 2 ? win_size_y / 2 - m_line_height : (m_lines_count == 1 ? win_size_y / 2 - m_line_height / 2 : m_line_height / 2));
float shift_y = m_line_height;
std::string line;
for (size_t i = 0; i < m_lines_count; i++) {
line.clear();
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
line = m_text1.substr(last_end, m_endlines[i] - last_end);
last_end = m_endlines[i];
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
}
//hyperlink text
if (!m_hypertext.empty()) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_lines_count - 1) * shift_y, m_hypertext);
}
} else {
// line1
if (m_text1.size() >= m_endlines[0]) {
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
}
// line2
std::string line;
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(win_size.y / 2 + win_size.y / 6 - m_line_height / 2);
if (m_text1.size() >= m_endlines[1]) {
for (size_t i = 0; i < (m_multiline ? m_endlines.size() : std::min(m_endlines.size(), (size_t)2)); i++) {
line.clear();
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(starting_y + i * shift_y);
if (m_endlines.size() > i && m_text1.size() >= m_endlines[i]) {
if (i == 1 && m_endlines.size() > 2 && !m_multiline) {
// second line with "more" hypertext
line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0), m_endlines[1] - m_endlines[0] - (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
if (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
line = line.substr(0, line.length() - 6);
line += "..";
} else
line += " ";
imgui.text(line.c_str());
while (ImGui::CalcTextSize(line.c_str()).x > m_window_width - m_window_width_offset - ImGui::CalcTextSize((".." + _u8L("More")).c_str()).x) {
line = line.substr(0, line.length() - 1);
}
line += "..";
}
// "More" hypertext
render_hypertext(imgui, x_offset + ImGui::CalcTextSize(line.c_str()).x, win_size.y / 2 + win_size.y / 6 - m_line_height / 2, _u8L("More"), true);
}
} else {
//text 1
float cursor_y = win_size.y / 2 - text_size.y / 2;
float cursor_x = x_offset;
if(m_lines_count > 1) {
// line1
if (m_text1.length() >= m_endlines[0]) { // could be equal than substr takes whole string
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
else {
// regural line
line = m_text1.substr(last_end, m_endlines[i] - last_end);
}
// line2
ImGui::SetCursorPosX(x_offset);
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
ImGui::SetCursorPosY(cursor_y);
if (m_text1.length() > m_endlines[0]) { // must be greater otherwise theres nothing to show and m_text1[m_endlines[0]] is beyond last letter
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
imgui.text(line.c_str());
cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
}
} else {
ImGui::SetCursorPosX(x_offset);
ImGui::SetCursorPosY(cursor_y);
imgui.text(m_text1.c_str());
cursor_x = x_offset + ImGui::CalcTextSize(m_text1.c_str()).x;
last_end = m_endlines[i];
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
imgui.text(line.c_str());
}
//hyperlink text
if (!m_hypertext.empty()) {
render_hypertext(imgui, cursor_x + 4, cursor_y, m_hypertext);
}
//notification text 2
//text 2 is suposed to be after the hyperlink - currently it is not used
/*
if (!m_text2.empty())
{
ImVec2 part_size = ImGui::CalcTextSize(m_hypertext.c_str());
ImGui::SetCursorPosX(win_size.x / 2 + text_size.x / 2 - part_size.x + 8 - x_offset);
ImGui::SetCursorPosY(cursor_y);
imgui.text(m_text2.c_str());
}
*/
}
//hyperlink text
if (!m_multiline && m_lines_count > 2) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + " ").c_str()).x, starting_y + shift_y, _u8L("More"), true);
}
else if (!m_hypertext.empty()) {
render_hypertext(imgui, x_offset + ImGui::CalcTextSize((line + (line.empty() ? "" : " ")).c_str()).x, starting_y + (m_endlines.size() - 1) * shift_y, m_hypertext);
}
// text2 (text after hypertext) is not rendered for regular notifications
// its rendering is in HintNotification::render_text
}
void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui, const float text_x, const float text_y, const std::string text, bool more)
@ -470,7 +450,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
orange_color.y += 0.2f;
//text
Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::SetCursorPosX(text_x);
ImGui::SetCursorPosY(text_y);
imgui.text(text.c_str());
@ -491,8 +471,8 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
@ -542,9 +522,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
//button - if part if treggered
@ -762,8 +742,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
@ -818,7 +798,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
}
bool NotificationManager::ExportFinishedNotification::on_text_click()
{
Notifications_Internal::open_folder(m_export_dir_path);
open_folder(m_export_dir_path);
return false;
}
//------ProgressBar----------------
@ -961,7 +941,7 @@ bool NotificationManager::PrintHostUploadNotification::push_background_color()
if (m_uj_state == UploadJobState::PB_ERROR) {
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
backcolor.x += 0.3f;
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
return true;
}
return false;
@ -1031,8 +1011,8 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
ImVec2 win_pos(win_pos_x, win_pos_y);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
std::string button_text;
@ -1079,8 +1059,37 @@ void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGu
ImGui::PopStyleColor();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
}
//------UpdatedItemsInfoNotification-------
void NotificationManager::UpdatedItemsInfoNotification::count_spaces()
{
//determine line width
m_line_height = ImGui::CalcTextSize("A").y;
std::string text;
text = ImGui::WarningMarker;
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
m_left_indentation = picture_width + m_line_height / 2;
m_window_width_offset = m_left_indentation + m_line_height * 3.f;
m_window_width = m_line_height * 25;
}
void NotificationManager::UpdatedItemsInfoNotification::render_left_sign(ImGuiWrapper& imgui)
{
std::string text;
switch (m_info_item_type) {
case InfoItemType::CustomSupports: text = ImGui::CustomSupportsMarker; break;
case InfoItemType::CustomSeam: text = ImGui::CustomSeamMarker; break;
case InfoItemType::MmuSegmentation: text = ImGui::MmuSegmentationMarker; break;
case InfoItemType::VariableLayerHeight: text = ImGui::VarLayerHeightMarker; break;
case InfoItemType::Sinking: text = ImGui::SinkingObjectMarker; break;
default: break;
}
ImGui::SetCursorPosX(m_line_height / 3);
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
imgui.text(text.c_str());
}
//------NotificationManager--------
NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
m_evt_handler(evt_handler)
@ -1088,10 +1097,10 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
}
void NotificationManager::push_notification(const NotificationType type, int timestamp)
{
auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(),
auto it = std::find_if(std::begin(basic_notifications), std::end(basic_notifications),
boost::bind(&NotificationData::type, boost::placeholders::_1) == type);
assert(it != basic_notifications.end());
if (it != basic_notifications.end())
assert(it != std::end(basic_notifications));
if (it != std::end(basic_notifications))
push_notification_data(*it, timestamp);
}
void NotificationManager::push_notification(const std::string& text, int timestamp)
@ -1108,15 +1117,22 @@ void NotificationManager::push_notification(NotificationType type,
{
int duration = 0;
switch (level) {
case NotificationLevel::RegularNotification: duration = 10; break;
case NotificationLevel::ErrorNotification: break;
case NotificationLevel::ImportantNotification: break;
case NotificationLevel::RegularNotification: duration = 10; break;
case NotificationLevel::ErrorNotification: break;
case NotificationLevel::WarningNotification: break;
case NotificationLevel::ImportantNotification: break;
case NotificationLevel::ProgressBarNotification: break;
default:
assert(false);
return;
}
push_notification_data({ type, level, duration, text, hypertext, callback }, timestamp);
}
void NotificationManager::push_validate_error_notification(const std::string& text)
{
push_notification_data({ NotificationType::ValidateError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0);
}
void NotificationManager::push_slicing_error_notification(const std::string& text)
{
set_all_slicing_errors_gray(false);
@ -1331,6 +1347,32 @@ void NotificationManager::upload_job_notification_show_error(int id, const std::
}
}
}
void NotificationManager::push_hint_notification()
{
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_type() == NotificationType::DidYouKnowHint)
return;
}
NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 0, "" };
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler), 0);
}
void NotificationManager::push_updated_item_info_notification(InfoItemType type)
{
std::string text = _utf8("Object(s) were loaded with ");
switch (type) {
case InfoItemType::CustomSupports: text += _utf8("custom supports."); break;
case InfoItemType::CustomSeam: text += _utf8("custom seam."); break;
case InfoItemType::MmuSegmentation: text += _utf8("MMU segmentation."); break;
case InfoItemType::VariableLayerHeight: text += _utf8("variable layer height."); break;
case InfoItemType::Sinking: text = _utf8("Partially sinking object(s) were loaded."); break;
default: text.clear(); break;
}
if (!text.empty()) {
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::RegularNotification, 10, text };
push_notification_data(std::make_unique<NotificationManager::UpdatedItemsInfoNotification>(data, m_id_provider, m_evt_handler, type), 0);
}
}
bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp)
{
return push_notification_data(std::make_unique<PopNotification>(notification_data, m_id_provider, m_evt_handler), timestamp);

View file

@ -29,6 +29,7 @@ wxDECLARE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClicke
class GLCanvas3D;
class ImGuiWrapper;
enum class InfoItemType;
enum class NotificationType
{
@ -55,8 +56,11 @@ enum class NotificationType
// Contains a hyperlink to execute installation of the new system profiles.
PresetUpdateAvailable,
// LoadingFailed,
// Not used - instead Slicing error is used for both slicing and validate errors.
// ValidateError,
// Errors emmited by Print::validate
// difference from Slicing error is that they disappear not grey out at update_background_process
ValidateError,
// Notification emitted by Print::validate
ValidateWarning,
// Slicing error produced by BackgroundSlicingProcess::validate() or by the BackgroundSlicingProcess background
// thread thowing a SlicingError exception.
SlicingError,
@ -79,8 +83,6 @@ enum class NotificationType
EmptyAutoColorChange,
// Notification about detected sign
SignDetected,
// Notification emitted by Print::validate
PrintValidateWarning,
// Notification telling user to quit SLA supports manual editing
QuitSLAManualMode,
// Desktop integration basic info
@ -89,8 +91,12 @@ enum class NotificationType
UndoDesktopIntegrationSuccess,
UndoDesktopIntegrationFail,
// Notification that a printer has more extruders than are supported by MM Gizmo/segmentation.
MmSegmentationExceededExtrudersLimit
MmSegmentationExceededExtrudersLimit,
// Did you know Notification appearing on startup with arrows to change hint
DidYouKnowHint,
// Shows when ObjectList::update_info_items finds information that should be stressed to the user
// Might contain logo taken from gizmos
UpdatedItemsInfo
};
class NotificationManager
@ -122,6 +128,8 @@ public:
// ErrorNotification and ImportantNotification are never faded out.
void push_notification(NotificationType type, NotificationLevel level, const std::string& text, const std::string& hypertext = "",
std::function<bool(wxEvtHandler*)> callback = std::function<bool(wxEvtHandler*)>(), int timestamp = 0);
// Creates Validate Error notification with a custom text and no fade out.
void push_validate_error_notification(const std::string& text);
// Creates Slicing Error notification with a custom text and no fade out.
void push_slicing_error_notification(const std::string& text);
// Creates Slicing Warning notification with a custom text and no fade out.
@ -160,6 +168,9 @@ public:
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
// Hint (did you know) notification
void push_hint_notification();
void push_updated_item_info_notification(InfoItemType type);
// Close old notification ExportFinished.
void new_export_began(bool on_removable);
// finds ExportFinished notification and closes it if it was to removable device
@ -188,7 +199,7 @@ private:
// Callback for hypertext - returns true if notification should close after triggering
// Usually sends event to UI thread thru wxEvtHandler.
// Examples in basic_notifications.
std::function<bool(wxEvtHandler*)> callback { nullptr };
std::function<bool(wxEvtHandler*)> callback;
const std::string text2;
};
@ -237,7 +248,7 @@ private:
//returns top in actual frame
float get_current_top() const { return m_top_y; }
const NotificationType get_type() const { return m_data.type; }
const NotificationData get_data() const { return m_data; }
const NotificationData& get_data() const { return m_data; }
const bool is_gray() const { return m_is_gray; }
void set_gray(bool g) { m_is_gray = g; }
virtual bool compare_text(const std::string& text) const;
@ -318,7 +329,10 @@ private:
float m_top_y { 0.0f };
// Height of text - Used as basic scaling unit!
float m_line_height;
// endlines for text1, hypertext excluded
std::vector<size_t> m_endlines;
// endlines for text2
std::vector<size_t> m_endlines2;
// Gray are f.e. eorrors when its uknown if they are still valid
bool m_is_gray { false };
//if multiline = true, notification is showing all lines(>2)
@ -337,7 +351,7 @@ private:
void set_large(bool l);
bool get_large() { return m_is_large; }
void set_print_info(const std::string &info);
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override
void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width) override
{
// This notification is always hidden if !large (means side bar is collapsed)
if (!get_large() && !is_finished())
@ -345,7 +359,7 @@ private:
PopNotification::render(canvas, initial_y, move_from_overlay, overlay_width);
}
protected:
virtual void render_text(ImGuiWrapper& imgui,
void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y)
override;
@ -366,7 +380,7 @@ private:
{
public:
PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
virtual void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
void close() override { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
void show() { m_state = EState::Unknown; }
};
@ -382,17 +396,17 @@ private:
virtual void init() override;
virtual void count_lines() override;
virtual void render_text(ImGuiWrapper& imgui,
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_bar(ImGuiWrapper& imgui,
virtual void render_bar(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) ;
virtual void render_cancel_button(ImGuiWrapper& imgui,
virtual void render_cancel_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y)
{}
virtual void render_minimize_button(ImGuiWrapper& imgui,
void render_minimize_button(ImGuiWrapper& imgui,
const float win_pos_x, const float win_pos_y) override {}
float m_percentage;
@ -421,22 +435,22 @@ private:
m_has_cancel_button = true;
}
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return /*"[" + std::to_string(id) + "] " + */filename + " -> " + host; }
virtual void set_percentage(float percent) override;
void set_percentage(float percent) override;
void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; init(); }
bool compare_job_id(const int other_id) const { return m_job_id == other_id; }
virtual bool compare_text(const std::string& text) const override { return false; }
bool compare_text(const std::string& text) const override { return false; }
protected:
virtual void init() override;
virtual void count_spaces() override;
virtual bool push_background_color() override;
virtual void render_bar(ImGuiWrapper& imgui,
void init() override;
void count_spaces() override;
bool push_background_color() override;
void render_bar(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_cancel_button(ImGuiWrapper& imgui,
void render_cancel_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
virtual void render_left_sign(ImGuiWrapper& imgui) override;
void render_left_sign(ImGuiWrapper& imgui) override;
// Identifies job in cancel callback
int m_job_id;
// Size of uploaded size to be displayed in MB
@ -461,24 +475,41 @@ private:
std::string m_export_dir_path;
protected:
// Reserves space on right for more buttons
virtual void count_spaces() override;
virtual void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void count_spaces() override;
void render_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
// Renders also button to open directory with exported path and eject removable media
virtual void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void render_close_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y) override;
void render_eject_button(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
virtual void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
{ m_minimize_b_visible = false; }
virtual bool on_text_click() override;
bool on_text_click() override;
// local time of last hover for showing tooltip
long m_hover_time { 0 };
};
class UpdatedItemsInfoNotification : public PopNotification
{
public:
UpdatedItemsInfoNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, InfoItemType info_item_type)
: PopNotification(n, id_provider, evt_handler)
, m_info_item_type(info_item_type)
{
}
void count_spaces() override;
protected:
void render_left_sign(ImGuiWrapper& imgui) override;
InfoItemType m_info_item_type;
};
// in HintNotification.hpp
class HintNotification;
//pushes notification into the queue of notifications that are rendered
//can be used to create custom notification
bool push_notification_data(const NotificationData& notification_data, int timestamp);
@ -504,36 +535,9 @@ private:
// Timestamp of last rendering
int64_t m_last_render { 0LL };
// Notification types that can be shown multiple types at once (compared by text)
const std::vector<NotificationType> m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload };
const std::vector<NotificationType> m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload, NotificationType::UpdatedItemsInfo };
//prepared (basic) notifications
const std::vector<NotificationData> basic_notifications = {
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
[](wxEvtHandler* evnthndlr) {
if (evnthndlr != nullptr)
wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED));
return true;
}
},
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr){
wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
_u8L("You have just added a G-code for color change, but its value is empty.\n"
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
_u8L("This model doesn't allow to automatically add the color changes") },
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Desktop integration was successful.") },
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Desktop integration failed.") },
{NotificationType::UndoDesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Undo desktop integration was successful.") },
{NotificationType::UndoDesktopIntegrationFail, NotificationLevel::WarningNotification, 10,
_u8L("Undo desktop integration failed.") },
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
};
static const NotificationData basic_notifications[];
};
}//namespace GUI

View file

@ -65,6 +65,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_name = info_type == InfoItemType::CustomSupports ? _L("Paint-on supports") :
info_type == InfoItemType::CustomSeam ? _L("Paint-on seam") :
info_type == InfoItemType::MmuSegmentation ? _L("Paint-on segmentation") :
info_type == InfoItemType::Sinking ? _L("Sinking") :
_L("Variable layer height");
m_info_item_type = info_type;
}

View file

@ -51,6 +51,7 @@ enum class InfoItemType
CustomSupports,
CustomSeam,
MmuSegmentation,
Sinking,
VariableLayerHeight
};

View file

@ -47,7 +47,7 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str
{
m_sizer = new wxBoxSizer(wxVERTICAL);
m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross", "", wxDefaultSize, wxDefaultPosition, /*wxBU_LEFT | */wxBU_EXACTFIT);
m_delete_preset_btn = new ScalableButton(parent, wxID_ANY, "cross");
m_delete_preset_btn->SetFont(wxGetApp().normal_font());
m_delete_preset_btn->SetToolTip(_L("Delete this preset from this printer device"));
m_delete_preset_btn->Bind(wxEVT_BUTTON, &PresetForPrinter::DeletePreset, this);
@ -175,7 +175,7 @@ PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_
wxStaticText* label_top = new wxStaticText(this, wxID_ANY, _L("Descriptive name for the printer") + ":");
m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies", "", wxDefaultSize, wxDefaultPosition, wxBU_EXACTFIT);
m_add_preset_btn = new ScalableButton(this, wxID_ANY, "add_copies");
m_add_preset_btn->SetFont(wxGetApp().normal_font());
m_add_preset_btn->SetToolTip(_L("Add preset for this printer device"));
m_add_preset_btn->Bind(wxEVT_BUTTON, &PhysicalPrinterDialog::AddPreset, this);

View file

@ -82,9 +82,7 @@
#include "NotificationManager.hpp"
#include "PresetComboBoxes.hpp"
#include "MsgDialog.hpp"
#if ENABLE_PROJECT_DIRTY_STATE
#include "ProjectDirtyStateManager.hpp"
#endif // ENABLE_PROJECT_DIRTY_STATE
#ifdef __APPLE__
#include "Gizmos/GLGizmosManager.hpp"
@ -1045,6 +1043,12 @@ void Sidebar::search()
p->searcher.search();
}
void Sidebar::jump_to_option(const std::string& opt_key, Preset::Type type, const std::wstring& category)
{
//const Search::Option& opt = p->searcher.get_option(opt_key, type);
wxGetApp().get_tab(type)->activate_option(opt_key, category);
}
void Sidebar::jump_to_option(size_t selected)
{
const Search::Option& opt = p->searcher.get_option(selected);
@ -1454,13 +1458,9 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi
this->MSWUpdateDragImageOnLeave();
#endif // WIN32
#if ENABLE_PROJECT_DIRTY_STATE
bool res = (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
wxGetApp().mainframe->update_title();
return res;
#else
return (m_plater != nullptr) ? m_plater->load_files(filenames) : false;
#endif // ENABLE_PROJECT_DIRTY_STATE
}
// State to manage showing after export notifications and device ejecting
@ -1504,9 +1504,7 @@ struct Plater::priv
Preview *preview;
NotificationManager* notification_manager { nullptr };
#if ENABLE_PROJECT_DIRTY_STATE
ProjectDirtyStateManager dirty_state;
#endif // ENABLE_PROJECT_DIRTY_STATE
BackgroundSlicingProcess background_process;
bool suppressed_backround_processing_update { false };
@ -1565,9 +1563,7 @@ struct Plater::priv
std::string label_btn_export;
std::string label_btn_send;
#if ENABLE_RENDER_STATISTICS
bool show_render_statistic_dialog{ false };
#endif // ENABLE_RENDER_STATISTICS
static const std::regex pattern_bundle;
static const std::regex pattern_3mf;
@ -1578,7 +1574,6 @@ struct Plater::priv
priv(Plater *q, MainFrame *main_frame);
~priv();
#if ENABLE_PROJECT_DIRTY_STATE
bool is_project_dirty() const { return dirty_state.is_dirty(); }
void update_project_dirty_from_presets() { dirty_state.update_from_presets(); }
bool save_project_if_dirty() {
@ -1602,7 +1597,6 @@ struct Plater::priv
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_project_state_debug_window() const { dirty_state.render_debug_window(); }
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
#endif // ENABLE_PROJECT_DIRTY_STATE
enum class UpdateParams {
FORCE_FULL_SCREEN_REFRESH = 1,
@ -1650,11 +1644,8 @@ struct Plater::priv
BoundingBox scaled_bed_shape_bb() const;
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
#if ENABLE_ALLOW_NEGATIVE_Z
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
#else
std::vector<size_t> load_model_objects(const ModelObjectPtrs &model_objects);
#endif // ENABLE_ALLOW_NEGATIVE_Z
wxString get_export_file(GUI::FileType file_type);
const Selection& get_selection() const;
@ -2418,19 +2409,11 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
return obj_idxs;
}
#if ENABLE_ALLOW_NEGATIVE_Z
for (ModelObject* model_object : model.objects) {
if (!type_3mf && !type_zip_amf)
model_object->center_around_origin(false);
model_object->ensure_on_bed(is_project_file);
}
#else
for (ModelObject* model_object : model.objects) {
if (!type_3mf && !type_zip_amf)
model_object->center_around_origin(false);
model_object->ensure_on_bed();
}
#endif // ENABLE_ALLOW_NEGATIVE_Z
// check multi-part object adding for the SLA-printing
if (printer_technology == ptSLA) {
@ -2444,11 +2427,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
if (one_by_one) {
#if ENABLE_ALLOW_NEGATIVE_Z
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
#else
auto loaded_idxs = load_model_objects(model.objects);
#endif // ENABLE_ALLOW_NEGATIVE_Z
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
} else {
// This must be an .stl or .obj file, which may contain a maximum of one volume.
@ -2501,11 +2480,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// #define AUTOPLACEMENT_ON_LOAD
#if ENABLE_ALLOW_NEGATIVE_Z
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
#else
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects)
#endif // ENABLE_ALLOW_NEGATIVE_Z
{
const BoundingBoxf bed_shape = bed_shape_bb();
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
@ -2540,7 +2515,6 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
#endif /* AUTOPLACEMENT_ON_LOAD */
}
#if ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG
for (size_t i = 0; i < object->instances.size(); ++i) {
ModelInstance* instance = object->instances[i];
const Vec3d size = object->instance_bounding_box(i).size();
@ -2562,32 +2536,8 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
scaled_down = true;
}
}
#else
const Vec3d size = object->bounding_box().size();
const Vec3d ratio = size.cwiseQuotient(bed_size);
const double max_ratio = std::max(ratio(0), ratio(1));
if (max_ratio > 10000) {
// the size of the object is too big -> this could lead to overflow when moving to clipper coordinates,
// so scale down the mesh
double inv = 1. / max_ratio;
object->scale_mesh_after_creation(Vec3d(inv, inv, inv));
object->origin_translation = Vec3d::Zero();
object->center_around_origin();
scaled_down = true;
} else if (max_ratio > 5) {
const Vec3d inverse = 1.0 / max_ratio * Vec3d::Ones();
for (ModelInstance *instance : object->instances) {
instance->set_scaling_factor(inverse);
}
scaled_down = true;
}
#endif // ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG
#if ENABLE_ALLOW_NEGATIVE_Z
object->ensure_on_bed(allow_negative_z);
#else
object->ensure_on_bed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
#ifdef AUTOPLACEMENT_ON_LOAD
@ -2698,8 +2648,10 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type)
default: break;
}
std::string out_dir = (boost::filesystem::path(output_file).parent_path()).string();
wxFileDialog dlg(q, dlg_title,
from_path(output_file.parent_path()), from_path(output_file.filename()),
is_shapes_dir(out_dir) ? from_u8(wxGetApp().app_config->get_last_dir()) : from_path(output_file.parent_path()), from_path(output_file.filename()),
wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (dlg.ShowModal() != wxID_OK)
@ -2936,7 +2888,7 @@ void Plater::priv::update_print_volume_state()
void Plater::priv::process_validation_warning(const std::string& warning) const
{
if (warning.empty())
notification_manager->close_notification_of_type(NotificationType::PrintValidateWarning);
notification_manager->close_notification_of_type(NotificationType::ValidateWarning);
else {
std::string text = warning;
std::string hypertext = "";
@ -2959,9 +2911,9 @@ void Plater::priv::process_validation_warning(const std::string& warning) const
}
notification_manager->push_notification(
NotificationType::PrintValidateWarning,
NotificationManager::NotificationLevel::ImportantNotification,
text, hypertext, action_fn
NotificationType::ValidateWarning,
NotificationManager::NotificationLevel::WarningNotification,
_u8L("WARNING:") + "\n" + text, hypertext, action_fn
);
}
}
@ -3013,6 +2965,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
std::string err = background_process.validate(&warning);
if (err.empty()) {
notification_manager->set_all_slicing_errors_gray(true);
notification_manager->close_notification_of_type(NotificationType::ValidateError);
if (invalidated != Print::APPLY_STATUS_UNCHANGED && background_processing_enabled())
return_state |= UPDATE_BACKGROUND_PROCESS_RESTART;
@ -3028,7 +2981,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
else {
// The print is not valid.
// Show error as notification.
notification_manager->push_slicing_error_notification(err);
notification_manager->push_validate_error_notification(err);
return_state |= UPDATE_BACKGROUND_PROCESS_INVALID;
if (printer_technology == ptFFF) {
const Print* print = background_process.fff_print();
@ -3248,9 +3201,7 @@ void Plater::priv::replace_with_stl()
ModelObject* old_model_object = model.objects[object_idx];
ModelVolume* old_volume = old_model_object->volumes[volume_idx];
#if ENABLE_ALLOW_NEGATIVE_Z
bool sinking = old_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD;
#endif // ENABLE_ALLOW_NEGATIVE_Z
ModelObject* new_model_object = new_model.objects[0];
old_model_object->add_volume(*new_model_object->volumes[0]);
@ -3270,9 +3221,7 @@ void Plater::priv::replace_with_stl()
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
#if ENABLE_ALLOW_NEGATIVE_Z
if (!sinking)
#endif // ENABLE_ALLOW_NEGATIVE_Z
old_model_object->ensure_on_bed();
old_model_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
@ -3423,9 +3372,7 @@ void Plater::priv::reload_from_disk()
ModelObject* old_model_object = model.objects[sel_v.object_idx];
ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx];
#if ENABLE_ALLOW_NEGATIVE_Z
bool sinking = old_model_object->bounding_box().min.z() < SINKING_Z_THRESHOLD;
#endif // ENABLE_ALLOW_NEGATIVE_Z
bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string());
@ -3482,9 +3429,7 @@ void Plater::priv::reload_from_disk()
new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets);
std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
#if ENABLE_ALLOW_NEGATIVE_Z
if (!sinking)
#endif // ENABLE_ALLOW_NEGATIVE_Z
old_model_object->ensure_on_bed();
old_model_object->sort_volumes(wxGetApp().app_config->get("order_volumes") == "1");
@ -3613,10 +3558,8 @@ void Plater::priv::set_current_panel(wxPanel* panel)
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
view3D->set_as_dirty();
#if ENABLE_SCROLLABLE_LEGEND
// reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size
view3D->get_canvas3d()->reset_old_size();
#endif // ENABLE_SCROLLABLE_LEGEND
view_toolbar.select_item("3D");
if (notification_manager != nullptr)
notification_manager->set_in_preview(false);
@ -3637,10 +3580,8 @@ void Plater::priv::set_current_panel(wxPanel* panel)
preview->reload_print(true);
preview->set_as_dirty();
#if ENABLE_SCROLLABLE_LEGEND
// reset cached size to force a resize on next call to render() to keep imgui in synch with canvas size
preview->get_canvas3d()->reset_old_size();
#endif // ENABLE_SCROLLABLE_LEGEND
view_toolbar.select_item("Preview");
if (notification_manager != nullptr)
notification_manager->set_in_preview(true);
@ -4243,12 +4184,8 @@ bool Plater::priv::layers_height_allowed() const
return false;
int obj_idx = get_selected_object_idx();
#if ENABLE_ALLOW_NEGATIVE_Z
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && model.objects[obj_idx]->bounding_box().max.z() > SINKING_Z_THRESHOLD &&
config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
#else
return 0 <= obj_idx && obj_idx < (int)model.objects.size() && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
bool Plater::priv::can_mirror() const
@ -4482,9 +4419,7 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name)
this->undo_redo_stack().take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager(), snapshot_data);
this->undo_redo_stack().release_least_recently_used();
#if ENABLE_PROJECT_DIRTY_STATE
dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::TakeSnapshot);
#endif // ENABLE_PROJECT_DIRTY_STATE
// Save the last active preset name of a particular printer technology.
((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name();
@ -4526,13 +4461,8 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
if (printer_technology_changed) {
// Switching the printer technology when jumping forwards / backwards in time. Switch to the last active printer profile of the other type.
std::string s_pt = (it_snapshot->snapshot_data.printer_technology == ptFFF) ? "FFF" : "SLA";
#if ENABLE_PROJECT_DIRTY_STATE
if (!wxGetApp().check_and_save_current_preset_changes(format_wxstr(_L(
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
#else
if (! wxGetApp().check_unsaved_changes(format_wxstr(_L(
"%1% printer was active at the time the target Undo / Redo snapshot was taken. Switching to %1% printer requires reloading of %1% presets."), s_pt)))
#endif // ENABLE_PROJECT_DIRTY_STATE
// Don't switch the profiles.
return;
}
@ -4624,9 +4554,7 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
view3D->get_canvas3d()->force_main_toolbar_left_action(view3D->get_canvas3d()->get_main_toolbar_item_id("layersediting"));
}
#if ENABLE_PROJECT_DIRTY_STATE
dirty_state.update_from_undo_redo_stack(ProjectDirtyStateManager::UpdateType::UndoRedoTo);
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void Plater::priv::update_after_undo_redo(const UndoRedo::Snapshot& snapshot, bool /* temp_snapshot_was_taken */)
@ -4710,7 +4638,6 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame)
// Initialization performed in the private c-tor
}
#if ENABLE_PROJECT_DIRTY_STATE
bool Plater::is_project_dirty() const { return p->is_project_dirty(); }
void Plater::update_project_dirty_from_presets() { p->update_project_dirty_from_presets(); }
bool Plater::save_project_if_dirty() { return p->save_project_if_dirty(); }
@ -4719,9 +4646,9 @@ void Plater::reset_project_dirty_initial_presets() { p->reset_project_dirty_init
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void Plater::render_project_state_debug_window() const { p->render_project_state_debug_window(); }
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
#endif // ENABLE_PROJECT_DIRTY_STATE
Sidebar& Plater::sidebar() { return *p->sidebar; }
const Model& Plater::model() const { return p->model; }
Model& Plater::model() { return p->model; }
const Print& Plater::fff_print() const { return p->fff_print; }
Print& Plater::fff_print() { return p->fff_print; }
@ -4730,29 +4657,21 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
void Plater::new_project()
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!p->save_project_if_dirty())
return;
#endif // ENABLE_PROJECT_DIRTY_STATE
p->select_view_3D("3D");
#if ENABLE_PROJECT_DIRTY_STATE
take_snapshot(_L("New Project"));
Plater::SuppressSnapshots suppress(this);
reset();
reset_project_dirty_initial_presets();
update_project_dirty_from_presets();
#else
wxPostEvent(p->view3D->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL));
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void Plater::load_project()
{
#if ENABLE_PROJECT_DIRTY_STATE
if (!p->save_project_if_dirty())
return;
#endif // ENABLE_PROJECT_DIRTY_STATE
// Ask user for a project file name.
wxString input_file;
@ -4777,16 +4696,11 @@ void Plater::load_project(const wxString& filename)
std::vector<size_t> res = load_files(input_paths);
// if res is empty no data has been loaded
#if ENABLE_PROJECT_DIRTY_STATE
if (!res.empty()) {
p->set_project_filename(filename);
reset_project_dirty_initial_presets();
update_project_dirty_from_presets();
}
#else
if (!res.empty())
p->set_project_filename(filename);
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void Plater::add_model(bool imperial_units/* = false*/)
@ -4817,13 +4731,9 @@ void Plater::add_model(bool imperial_units/* = false*/)
}
Plater::TakeSnapshot snapshot(this, snapshot_label);
#if ENABLE_PROJECT_DIRTY_STATE
std::vector<size_t> res = load_files(paths, true, false, imperial_units);
if (!res.empty())
wxGetApp().mainframe->update_title();
#else
load_files(paths, true, false, imperial_units);
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void Plater::import_sl1_archive()
@ -4871,7 +4781,15 @@ void Plater::load_gcode(const wxString& filename)
// process gcode
GCodeProcessor processor;
processor.enable_producers(true);
processor.process_file(filename.ToUTF8().data(), false);
try
{
processor.process_file(filename.ToUTF8().data(), false);
}
catch (const std::exception& ex)
{
show_error(this, ex.what());
return;
}
p->gcode_result = std::move(processor.extract_result());
// show results
@ -5573,38 +5491,22 @@ void Plater::export_amf()
}
}
#if ENABLE_PROJECT_DIRTY_STATE
bool Plater::export_3mf(const boost::filesystem::path& output_path)
#else
void Plater::export_3mf(const boost::filesystem::path& output_path)
#endif // ENABLE_PROJECT_DIRTY_STATE
{
if (p->model.objects.empty())
#if ENABLE_PROJECT_DIRTY_STATE
return false;
#else
return;
#endif // ENABLE_PROJECT_DIRTY_STATE
wxString path;
bool export_config = true;
if (output_path.empty()) {
path = p->get_export_file(FT_3MF);
#if ENABLE_PROJECT_DIRTY_STATE
if (path.empty()) { return false; }
#else
if (path.empty()) { return; }
#endif // ENABLE_PROJECT_DIRTY_STATE
}
else
path = from_path(output_path);
if (!path.Lower().EndsWith(".3mf"))
#if ENABLE_PROJECT_DIRTY_STATE
return false;
#else
return;
#endif // ENABLE_PROJECT_DIRTY_STATE
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
const std::string path_u8 = into_u8(path);
@ -5613,7 +5515,6 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
ThumbnailData thumbnail_data;
ThumbnailsParams thumbnail_params = { {}, false, true, true, true };
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, thumbnail_params, Camera::EType::Ortho);
#if ENABLE_PROJECT_DIRTY_STATE
bool ret = Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data);
if (ret) {
// Success
@ -5625,17 +5526,6 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
}
return ret;
#else
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
// Success
p->statusbar()->set_status_text(format_wxstr(_L("3MF file exported to %s"), path));
p->set_project_filename(path);
}
else {
// Failure
p->statusbar()->set_status_text(format_wxstr(_L("Error exporting 3MF file %s"), path));
}
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void Plater::reload_from_disk()
@ -6083,9 +5973,22 @@ std::vector<std::string> Plater::get_colors_for_color_print(const GCodeProcessor
std::vector<std::string> colors = get_extruder_colors_from_plater_config(result);
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes)
if (code.type == CustomGCode::ColorChange)
colors.emplace_back(code.color);
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
if (wxGetApp().is_gcode_viewer() && result != nullptr) {
for (const CustomGCode::Item& code : result->custom_gcode_per_print_z) {
if (code.type == CustomGCode::ColorChange)
colors.emplace_back(code.color);
}
}
else {
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
for (const CustomGCode::Item& code : p->model.custom_gcode_per_print_z.gcodes) {
if (code.type == CustomGCode::ColorChange)
colors.emplace_back(code.color);
}
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
}
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
return colors;
}
@ -6140,7 +6043,6 @@ BoundingBoxf Plater::bed_shape_bb() const
return p->bed_shape_bb();
}
#if ENABLE_GCODE_WINDOW
void Plater::start_mapping_gcode_window()
{
p->preview->get_canvas3d()->start_mapping_gcode_window();
@ -6150,7 +6052,6 @@ void Plater::stop_mapping_gcode_window()
{
p->preview->get_canvas3d()->stop_mapping_gcode_window();
}
#endif // ENABLE_GCODE_WINDOW
void Plater::arrange()
{
@ -6189,13 +6090,11 @@ bool Plater::set_printer_technology(PrinterTechnology printer_technology)
//FIXME for SLA synchronize
//p->background_process.apply(Model)!
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
if (printer_technology == ptSLA) {
for (ModelObject* model_object : p->model.objects) {
model_object->ensure_on_bed();
}
}
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
p->label_btn_export = printer_technology == ptFFF ? L("Export G-code") : L("Export");
p->label_btn_send = printer_technology == ptFFF ? L("Send G-code") : L("Send to printer");
@ -6261,15 +6160,7 @@ void Plater::changed_object(int obj_idx)
return;
// recenter and re - align to Z = 0
auto model_object = p->model.objects[obj_idx];
#if ENABLE_ALLOW_NEGATIVE_Z
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
model_object->ensure_on_bed(this->p->printer_technology != ptSLA);
#else
model_object->ensure_on_bed(true);
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
#else
model_object->ensure_on_bed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
if (this->p->printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data, update the 3D scene.
@ -6288,17 +6179,11 @@ void Plater::changed_objects(const std::vector<size_t>& object_idxs)
return;
for (size_t obj_idx : object_idxs) {
#if ENABLE_ALLOW_NEGATIVE_Z
if (obj_idx < p->model.objects.size()) {
if (p->model.objects[obj_idx]->bounding_box().min.z() >= SINKING_Z_THRESHOLD)
// re - align to Z = 0
p->model.objects[obj_idx]->ensure_on_bed();
}
#else
if (obj_idx < p->model.objects.size())
// recenter and re - align to Z = 0
p->model.objects[obj_idx]->ensure_on_bed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
if (this->p->printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
@ -6340,7 +6225,7 @@ void Plater::mirror(Axis axis) { p->mirror(axis); }
void Plater::split_object() { p->split_object(); }
void Plater::split_volume() { p->split_volume(); }
void Plater::optimize_rotation() { p->m_ui_jobs.optimize_rotation();}
void Plater::update_object_menu() { p->menus.update_object_menu(); }
void Plater::update_menus() { p->menus.update(); }
void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); }
void Plater::copy_selection_to_clipboard()
@ -6582,14 +6467,11 @@ bool Plater::can_mirror() const { return p->can_mirror(); }
bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
#if ENABLE_PROJECT_DIRTY_STATE
const UndoRedo::Stack& Plater::undo_redo_stack_active() const { return p->undo_redo_stack(); }
#endif // ENABLE_PROJECT_DIRTY_STATE
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }
bool Plater::inside_snapshot_capture() { return p->inside_snapshot_capture(); }
#if ENABLE_RENDER_STATISTICS
void Plater::toggle_render_statistic_dialog()
{
p->show_render_statistic_dialog = !p->show_render_statistic_dialog;
@ -6599,7 +6481,6 @@ bool Plater::is_render_statistic_dialog_visible() const
{
return p->show_render_statistic_dialog;
}
#endif // ENABLE_RENDER_STATISTICS
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)

View file

@ -83,6 +83,7 @@ public:
void sys_color_changed();
void search();
void jump_to_option(size_t selected);
void jump_to_option(const std::string& opt_key, Preset::Type type, const std::wstring& category);
ObjectManipulation* obj_manipul();
ObjectList* obj_list();
@ -137,7 +138,6 @@ public:
Plater &operator=(const Plater &) = delete;
~Plater() = default;
#if ENABLE_PROJECT_DIRTY_STATE
bool is_project_dirty() const;
void update_project_dirty_from_presets();
bool save_project_if_dirty();
@ -146,9 +146,9 @@ public:
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
void render_project_state_debug_window() const;
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
#endif // ENABLE_PROJECT_DIRTY_STATE
Sidebar& sidebar();
const Model& model() const;
Model& model();
const Print& fff_print() const;
Print& fff_print();
@ -216,11 +216,7 @@ public:
void export_gcode(bool prefer_removable);
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
#if ENABLE_PROJECT_DIRTY_STATE
bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
#else
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
#endif // ENABLE_PROJECT_DIRTY_STATE
void reload_from_disk();
void replace_with_stl();
void reload_all_from_disk();
@ -255,9 +251,7 @@ public:
// For the memory statistics.
const Slic3r::UndoRedo::Stack& undo_redo_stack_main() const;
void clear_undo_redo_stack_main();
#if ENABLE_PROJECT_DIRTY_STATE
const Slic3r::UndoRedo::Stack& undo_redo_stack_active() const;
#endif // ENABLE_PROJECT_DIRTY_STATE
// 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();
@ -272,7 +266,7 @@ public:
std::vector<std::string> get_extruder_colors_from_plater_config(const GCodeProcessor::Result* const result = nullptr) const;
std::vector<std::string> get_colors_for_color_print(const GCodeProcessor::Result* const result = nullptr) const;
void update_object_menu();
void update_menus();
void show_action_buttons(const bool is_ready_to_slice) const;
wxString get_project_filename(const wxString& extension = wxEmptyString) const;
@ -288,10 +282,8 @@ public:
GLCanvas3D* get_current_canvas3D();
BoundingBoxf bed_shape_bb() const;
#if ENABLE_GCODE_WINDOW
void start_mapping_gcode_window();
void stop_mapping_gcode_window();
#endif // ENABLE_GCODE_WINDOW
void arrange();
void find_new_position(const ModelInstancePtrs &instances);
@ -410,10 +402,8 @@ public:
bool inside_snapshot_capture();
#if ENABLE_RENDER_STATISTICS
void toggle_render_statistic_dialog();
bool is_render_statistic_dialog_visible() const;
#endif // ENABLE_RENDER_STATISTICS
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition);

View file

@ -11,14 +11,14 @@
namespace Slic3r {
namespace GUI {
PreferencesDialog::PreferencesDialog(wxWindow* parent) :
PreferencesDialog::PreferencesDialog(wxWindow* parent, int selected_tab) :
DPIDialog(parent, wxID_ANY, _L("Preferences"), wxDefaultPosition,
wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
{
#ifdef __WXOSX__
isOSX = true;
#endif
build();
build(selected_tab);
}
static std::shared_ptr<ConfigOptionsGroup>create_options_tab(const wxString& title, wxBookCtrlBase* tabs)
@ -44,7 +44,7 @@ static void activate_options_tab(std::shared_ptr<ConfigOptionsGroup> optgroup)
sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 10);
}
void PreferencesDialog::build()
void PreferencesDialog::build(size_t selected_tab)
{
#ifdef _WIN32
wxGetApp().UpdateDarkUI(this);
@ -292,16 +292,6 @@ void PreferencesDialog::build()
option = Option(def, "seq_top_layer_only");
m_optgroup_gui->append_single_option_line(option);
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
def.label = L("Sequential slider shows gcode line numbers");
def.type = coBool;
def.tooltip = L("If enabled, the sequential slider, in preview, shows the gcode lines numbers."
"If disabled, the sequential slider, in preview, shows the move index.");
def.set_default_value(new ConfigOptionBool{ app_config->get("seq_top_gcode_indices") == "1" });
option = Option(def, "seq_top_gcode_indices");
m_optgroup_gui->append_single_option_line(option);
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
if (is_editor) {
def.label = L("Show sidebar collapse/expand button");
def.type = coBool;
@ -351,13 +341,20 @@ void PreferencesDialog::build()
option = Option(def, "tabs_as_menu");
m_optgroup_gui->append_single_option_line(option);
#endif
def.label = L("Show \"Did you know\" hints after start");
def.type = coBool;
def.tooltip = L("If enabled, useful hints are displayed at startup.");
def.set_default_value(new ConfigOptionBool{ app_config->get("show_hints") == "1" });
option = Option(def, "show_hints");
m_optgroup_gui->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.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" });
option = Option(def, "use_custom_toolbar_size");
m_optgroup_gui->append_single_option_line(option);
m_optgroup_gui->append_single_option_line(option);
}
activate_options_tab(m_optgroup_gui);
@ -389,6 +386,9 @@ void PreferencesDialog::build()
}
#endif // ENABLE_ENVIRONMENT_MAP
if (selected_tab < tabs->GetPageCount())
tabs->SetSelection(selected_tab);
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(tabs, 1, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5);
@ -438,12 +438,6 @@ void PreferencesDialog::accept(wxEvent&)
if (auto it = m_values.find("seq_top_layer_only"); it != m_values.end())
m_seq_top_layer_only_changed = app_config->get("seq_top_layer_only") != it->second;
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_seq_top_gcode_indices_changed = false;
if (auto it = m_values.find("seq_top_gcode_indices"); it != m_values.end())
m_seq_top_gcode_indices_changed = app_config->get("seq_top_gcode_indices") != it->second;
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
m_settings_layout_changed = false;
for (const std::string& key : { "old_settings_layout_mode",
"new_settings_layout_mode",

View file

@ -29,22 +29,16 @@ class PreferencesDialog : public DPIDialog
bool isOSX {false};
bool m_settings_layout_changed {false};
bool m_seq_top_layer_only_changed{ false };
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
bool m_seq_top_gcode_indices_changed{ false };
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
bool m_recreate_GUI{false};
public:
explicit PreferencesDialog(wxWindow* parent);
explicit PreferencesDialog(wxWindow* parent, int selected_tab = 0);
~PreferencesDialog() = default;
bool settings_layout_changed() const { return m_settings_layout_changed; }
bool seq_top_layer_only_changed() const { return m_seq_top_layer_only_changed; }
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
bool seq_seq_top_gcode_indices_changed() const { return m_seq_top_gcode_indices_changed; }
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
bool recreate_GUI() const { return m_recreate_GUI; }
void build();
void build(size_t selected_tab = 0);
void accept(wxEvent&);
protected:

View file

@ -13,8 +13,6 @@
#include <algorithm>
#include <assert.h>
#if ENABLE_PROJECT_DIRTY_STATE
namespace Slic3r {
namespace GUI {
@ -411,5 +409,3 @@ void ProjectDirtyStateManager::update_from_undo_redo_gizmo_stack(UpdateType type
} // namespace GUI
} // namespace Slic3r
#endif // ENABLE_PROJECT_DIRTY_STATE

View file

@ -3,8 +3,6 @@
#include "libslic3r/Preset.hpp"
#if ENABLE_PROJECT_DIRTY_STATE
namespace Slic3r {
namespace UndoRedo {
class Stack;
@ -90,7 +88,5 @@ private:
} // namespace GUI
} // namespace Slic3r
#endif // ENABLE_PROJECT_DIRTY_STATE
#endif // slic3r_ProjectDirtyStateManager_hpp_

View file

@ -13,9 +13,7 @@
#include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/Model.hpp"
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
#include "libslic3r/PresetBundle.hpp"
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
#include <GL/glew.h>
@ -701,14 +699,14 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
int rot_axis_max = 0;
if (rotation.isApprox(Vec3d::Zero())) {
for (unsigned int i : m_list) {
GLVolume &volume = *(*m_volumes)[i];
GLVolume &v = *(*m_volumes)[i];
if (m_mode == Instance) {
volume.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation());
volume.set_instance_offset(m_cache.volumes_data[i].get_instance_position());
v.set_instance_rotation(m_cache.volumes_data[i].get_instance_rotation());
v.set_instance_offset(m_cache.volumes_data[i].get_instance_position());
}
else if (m_mode == Volume) {
volume.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation());
volume.set_volume_offset(m_cache.volumes_data[i].get_volume_position());
v.set_volume_rotation(m_cache.volumes_data[i].get_volume_rotation());
v.set_volume_offset(m_cache.volumes_data[i].get_volume_position());
}
}
}
@ -749,22 +747,22 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
};
for (unsigned int i : m_list) {
GLVolume &volume = *(*m_volumes)[i];
GLVolume &v = *(*m_volumes)[i];
if (is_single_full_instance())
rotate_instance(volume, i);
rotate_instance(v, i);
else if (is_single_volume() || is_single_modifier()) {
if (transformation_type.independent())
volume.set_volume_rotation(volume.get_volume_rotation() + rotation);
v.set_volume_rotation(v.get_volume_rotation() + rotation);
else {
const Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
const Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_volume_rotation_matrix());
volume.set_volume_rotation(new_rotation);
v.set_volume_rotation(new_rotation);
}
}
else
{
if (m_mode == Instance)
rotate_instance(volume, i);
rotate_instance(v, i);
else if (m_mode == Volume) {
// extracts rotations from the composed transformation
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation);
@ -772,9 +770,9 @@ void Selection::rotate(const Vec3d& rotation, TransformationType transformation_
if (transformation_type.joint()) {
const Vec3d local_pivot = m_cache.volumes_data[i].get_instance_full_matrix().inverse() * m_cache.dragging_center;
const Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() - local_pivot);
volume.set_volume_offset(local_pivot + offset);
v.set_volume_offset(local_pivot + offset);
}
volume.set_volume_rotation(new_rotation);
v.set_volume_rotation(new_rotation);
}
}
}
@ -838,21 +836,8 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
if (!m_valid)
return;
#if ENABLE_ALLOW_NEGATIVE_Z
bool is_any_volume_sinking = false;
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
bool is_sla = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA;
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
#endif // ENABLE_ALLOW_NEGATIVE_Z
for (unsigned int i : m_list) {
GLVolume &volume = *(*m_volumes)[i];
#if ENABLE_ALLOW_NEGATIVE_Z
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
if (!is_sla)
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
is_any_volume_sinking |= !volume.is_modifier && std::find(m_cache.sinking_volumes.begin(), m_cache.sinking_volumes.end(), i) != m_cache.sinking_volumes.end();
#endif // ENABLE_ALLOW_NEGATIVE_Z
GLVolume &v = *(*m_volumes)[i];
if (is_single_full_instance()) {
if (transformation_type.relative()) {
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
@ -860,23 +845,23 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
// extracts scaling factors from the composed transformation
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
if (transformation_type.joint())
volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
volume.set_instance_scaling_factor(new_scale);
v.set_instance_scaling_factor(new_scale);
}
else {
if (transformation_type.world() && (std::abs(scale.x() - scale.y()) > EPSILON || std::abs(scale.x() - scale.z()) > EPSILON)) {
// Non-uniform scaling. Transform the scaling factors into the local coordinate system.
// This is only possible, if the instance rotation is mulitples of ninety degrees.
assert(Geometry::is_rotation_ninety_degrees(volume.get_instance_rotation()));
volume.set_instance_scaling_factor((volume.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs());
assert(Geometry::is_rotation_ninety_degrees(v.get_instance_rotation()));
v.set_instance_scaling_factor((v.get_instance_transformation().get_matrix(true, false, true, true).matrix().block<3, 3>(0, 0).transpose() * scale).cwiseAbs());
}
else
volume.set_instance_scaling_factor(scale);
v.set_instance_scaling_factor(scale);
}
}
else if (is_single_volume() || is_single_modifier())
volume.set_volume_scaling_factor(scale);
v.set_volume_scaling_factor(scale);
else {
Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale);
if (m_mode == Instance) {
@ -884,9 +869,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
// extracts scaling factors from the composed transformation
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
if (transformation_type.joint())
volume.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
v.set_instance_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center));
volume.set_instance_scaling_factor(new_scale);
v.set_instance_scaling_factor(new_scale);
}
else if (m_mode == Volume) {
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> new_matrix = (m * m_cache.volumes_data[i].get_volume_scale_matrix()).matrix().block(0, 0, 3, 3);
@ -894,9 +879,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm());
if (transformation_type.joint()) {
Vec3d offset = m * (m_cache.volumes_data[i].get_volume_position() + m_cache.volumes_data[i].get_instance_position() - m_cache.dragging_center);
volume.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
v.set_volume_offset(m_cache.dragging_center - m_cache.volumes_data[i].get_instance_position() + offset);
}
volume.set_volume_scaling_factor(new_scale);
v.set_volume_scaling_factor(new_scale);
}
}
}
@ -907,13 +892,9 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
else if (m_mode == Volume)
synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH
#if ENABLE_ALLOW_NEGATIVE_Z
if (!is_any_volume_sinking)
ensure_on_bed();
#endif // ENABLE_ALLOW_NEGATIVE_Z
this->set_bounding_boxes_dirty();
ensure_on_bed();
set_bounding_boxes_dirty();
}
void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
@ -964,14 +945,12 @@ void Selection::mirror(Axis axis)
if (!m_valid)
return;
bool single_full_instance = is_single_full_instance();
for (unsigned int i : m_list) {
GLVolume& v = *(*m_volumes)[i];
if (single_full_instance)
v.set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis));
if (is_single_full_instance())
v.set_instance_mirror(axis, -v.get_instance_mirror(axis));
else if (m_mode == Volume)
v.set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis));
v.set_volume_mirror(axis, -v.get_volume_mirror(axis));
}
#if !DISABLE_INSTANCES_SYNCH
@ -981,7 +960,7 @@ void Selection::mirror(Axis axis)
synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH
this->set_bounding_boxes_dirty();
set_bounding_boxes_dirty();
}
void Selection::translate(unsigned int object_idx, const Vec3d& displacement)
@ -1657,16 +1636,12 @@ void Selection::update_type()
void Selection::set_caches()
{
m_cache.volumes_data.clear();
#if ENABLE_ALLOW_NEGATIVE_Z
m_cache.sinking_volumes.clear();
#endif // ENABLE_ALLOW_NEGATIVE_Z
for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) {
const GLVolume& v = *(*m_volumes)[i];
m_cache.volumes_data.emplace(i, VolumeCache(v.get_volume_transformation(), v.get_instance_transformation()));
#if ENABLE_ALLOW_NEGATIVE_Z
if (v.is_sinking())
m_cache.sinking_volumes.push_back(i);
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
m_cache.dragging_center = get_bounding_box().center();
}
@ -2071,21 +2046,12 @@ void Selection::synchronize_unselected_instances(SyncRotationType sync_rotation_
assert(is_rotation_xy_synchronized(m_cache.volumes_data[i].get_instance_rotation(), m_cache.volumes_data[j].get_instance_rotation()));
switch (sync_rotation_type) {
case SYNC_ROTATION_NONE: {
#if ENABLE_ALLOW_NEGATIVE_Z
// z only rotation -> synch instance z
// The X,Y rotations should be synchronized from start to end of the rotation.
assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation()));
#if DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA)
#endif // DISABLE_ALLOW_NEGATIVE_Z_FOR_SLA
v->set_instance_offset(Z, volume->get_instance_offset().z());
break;
#else
// z only rotation -> keep instance z
// The X,Y rotations should be synchronized from start to end of the rotation.
assert(is_rotation_xy_synchronized(rotation, v->get_instance_rotation()));
break;
#endif // ENABLE_ALLOW_NEGATIVE_Z
}
case SYNC_ROTATION_FULL:
// rotation comes from place on face -> force given z
@ -2146,9 +2112,11 @@ void Selection::ensure_on_bed()
typedef std::map<std::pair<int, int>, double> InstancesToZMap;
InstancesToZMap instances_min_z;
for (GLVolume* volume : *m_volumes) {
if (!volume->is_wipe_tower && !volume->is_modifier) {
double min_z = volume->transformed_convex_hull_bounding_box().min(2);
for (size_t i = 0; i < m_volumes->size(); ++i) {
GLVolume* volume = (*m_volumes)[i];
if (!volume->is_wipe_tower && !volume->is_modifier &&
std::find(m_cache.sinking_volumes.begin(), m_cache.sinking_volumes.end(), i) == m_cache.sinking_volumes.end()) {
const double min_z = volume->transformed_convex_hull_bounding_box().min.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_min_z.find(instance);
if (it == instances_min_z.end())

View file

@ -186,10 +186,8 @@ private:
// to a set of indices of ModelVolume instances in ModelObject::instances
// Here the index means a position inside the respective std::vector, not ObjectID.
ObjectIdxsToInstanceIdxsMap content;
#if ENABLE_ALLOW_NEGATIVE_Z
// List of ids of the volumes which are sinking when starting dragging
std::vector<unsigned int> sinking_volumes;
#endif // ENABLE_ALLOW_NEGATIVE_Z
};
// Volumes owned by GLCanvas3D.

View file

@ -14,7 +14,8 @@
#include "GUI_App.hpp"
#include "MainFrame.hpp"
#include "wxExtensions.hpp"
#include "../libslic3r/LibraryCheck.hpp"
#include "../libslic3r/BlacklistedLibraryCheck.hpp"
#include "format.hpp"
#ifdef _WIN32
// The standard Windows includes.
@ -142,19 +143,23 @@ SysInfoDialog::SysInfoDialog()
m_opengl_info_html->SetMinSize(wxSize(-1, 16 * wxGetApp().em_unit()));
m_opengl_info_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
m_opengl_info_html->SetBorders(10);
const auto text = wxString::Format(
wxString blacklisted_libraries_message;
#ifdef WIN32
std::wstring blacklisted_libraries = BlacklistedLibraryCheck::get_instance().get_blacklisted_string().c_str();
if (! blacklisted_libraries.empty())
blacklisted_libraries_message = wxString("<br><b>") + _L("Blacklisted libraries loaded into PrusaSlicer process:") + "</b><br>" + blacklisted_libraries;
#endif // WIN32
const auto text = GUI::format_wxstr(
"<html>"
"<body bgcolor= %s link= %s>"
"<font color=%s>"
"%s"
"%s<br>%s<br>%s<br>%s"
"</font>"
"</body>"
"</html>", bgr_clr_str, text_clr_str, text_clr_str,
get_mem_info(true) + "<br>" + wxGetApp().get_gl_info(true, true) + "<br>Eigen vectorization supported: " + Eigen::SimdInstructionSetsInUse()
#ifdef WIN32
+ "<br><br><b>Blacklisted loaded libraries:</b><br>" + LibraryCheck::get_instance().get_blacklisted_string().c_str()
#endif
);
blacklisted_libraries_message,
get_mem_info(true), wxGetApp().get_gl_info(true, true),
"<b>" + _L("Eigen vectorization supported:") + "</b> " + Eigen::SimdInstructionSetsInUse());
m_opengl_info_html->SetPage(text);
main_sizer->Add(m_opengl_info_html, 1, wxEXPAND | wxBOTTOM, 15);

View file

@ -5,9 +5,7 @@
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Model.hpp"
#if ENABLE_VALIDATE_CUSTOM_GCODE
#include "libslic3r/GCode/GCodeProcessor.hpp"
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/PrintHost.hpp"
@ -1172,9 +1170,13 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
wxString page_title = translate_category(category, m_type);
auto cur_item = m_treectrl->GetFirstVisibleItem();
if (!cur_item || !m_treectrl->IsVisible(cur_item))
if (!cur_item)
return;
// We should to activate a tab with searched option, if it doesn't.
// And do it before finding of the cur_item to avoid a case when Tab isn't activated jet and all treeItems are invisible
wxGetApp().mainframe->select_tab(this);
while (cur_item) {
auto title = m_treectrl->GetItemText(cur_item);
if (page_title != title) {
@ -1186,8 +1188,6 @@ void Tab::activate_option(const std::string& opt_key, const wxString& category)
break;
}
// we should to activate a tab with searched option, if it doesn't.
wxGetApp().mainframe->select_tab(this);
Field* field = get_field(opt_key);
// focused selected field
@ -1254,9 +1254,7 @@ void Tab::on_presets_changed()
// to avoid needless preset loading from update() function
m_dependent_tabs.clear();
#if ENABLE_PROJECT_DIRTY_STATE
wxGetApp().plater()->update_project_dirty_from_presets();
#endif // ENABLE_PROJECT_DIRTY_STATE
}
void Tab::build_preset_description_line(ConfigOptionsGroup* optgroup)
@ -1744,7 +1742,6 @@ void TabPrint::clear_pages()
m_top_bottom_shell_thickness_explanation = nullptr;
}
#if ENABLE_VALIDATE_CUSTOM_GCODE
bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode)
{
std::vector<std::string> tags;
@ -1770,7 +1767,6 @@ static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group,
tab->update_dirty();
tab->on_value_change(opt_key, value);
}
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
void TabFilament::add_filament_overrides_page()
{
@ -1996,11 +1992,9 @@ void TabFilament::build()
page = add_options_page(L("Custom G-code"), "cog");
optgroup = page->new_optgroup(L("Start G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("start_filament_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2008,11 +2002,9 @@ void TabFilament::build()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("End G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("end_filament_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2139,16 +2131,9 @@ wxSizer* Tab::description_line_widget(wxWindow* parent, ogStaticText* *StaticTex
return sizer;
}
#if ENABLE_PROJECT_DIRTY_STATE
bool Tab::saved_preset_is_dirty() const { return m_presets->saved_is_dirty(); }
void Tab::update_saved_preset_from_current_preset() { m_presets->update_saved_preset_from_current_preset(); }
bool Tab::current_preset_is_dirty() const { return m_presets->current_is_dirty(); }
#else
bool Tab::current_preset_is_dirty()
{
return m_presets->current_is_dirty();
}
#endif // ENABLE_PROJECT_DIRTY_STATE
void TabPrinter::build()
{
@ -2323,11 +2308,9 @@ void TabPrinter::build_fff()
const int notes_field_height = 25; // 250
page = add_options_page(L("Custom G-code"), "cog");
optgroup = page->new_optgroup(L("Start G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("start_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2335,11 +2318,9 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("End G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("end_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2347,11 +2328,9 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Before layer change G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("before_layer_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2359,11 +2338,9 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("After layer change G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("layer_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2371,11 +2348,9 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Tool change G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("toolchange_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2383,11 +2358,9 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Between objects G-code (for sequential printing)"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("between_objects_gcode");
option.opt.full_width = true;
option.opt.is_code = true;
@ -2395,33 +2368,27 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Color Change G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("color_change_gcode");
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Pause Print G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("pause_print_gcode");
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Template Custom G-code"), 0);
#if ENABLE_VALIDATE_CUSTOM_GCODE
optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup, opt_key, value);
};
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
option = optgroup->get_option("template_custom_gcode");
option.opt.is_code = true;
option.opt.height = gcode_field_height;//150;
@ -3083,6 +3050,7 @@ void Tab::load_current_preset()
if (!wxGetApp().tabs_as_menu()) {
std::string bmp_name = tab->type() == Slic3r::Preset::TYPE_FILAMENT ? "spool" :
tab->type() == Slic3r::Preset::TYPE_SLA_MATERIAL ? "resin" : "cog";
tab->Hide(); // #ys_WORKAROUND : Hide tab before inserting to avoid unwanted rendering of the tab
dynamic_cast<Notebook*>(wxGetApp().tab_panel())->InsertPage(wxGetApp().tab_panel()->FindPage(this), tab, tab->title(), bmp_name);
}
else
@ -3904,7 +3872,6 @@ void TabPrinter::apply_extruder_cnt_from_cache()
}
}
#if ENABLE_VALIDATE_CUSTOM_GCODE
bool Tab::validate_custom_gcodes()
{
if (m_type != Preset::TYPE_FILAMENT &&
@ -3930,7 +3897,6 @@ bool Tab::validate_custom_gcodes()
}
return valid;
}
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
void TabPrinter::update_machine_limits_description(const MachineLimitsUsage usage)
{
@ -4157,7 +4123,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la
return optgroup;
}
#if ENABLE_VALIDATE_CUSTOM_GCODE
const ConfigOptionsGroupShp Page::get_optgroup(const wxString& title) const
{
for (ConfigOptionsGroupShp optgroup : m_optgroups) {
@ -4167,7 +4132,6 @@ const ConfigOptionsGroupShp Page::get_optgroup(const wxString& title) const
return nullptr;
}
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
void TabSLAMaterial::build()
{

View file

@ -79,9 +79,7 @@ public:
Field* get_field(const t_config_option_key& opt_key, int opt_index = -1) const;
bool set_value(const t_config_option_key& opt_key, const boost::any& value);
ConfigOptionsGroupShp new_optgroup(const wxString& title, int noncommon_label_width = -1);
#if ENABLE_VALIDATE_CUSTOM_GCODE
const ConfigOptionsGroupShp get_optgroup(const wxString& title) const;
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
bool set_item_colour(const wxColour *clr) {
if (m_item_color != clr) {
@ -269,11 +267,7 @@ public:
Preset::Type type() const { return m_type; }
// The tab is already constructed.
bool completed() const { return m_completed; }
#if ENABLE_PROJECT_DIRTY_STATE
virtual bool supports_printer_technology(const PrinterTechnology tech) const = 0;
#else
virtual bool supports_printer_technology(const PrinterTechnology tech) = 0;
#endif // ENABLE_PROJECT_DIRTY_STATE
void create_preset_tab();
void add_scaled_button(wxWindow* parent, ScalableButton** btn, const std::string& icon_name,
@ -336,13 +330,9 @@ public:
Field* get_field(const t_config_option_key &opt_key, Page** selected_page, int opt_index = -1);
void toggle_option(const std::string& opt_key, bool toggle, int opt_index = -1);
wxSizer* description_line_widget(wxWindow* parent, ogStaticText** StaticText, wxString text = wxEmptyString);
#if ENABLE_PROJECT_DIRTY_STATE
bool current_preset_is_dirty() const;
bool saved_preset_is_dirty() const;
void update_saved_preset_from_current_preset();
#else
bool current_preset_is_dirty();
#endif // ENABLE_PROJECT_DIRTY_STATE
DynamicPrintConfig* get_config() { return m_config; }
PresetCollection* get_presets() { return m_presets; }
@ -357,11 +347,9 @@ public:
const std::map<wxString, std::string>& get_category_icon_map() { return m_category_icon; }
#if ENABLE_VALIDATE_CUSTOM_GCODE
static bool validate_custom_gcode(const wxString& title, const std::string& gcode);
bool validate_custom_gcodes();
bool validate_custom_gcodes_was_shown { false };
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
bool validate_custom_gcodes_was_shown{ false };
protected:
void create_line_with_widget(ConfigOptionsGroup* optgroup, const std::string& opt_key, const wxString& path, widget_t widget);
@ -395,11 +383,7 @@ public:
void toggle_options() override;
void update() override;
void clear_pages() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
#endif // ENABLE_PROJECT_DIRTY_STATE
private:
ogStaticText* m_recommended_thin_wall_thickness_description_line = nullptr;
@ -428,11 +412,7 @@ public:
void toggle_options() override;
void update() override;
void clear_pages() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptFFF; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptFFF; }
#endif // ENABLE_PROJECT_DIRTY_STATE
};
class TabPrinter : public Tab
@ -485,11 +465,7 @@ public:
void on_preset_loaded() override;
void init_options_list() override;
void msw_rescale() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology /* tech */) const override { return true; }
#else
bool supports_printer_technology(const PrinterTechnology /* tech */) override { return true; }
#endif // ENABLE_PROJECT_DIRTY_STATE
wxSizer* create_bed_shape_widget(wxWindow* parent);
void cache_extruder_cnt();
@ -508,11 +484,7 @@ public:
void toggle_options() override {};
void update() override;
void init_options_list() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
#endif // ENABLE_PROJECT_DIRTY_STATE
};
class TabSLAPrint : public Tab
@ -530,11 +502,7 @@ public:
void toggle_options() override;
void update() override;
void clear_pages() override;
#if ENABLE_PROJECT_DIRTY_STATE
bool supports_printer_technology(const PrinterTechnology tech) const override { return tech == ptSLA; }
#else
bool supports_printer_technology(const PrinterTechnology tech) override { return tech == ptSLA; }
#endif // ENABLE_PROJECT_DIRTY_STATE
};
} // GUI

View file

@ -36,6 +36,8 @@
#include "../GUI/GUI.hpp"
#include "../GUI/I18N.hpp"
#include "../GUI/MsgDialog.hpp"
#include "../GUI/GUI_App.hpp"
#include "../GUI/Mainframe.hpp"
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
@ -341,7 +343,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
wxProgressDialog progress_dialog(
_L("Model fixing"),
_L("Exporting model") + "...",
100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
100, GUI::wxGetApp().mainframe, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
// Executing the calculation in a background thread, so that the COM context could be created with its own threading model.
// (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context).
bool success = false;
@ -423,12 +425,10 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
if (canceled) {
// Nothing to show.
} else if (success) {
//wxMessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT);
Slic3r::GUI::MessageDialog dlg(nullptr, _(L("Model repaired successfully")), _(L("Model Repair by the Netfabb service")), wxICON_INFORMATION | wxOK_DEFAULT);
Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repaired successfully"), _L("Model Repair by the Netfabb service"), wxICON_INFORMATION | wxOK);
dlg.ShowModal();
} else {
//wxMessageDialog dlg(nullptr, _(L("Model repair failed:")) + " \n" + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT);
Slic3r::GUI::MessageDialog dlg(nullptr, _(L("Model repair failed:")) + " \n" + _(progress.message), _(L("Model Repair by the Netfabb service")), wxICON_ERROR | wxOK_DEFAULT);
Slic3r::GUI::MessageDialog dlg(nullptr, _L("Model repair failed:") + " \n" + _(progress.message), _L("Model Repair by the Netfabb service"), wxICON_ERROR | wxOK);
dlg.ShowModal();
}
worker_thread.join();