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

This commit is contained in:
tamasmeszaros 2018-07-18 10:14:15 +02:00
commit 9559ad77e5
121 changed files with 18093 additions and 3459 deletions

View file

@ -1,11 +1,11 @@
#include "AppController.hpp"
#include <future>
#include <chrono>
#include <sstream>
#include <cstdarg>
#include <thread>
#include <unordered_map>
#include <chrono>
#include <slic3r/GUI/GUI.hpp>
#include <slic3r/GUI/PresetBundle.hpp>
@ -13,6 +13,7 @@
#include <PrintConfig.hpp>
#include <Print.hpp>
#include <PrintExport.hpp>
#include <Geometry.hpp>
#include <Model.hpp>
#include <Utils.hpp>
@ -248,7 +249,6 @@ void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri)
Slic3r::trace(3, _(L("Slicing process finished.")));
}
void PrintController::slice()
{
auto pri = global_progress_indicator();
@ -390,4 +390,61 @@ void IProgressIndicator::message_fmt(
message(ss.str());
}
const PrintConfig &PrintController::config() const
{
return print_->config;
}
void AppController::arrange_model()
{
auto ftr = std::async(
supports_asynch()? std::launch::async : std::launch::deferred,
[this]()
{
unsigned count = 0;
for(auto obj : model_->objects) count += obj->instances.size();
auto pind = global_progress_indicator();
float pmax = 1.0;
if(pind) {
pmax = pind->max();
// Set the range of the progress to the object count
pind->max(count);
}
auto dist = print_ctl()->config().min_object_distance();
BoundingBoxf bb(print_ctl()->config().bed_shape.values);
if(pind) pind->update(0, _(L("Arranging objects...")));
try {
model_->arrange_objects(dist, &bb, [pind, count](unsigned rem){
if(pind) pind->update(count - rem, _(L("Arranging objects...")));
});
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR,
_(L("Could not arrange model objects! "
"Some geometries may be invalid.")),
_(L("Exception occurred")));
}
// Restore previous max value
if(pind) {
pind->max(pmax);
pind->update(0, _(L("Arranging done.")));
}
});
while( ftr.wait_for(std::chrono::milliseconds(10))
!= std::future_status::ready) {
process_events();
}
}
}

View file

@ -14,6 +14,7 @@ namespace Slic3r {
class Model;
class Print;
class PrintObject;
class PrintConfig;
/**
* @brief A boilerplate class for creating application logic. It should provide
@ -226,6 +227,7 @@ public:
*/
void slice_to_png();
const PrintConfig& config() const;
};
/**
@ -278,6 +280,8 @@ public:
*/
void set_global_progress_indicator(unsigned gauge_id,
unsigned statusbar_id);
void arrange_model();
};
}

View file

@ -224,7 +224,7 @@ class Wrapper: public IProgressIndicator, public wxEvtHandler {
}
void _state(unsigned st) {
if( st <= max() ) {
if( st <= IProgressIndicator::max() ) {
Base::state(st);
if(!gauge_->IsShown()) showProgress(true);
@ -261,7 +261,14 @@ public:
}
virtual void state(float val) override {
if(val >= 1.0) state(unsigned(val));
state(unsigned(val));
}
virtual void max(float val) override {
if(val > 1.0) {
gauge_->SetRange(static_cast<int>(val));
IProgressIndicator::max(val);
}
}
void state(unsigned st) {
@ -269,7 +276,9 @@ public:
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
evt->SetInt(st);
wxQueueEvent(this, evt);
} else _state(st);
} else {
_state(st);
}
}
virtual void message(const string & msg) override {
@ -279,7 +288,7 @@ public:
virtual void message_fmt(const string& fmt, ...) override {
va_list arglist;
va_start(arglist, fmt);
message_ = wxString::Format(wxString(fmt), arglist);
message_ = wxString::Format(fmt, arglist);
va_end(arglist);
}

View file

@ -2,7 +2,6 @@
#include "3DScene.hpp"
#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/ExtrusionEntity.hpp"
#include "../../libslic3r/ExtrusionEntityCollection.hpp"
#include "../../libslic3r/Geometry.hpp"
@ -28,8 +27,15 @@
#include <wx/image.h>
#include <wx/settings.h>
#include <Eigen/Dense>
#include "GUI.hpp"
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
namespace Slic3r {
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
@ -198,6 +204,34 @@ const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
: m_angle_z(0.0f)
, m_scale_factor(1.0f)
, m_dirty(true)
, composite_id(-1)
, select_group_id(-1)
, drag_group_id(-1)
, extruder_id(0)
, selected(false)
, is_active(true)
, zoom_to_volumes(true)
, outside_printer_detection_enabled(true)
, is_outside(false)
, hover(false)
, is_modifier(false)
, is_wipe_tower(false)
, tverts_range(0, size_t(-1))
, qverts_range(0, size_t(-1))
{
m_world_mat = std::vector<float>(UNIT_MATRIX, std::end(UNIT_MATRIX));
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = a;
set_render_color(r, g, b, a);
}
void GLVolume::set_render_color(float r, float g, float b, float a)
{
render_color[0] = r;
@ -218,12 +252,7 @@ void GLVolume::set_render_color(const float* rgba, unsigned int size)
void GLVolume::set_render_color()
{
if (selected)
{
if (is_outside)
set_render_color(SELECTED_OUTSIDE_COLOR, 4);
else
set_render_color(SELECTED_COLOR, 4);
}
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (is_outside)
@ -232,6 +261,52 @@ void GLVolume::set_render_color()
set_render_color(color, 4);
}
const Pointf3& GLVolume::get_origin() const
{
return m_origin;
}
void GLVolume::set_origin(const Pointf3& origin)
{
m_origin = origin;
m_dirty = true;
}
void GLVolume::set_angle_z(float angle_z)
{
m_angle_z = angle_z;
m_dirty = true;
}
void GLVolume::set_scale_factor(float scale_factor)
{
m_scale_factor = scale_factor;
m_dirty = true;
}
const std::vector<float>& GLVolume::world_matrix() const
{
if (m_dirty)
{
Eigen::Transform<float, 3, Eigen::Affine> m = Eigen::Transform<float, 3, Eigen::Affine>::Identity();
m.translate(Eigen::Vector3f((float)m_origin.x, (float)m_origin.y, (float)m_origin.z));
m.rotate(Eigen::AngleAxisf(m_angle_z, Eigen::Vector3f::UnitZ()));
m.scale(m_scale_factor);
::memcpy((void*)m_world_mat.data(), (const void*)m.data(), 16 * sizeof(float));
m_dirty = false;
}
return m_world_mat;
}
BoundingBoxf3 GLVolume::transformed_bounding_box() const
{
if (m_dirty)
m_transformed_bounding_box = bounding_box.transformed(world_matrix());
return m_transformed_bounding_box;
}
void GLVolume::set_range(double min_z, double max_z)
{
this->qverts_range.first = 0;
@ -272,14 +347,16 @@ void GLVolume::render() const
if (!is_active)
return;
glCullFace(GL_BACK);
glPushMatrix();
glTranslated(this->origin.x, this->origin.y, this->origin.z);
::glCullFace(GL_BACK);
::glPushMatrix();
::glTranslated(m_origin.x, m_origin.y, m_origin.z);
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
else
this->indexed_vertex_array.render();
glPopMatrix();
::glPopMatrix();
}
void GLVolume::render_using_layer_height() const
@ -297,6 +374,7 @@ void GLVolume::render_using_layer_height() const
GLint z_texture_row_to_normalized_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_texture_row_to_normalized") : -1;
GLint z_cursor_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor") : -1;
GLint z_cursor_band_width_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "z_cursor_band_width") : -1;
GLint world_matrix_id = (layer_height_texture_data.shader_id > 0) ? glGetUniformLocation(layer_height_texture_data.shader_id, "volume_world_matrix") : -1;
if (z_to_texture_row_id >= 0)
glUniform1f(z_to_texture_row_id, (GLfloat)layer_height_texture_z_to_row_id());
@ -310,14 +388,20 @@ void GLVolume::render_using_layer_height() const
if (z_cursor_band_width_id >= 0)
glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width);
unsigned int w = layer_height_texture_width();
unsigned int h = layer_height_texture_height();
if (world_matrix_id >= 0)
::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
GLsizei w = (GLsizei)layer_height_texture_width();
GLsizei h = (GLsizei)layer_height_texture_height();
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, layer_height_texture_data.texture_id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, w / 2, h / 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level0());
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, w / 2, h / 2, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, layer_height_texture_data_ptr_level1());
render();
@ -327,6 +411,128 @@ void GLVolume::render_using_layer_height() const
glUseProgram(current_program_id);
}
void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const
{
if (!is_active)
return;
if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
return;
if (layer_height_texture_data.can_use())
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
render_using_layer_height();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
return;
}
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
if (color_id >= 0)
{
float color[4];
::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float));
::glUniform4fv(color_id, 1, (const GLfloat*)color);
}
else
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
if (detection_id != -1)
::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
if (worldmatrix_id != -1)
::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
return;
}
if (color_id >= 0)
::glUniform4fv(color_id, 1, (const GLfloat*)render_color);
else
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
if (detection_id != -1)
::glUniform1i(detection_id, outside_printer_detection_enabled ? 1 : 0);
if (worldmatrix_id != -1)
::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data());
::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
::glPushMatrix();
::glTranslated(m_origin.x, m_origin.y, m_origin.z);
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (n_triangles > 0)
{
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id);
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4));
}
if (n_quads > 0)
{
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id);
::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4));
}
::glPopMatrix();
}
void GLVolume::render_legacy() const
{
assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
if (!is_active)
return;
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
return;
}
::glColor4f(render_color[0], render_color[1], render_color[2], render_color[3]);
::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data());
::glPushMatrix();
::glTranslated(m_origin.x, m_origin.y, m_origin.z);
::glRotatef(m_angle_z * 180.0f / PI, 0.0f, 0.0f, 1.0f);
::glScalef(m_scale_factor, m_scale_factor, m_scale_factor);
if (n_triangles > 0)
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first);
if (n_quads > 0)
::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first);
::glPopMatrix();
}
double GLVolume::layer_height_texture_z_to_row_id() const
{
return (this->layer_height_texture.get() == nullptr) ? 0.0 : double(this->layer_height_texture->cells - 1) / (double(this->layer_height_texture->width) * this->layer_height_texture_data.print_object->model_object()->bounding_box().max.z);
@ -399,7 +605,6 @@ std::vector<int> GLVolumeCollection::load_object(
for (int instance_idx : instance_idxs) {
const ModelInstance *instance = model_object->instances[instance_idx];
TriangleMesh mesh = model_volume->mesh;
instance->transform_mesh(&mesh);
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
@ -434,13 +639,15 @@ std::vector<int> GLVolumeCollection::load_object(
}
v.is_modifier = model_volume->modifier;
v.outside_printer_detection_enabled = !model_volume->modifier;
v.set_origin(Pointf3(instance->offset.x, instance->offset.y, 0.0));
v.set_angle_z(instance->rotation);
v.set_scale_factor(instance->scaling_factor);
}
}
return volumes_idx;
}
int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs)
{
@ -461,7 +668,8 @@ int GLVolumeCollection::load_wipe_tower_preview(
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
v.origin = Pointf3(pos_x, pos_y, 0.);
v.set_origin(Pointf3(pos_x, pos_y, 0.));
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
@ -486,102 +694,23 @@ void GLVolumeCollection::render_VBOs() const
GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1;
GLint print_box_min_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.min") : -1;
GLint print_box_max_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.max") : -1;
GLint print_box_origin_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_origin") : -1;
GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1;
GLint print_box_worldmatrix_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_world_matrix") : -1;
for (GLVolume *volume : this->volumes) {
if (!volume->is_active)
continue;
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (!volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
continue;
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
for (GLVolume *volume : this->volumes)
{
if (volume->layer_height_texture_data.can_use())
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
volume->generate_layer_height_texture(volume->layer_height_texture_data.print_object, false);
volume->render_using_layer_height();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue;
}
volume->set_render_color();
GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
if (color_id >= 0)
{
float color[4];
::memcpy((void*)color, (const void*)volume->render_color, 4 * sizeof(float));
::glUniform4fv(color_id, 1, (const GLfloat*)color);
}
else
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
if (print_box_origin_id != -1)
{
float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
}
volume->render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue;
}
if (color_id >= 0)
::glUniform4fv(color_id, 1, (const GLfloat*)volume->render_color);
else
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
volume->set_render_color();
if (print_box_min_id != -1)
::glUniform3fv(print_box_min_id, 1, (const GLfloat*)print_box_min);
if (print_box_max_id != -1)
::glUniform3fv(print_box_max_id, 1, (const GLfloat*)print_box_max);
if (print_box_origin_id != -1)
{
float origin[4] = { (float)volume->origin.x, (float)volume->origin.y, (float)volume->origin.z, volume->outside_printer_detection_enabled ? 1.0f : 0.0f };
::glUniform4fv(print_box_origin_id, 1, (const GLfloat*)origin);
}
::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)));
::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr);
bool has_offset = (volume->origin.x != 0) || (volume->origin.y != 0) || (volume->origin.z != 0);
if (has_offset) {
::glPushMatrix();
::glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
}
if (n_triangles > 0) {
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id);
::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(volume->tverts_range.first * 4));
}
if (n_quads > 0) {
::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id);
::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(volume->qverts_range.first * 4));
}
if (has_offset)
::glPopMatrix();
volume->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id);
}
::glBindBuffer(GL_ARRAY_BUFFER, 0);
@ -602,43 +731,10 @@ void GLVolumeCollection::render_legacy() const
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
for (GLVolume *volume : this->volumes) {
assert(! volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
if (!volume->is_active)
continue;
for (GLVolume *volume : this->volumes)
{
volume->set_render_color();
GLsizei n_triangles = GLsizei(std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first));
GLsizei n_quads = GLsizei(std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first));
if (n_triangles + n_quads == 0)
{
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisableClientState(GL_NORMAL_ARRAY);
::glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
volume->render();
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_NORMAL_ARRAY);
continue;
}
glColor4f(volume->render_color[0], volume->render_color[1], volume->render_color[2], volume->render_color[3]);
glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data() + 3);
glNormalPointer(GL_FLOAT, 6 * sizeof(float), volume->indexed_vertex_array.vertices_and_normals_interleaved.data());
bool has_offset = volume->origin.x != 0 || volume->origin.y != 0 || volume->origin.z != 0;
if (has_offset) {
glPushMatrix();
glTranslated(volume->origin.x, volume->origin.y, volume->origin.z);
}
if (n_triangles > 0)
glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, volume->indexed_vertex_array.triangle_indices.data() + volume->tverts_range.first);
if (n_quads > 0)
glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, volume->indexed_vertex_array.quad_indices.data() + volume->qverts_range.first);
if (has_offset)
glPopMatrix();
volume->render_legacy();
}
glDisableClientState(GL_VERTEX_ARRAY);
@ -1486,11 +1582,12 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
unsigned int _3DScene::TextureBase::finalize()
{
if (!m_data.empty()) {
if ((m_tex_id == 0) && !m_data.empty()) {
// sends buffer to gpu
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_tex_id);
::glBindTexture(GL_TEXTURE_2D, m_tex_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)m_data.data());
::glBindTexture(GL_TEXTURE_2D, (GLuint)m_tex_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_tex_width, (GLsizei)m_tex_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)m_data.data());
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
@ -1589,7 +1686,7 @@ bool _3DScene::LegendTexture::generate(const GCodePreviewData& preview_data, con
m_data.clear();
// collects items to render
auto title = GUI::L_str(preview_data.get_legend_title());
auto title = _(preview_data.get_legend_title());
const GCodePreviewData::LegendItemsList& items = preview_data.get_legend_items(tool_colors);
unsigned int items_count = (unsigned int)items.size();
@ -1949,6 +2046,11 @@ void _3DScene::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
s_canvas_mgr.update_volumes_colors_by_extruder(canvas);
}
void _3DScene::update_gizmos_data(wxGLCanvas* canvas)
{
s_canvas_mgr.update_gizmos_data(canvas);
}
void _3DScene::render(wxGLCanvas* canvas)
{
s_canvas_mgr.render(canvas);
@ -2044,6 +2146,16 @@ void _3DScene::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, vo
s_canvas_mgr.register_on_gizmo_scale_uniformly_callback(canvas, callback);
}
void _3DScene::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_on_gizmo_rotate_callback(canvas, callback);
}
void _3DScene::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
{
s_canvas_mgr.register_on_update_geometry_info_callback(canvas, callback);
}
static inline int hex_digit_to_int(const char c)
{
return

View file

@ -240,7 +240,7 @@ class GLVolume {
edit_band_width = 0.0f;
}
bool can_use() { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
bool can_use() const { return (texture_id > 0) && (shader_id > 0) && (print_object != nullptr); }
};
public:
@ -249,44 +249,27 @@ public:
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f) :
composite_id(-1),
select_group_id(-1),
drag_group_id(-1),
extruder_id(0),
selected(false),
is_active(true),
zoom_to_volumes(true),
outside_printer_detection_enabled(true),
is_outside(false),
hover(false),
is_modifier(false),
is_wipe_tower(false),
tverts_range(0, size_t(-1)),
qverts_range(0, size_t(-1))
{
color[0] = r;
color[1] = g;
color[2] = b;
color[3] = a;
set_render_color(r, g, b, a);
}
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
std::vector<int> load_object(
const ModelObject *model_object,
const std::vector<int> &instance_idxs,
const std::string &color_by,
const std::string &select_by,
const std::string &drag_by);
private:
// Offset of the volume to be rendered.
Pointf3 m_origin;
// Rotation around Z axis of the volume to be rendered.
float m_angle_z;
// Scale factor of the volume to be rendered.
float m_scale_factor;
// World matrix of the volume to be rendered.
std::vector<float> m_world_mat;
// Bounding box of this volume, in unscaled coordinates.
mutable BoundingBoxf3 m_transformed_bounding_box;
// Whether or not is needed to recalculate the world matrix.
mutable bool m_dirty;
int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs);
public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box;
// Offset of the volume to be rendered.
Pointf3 origin;
// Color of the triangles / quads held by this volume.
float color[4];
// Color used to render this volume.
@ -333,10 +316,17 @@ public:
// Sets render color in dependence of current state
void set_render_color();
const Pointf3& get_origin() const;
void set_origin(const Pointf3& origin);
void set_angle_z(float angle_z);
void set_scale_factor(float scale_factor);
int object_idx() const { return this->composite_id / 1000000; }
int volume_idx() const { return (this->composite_id / 1000) % 1000; }
int instance_idx() const { return this->composite_id % 1000; }
BoundingBoxf3 transformed_bounding_box() const { BoundingBoxf3 bb = this->bounding_box; bb.translate(this->origin); return bb; }
const std::vector<float>& world_matrix() const;
BoundingBoxf3 transformed_bounding_box() const;
bool empty() const { return this->indexed_vertex_array.empty(); }
bool indexed() const { return this->indexed_vertex_array.indexed(); }
@ -344,6 +334,9 @@ public:
void set_range(coordf_t low, coordf_t high);
void render() const;
void render_using_layer_height() const;
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
void render_legacy() const;
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); }
@ -568,6 +561,7 @@ public:
static void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
static void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
static void update_gizmos_data(wxGLCanvas* canvas);
static void render(wxGLCanvas* canvas);
@ -590,6 +584,8 @@ public:
static void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
static void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
static void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
static void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
static std::vector<int> load_object(wxGLCanvas* canvas, const ModelObject* model_object, int obj_idx, std::vector<int> instance_idxs);
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);

View file

@ -45,6 +45,22 @@ namespace Slic3r { namespace GUI {
set_undo_bitmap(&bmp);
set_undo_to_sys_bitmap(&bmp);
switch (m_opt.type)
{
case coPercents:
case coFloats:
case coStrings:
case coBools:
case coInts: {
auto tag_pos = m_opt_id.find("#");
if (tag_pos != std::string::npos)
m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size()));
break;
}
default:
break;
}
BUILD();
}
@ -77,7 +93,7 @@ namespace Slic3r { namespace GUI {
wxString Field::get_tooltip_text(const wxString& default_string)
{
wxString tooltip_text("");
wxString tooltip = L_str(m_opt.tooltip);
wxString tooltip = _(m_opt.tooltip);
if (tooltip.length() > 0)
tooltip_text = tooltip + "(" + _(L("default")) + ": " +
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") +
@ -161,10 +177,10 @@ namespace Slic3r { namespace GUI {
case coFloat:
{
double val = m_opt.type == coFloats ?
static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(0) :
static_cast<const ConfigOptionFloats*>(m_opt.default_value)->get_at(m_opt_idx) :
m_opt.type == coFloat ?
m_opt.default_value->getFloat() :
static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(0);
static_cast<const ConfigOptionPercents*>(m_opt.default_value)->get_at(m_opt_idx);
text_value = double_to_string(val);
break;
}
@ -174,10 +190,8 @@ namespace Slic3r { namespace GUI {
case coStrings:
{
const ConfigOptionStrings *vec = static_cast<const ConfigOptionStrings*>(m_opt.default_value);
if (vec == nullptr || vec->empty()) break;
if (vec->size() > 1)
break;
text_value = vec->values.at(0);
if (vec == nullptr || vec->empty()) break; //for the case of empty default value
text_value = vec->get_at(m_opt_idx);
break;
}
default:
@ -259,7 +273,7 @@ void CheckBox::BUILD() {
bool check_value = m_opt.type == coBool ?
m_opt.default_value->getBool() : m_opt.type == coBools ?
static_cast<ConfigOptionBools*>(m_opt.default_value)->values.at(0) :
static_cast<ConfigOptionBools*>(m_opt.default_value)->get_at(m_opt_idx) :
false;
auto temp = new wxCheckBox(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
@ -365,7 +379,7 @@ void Choice::BUILD() {
}
else{
for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels){
const wxString& str = m_opt_id == "support" ? L_str(el) : el;
const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el;
temp->Append(str);
}
set_selection();
@ -418,7 +432,7 @@ void Choice::set_selection()
break;
}
case coStrings:{
text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->values.at(0);
text_value = static_cast<const ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx);
size_t idx = 0;
for (auto el : m_opt.enum_values)
@ -582,7 +596,7 @@ void ColourPicker::BUILD()
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->values.at(0));
wxString clr(static_cast<ConfigOptionStrings*>(m_opt.default_value)->get_at(m_opt_idx));
auto temp = new wxColourPickerCtrl(m_parent, wxID_ANY, clr, wxDefaultPosition, size);
// // recast as a wxWindow to fit the calling convention
@ -675,6 +689,22 @@ boost::any& PointCtrl::get_value()
return m_value = ret_point;
}
void StaticText::BUILD()
{
auto size = wxSize(wxDefaultSize);
if (m_opt.height >= 0) size.SetHeight(m_opt.height);
if (m_opt.width >= 0) size.SetWidth(m_opt.width);
wxString legend(static_cast<ConfigOptionString*>(m_opt.default_value)->value);
auto temp = new wxStaticText(m_parent, wxID_ANY, legend, wxDefaultPosition, size);
temp->SetFont(bold_font());
// // recast as a wxWindow to fit the calling convention
window = dynamic_cast<wxWindow*>(temp);
temp->SetToolTip(get_tooltip_text(legend));
}
} // GUI
} // Slic3r

View file

@ -95,6 +95,7 @@ public:
/// Copy of ConfigOption for deduction purposes
const ConfigOptionDef m_opt {ConfigOptionDef()};
const t_config_option_key m_opt_id;//! {""};
int m_opt_idx = 0;
/// Sets a value for this control.
/// subclasses should overload with a specific version
@ -384,6 +385,34 @@ public:
wxSizer* getSizer() override { return sizer; }
};
class StaticText : public Field {
using Field::Field;
public:
StaticText(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {}
StaticText(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {}
~StaticText() {}
wxWindow* window{ nullptr };
void BUILD() override;
void set_value(const std::string& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxStaticText*>(window)->SetLabel(value);
m_disable_change_event = false;
}
void set_value(const boost::any& value, bool change_event = false) {
m_disable_change_event = !change_event;
dynamic_cast<wxStaticText*>(window)->SetLabel(boost::any_cast<wxString>(value));
m_disable_change_event = false;
}
boost::any& get_value()override { return m_value; }
void enable() override { dynamic_cast<wxColourPickerCtrl*>(window)->Enable(); };
void disable() override{ dynamic_cast<wxColourPickerCtrl*>(window)->Disable(); };
wxWindow* getWindow() override { return window; }
};
} // GUI
} // Slic3r

View file

@ -1,5 +1,6 @@
#include "GLCanvas3D.hpp"
#include "../../libslic3r/libslic3r.h"
#include "../../slic3r/GUI/3DScene.hpp"
#include "../../slic3r/GUI/GLShader.hpp"
#include "../../slic3r/GUI/GUI.hpp"
@ -41,6 +42,11 @@ static const float VIEW_REAR[2] = { 180.0f, 90.0f };
static const float VARIABLE_LAYER_THICKNESS_BAR_WIDTH = 70.0f;
static const float VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
static const float UNIT_MATRIX[] = { 1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f };
namespace Slic3r {
namespace GUI {
@ -493,6 +499,7 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
::glEnable(GL_TEXTURE_2D);
::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
::glEnableClientState(GL_VERTEX_ARRAY);
::glEnableClientState(GL_TEXTURE_COORD_ARRAY);
@ -500,7 +507,6 @@ void GLCanvas3D::Bed::_render_prusa(float theta) const
if (theta > 90.0f)
::glFrontFace(GL_CW);
::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
::glBindTexture(GL_TEXTURE_2D, (theta <= 90.0f) ? (GLuint)m_top_texture.get_id() : (GLuint)m_bottom_texture.get_id());
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_triangles.get_vertices());
::glTexCoordPointer(2, GL_FLOAT, 0, (GLvoid*)m_triangles.get_tex_coords());
@ -553,6 +559,7 @@ void GLCanvas3D::Bed::_render_custom() const
::glDisableClientState(GL_VERTEX_ARRAY);
::glDisable(GL_BLEND);
::glDisable(GL_LIGHTING);
}
}
@ -577,7 +584,6 @@ GLCanvas3D::Axes::Axes()
void GLCanvas3D::Axes::render(bool depth_test) const
{
::glDisable(GL_LIGHTING);
if (depth_test)
::glEnable(GL_DEPTH_TEST);
else
@ -623,7 +629,6 @@ bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons)
void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const
{
::glDisable(GL_LIGHTING);
_render_plane(bb);
_render_contour();
}
@ -730,6 +735,12 @@ void GLCanvas3D::Shader::set_uniform(const std::string& name, float value) const
m_shader->set_uniform(name.c_str(), value);
}
void GLCanvas3D::Shader::set_uniform(const std::string& name, const float* matrix) const
{
if (m_shader != nullptr)
m_shader->set_uniform(name.c_str(), matrix);
}
const GLShader* GLCanvas3D::Shader::get_shader() const
{
return m_shader;
@ -963,15 +974,18 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
m_shader.set_uniform("z_texture_row_to_normalized", 1.0f / (float)volume.layer_height_texture_height());
m_shader.set_uniform("z_cursor", max_z * get_cursor_z_relative(canvas));
m_shader.set_uniform("z_cursor_band_width", band_width);
// The shader requires the original model coordinates when rendering to the texture, so we pass it the unit matrix
m_shader.set_uniform("volume_world_matrix", UNIT_MATRIX);
GLsizei w = (GLsizei)volume.layer_height_texture_width();
GLsizei h = (GLsizei)volume.layer_height_texture_height();
GLsizei half_w = w / 2;
GLsizei half_h = h / 2;
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glBindTexture(GL_TEXTURE_2D, m_z_texture_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level0());
::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, volume.layer_height_texture_data_ptr_level1());
@ -1053,7 +1067,9 @@ const Pointf3 GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MA
GLCanvas3D::Mouse::Drag::Drag()
: start_position_2D(Invalid_2D_Point)
, start_position_3D(Invalid_3D_Point)
, volume_idx(-1)
, move_with_shift(false)
, move_volume_idx(-1)
, gizmo_volume_idx(-1)
{
}
@ -1083,8 +1099,9 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const
return (drag.start_position_3D != Drag::Invalid_3D_Point);
}
const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f;
const float GLCanvas3D::Gizmos::OverlayGapY = 10.0f;
const float GLCanvas3D::Gizmos::OverlayTexturesScale = 0.75f;
const float GLCanvas3D::Gizmos::OverlayOffsetX = 10.0f * OverlayTexturesScale;
const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayTexturesScale;
GLCanvas3D::Gizmos::Gizmos()
: m_enabled(false)
@ -1150,7 +1167,7 @@ void GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Poin
if (it->second == nullptr)
continue;
float tex_size = (float)it->second->get_textures_size();
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@ -1176,7 +1193,7 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Poi
if (it->second == nullptr)
continue;
float tex_size = (float)it->second->get_textures_size();
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@ -1242,7 +1259,7 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const
if (it->second == nullptr)
continue;
float tex_size = (float)it->second->get_textures_size();
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale;
float half_tex_size = 0.5f * tex_size;
// we currently use circular icons for gizmo, so we check the radius
@ -1274,14 +1291,19 @@ void GLCanvas3D::Gizmos::update(const Pointf& mouse_pos)
curr->update(mouse_pos);
}
void GLCanvas3D::Gizmos::update_data(float scale)
void GLCanvas3D::Gizmos::refresh()
{
if (!m_enabled)
return;
GizmosMap::const_iterator it = m_gizmos.find(Scale);
if (it != m_gizmos.end())
reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
GLGizmoBase* curr = _get_current();
if (curr != nullptr)
curr->refresh();
}
GLCanvas3D::Gizmos::EType GLCanvas3D::Gizmos::get_current_type() const
{
return m_current;
}
bool GLCanvas3D::Gizmos::is_running() const
@ -1309,6 +1331,9 @@ void GLCanvas3D::Gizmos::start_dragging()
void GLCanvas3D::Gizmos::stop_dragging()
{
m_dragging = false;
GLGizmoBase* curr = _get_current();
if (curr != nullptr)
curr->stop_dragging();
}
float GLCanvas3D::Gizmos::get_scale() const
@ -1320,6 +1345,35 @@ float GLCanvas3D::Gizmos::get_scale() const
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale*>(it->second)->get_scale() : 1.0f;
}
void GLCanvas3D::Gizmos::set_scale(float scale)
{
if (!m_enabled)
return;
GizmosMap::const_iterator it = m_gizmos.find(Scale);
if (it != m_gizmos.end())
reinterpret_cast<GLGizmoScale*>(it->second)->set_scale(scale);
}
float GLCanvas3D::Gizmos::get_angle_z() const
{
if (!m_enabled)
return 0.0f;
GizmosMap::const_iterator it = m_gizmos.find(Rotate);
return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate*>(it->second)->get_angle_z() : 0.0f;
}
void GLCanvas3D::Gizmos::set_angle_z(float angle_z)
{
if (!m_enabled)
return;
GizmosMap::const_iterator it = m_gizmos.find(Rotate);
if (it != m_gizmos.end())
reinterpret_cast<GLGizmoRotate*>(it->second)->set_angle_z(angle_z);
}
void GLCanvas3D::Gizmos::render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const
{
if (!m_enabled)
@ -1375,8 +1429,8 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas) const
float scaled_gap_y = OverlayGapY * inv_zoom;
for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it)
{
float tex_size = (float)it->second->get_textures_size() * inv_zoom;
GLTexture::render_texture(it->second->get_textures_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
float tex_size = (float)it->second->get_textures_size() * OverlayTexturesScale * inv_zoom;
GLTexture::render_texture(it->second->get_texture_id(), top_x, top_x + tex_size, top_y - tex_size, top_y);
top_y -= (tex_size + scaled_gap_y);
}
}
@ -1849,6 +1903,42 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
m_volumes.update_colors_by_extruder(m_config);
}
void GLCanvas3D::update_gizmos_data()
{
if (!m_gizmos.is_running())
return;
int id = _get_first_selected_object_id();
if ((id != -1) && (m_model != nullptr))
{
ModelObject* model_object = m_model->objects[id];
if (model_object != nullptr)
{
ModelInstance* model_instance = model_object->instances[0];
if (model_instance != nullptr)
{
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
m_gizmos.set_scale(model_instance->scaling_factor);
break;
}
case Gizmos::Rotate:
{
m_gizmos.set_angle_z(model_instance->rotation);
break;
}
default:
{
break;
}
}
}
}
}
}
void GLCanvas3D::render()
{
if (m_canvas == nullptr)
@ -1961,6 +2051,7 @@ void GLCanvas3D::reload_scene(bool force)
m_objects_volumes_idxs.push_back(load_object(*m_model, obj_idx));
}
update_gizmos_data();
update_volumes_selection(m_objects_selections);
if (m_config->has("nozzle_diameter"))
@ -2512,6 +2603,18 @@ void GLCanvas3D::register_on_gizmo_scale_uniformly_callback(void* callback)
m_on_gizmo_scale_uniformly_callback.register_callback(callback);
}
void GLCanvas3D::register_on_gizmo_rotate_callback(void* callback)
{
if (callback != nullptr)
m_on_gizmo_rotate_callback.register_callback(callback);
}
void GLCanvas3D::register_on_update_geometry_info_callback(void* callback)
{
if (callback != nullptr)
m_on_update_geometry_info_callback.register_callback(callback);
}
void GLCanvas3D::bind_event_handlers()
{
if (m_canvas != nullptr)
@ -2729,14 +2832,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if ((selected_object_idx != -1) && gizmos_overlay_contains_mouse)
{
update_gizmos_data();
m_gizmos.update_on_off_state(*this, m_mouse.position);
_update_gizmos_data();
m_dirty = true;
}
else if ((selected_object_idx != -1) && m_gizmos.grabber_contains_mouse())
{
_update_gizmos_data();
{
update_gizmos_data();
m_gizmos.start_dragging();
m_mouse.drag.gizmo_volume_idx = _get_first_selected_volume_id();
m_dirty = true;
}
else
@ -2761,9 +2865,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
if (m_gizmos.is_running())
_update_gizmos_data();
update_gizmos_data();
m_gizmos.refresh();
m_dirty = true;
}
}
@ -2786,7 +2889,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (volume_bbox.contains(pos3d))
{
// The dragging operation is initiated.
m_mouse.drag.volume_idx = volume_idx;
m_mouse.drag.move_with_shift = evt.ShiftDown();
m_mouse.drag.move_volume_idx = volume_idx;
m_mouse.drag.start_position_3D = pos3d;
// Remember the shift to to the object center.The object center will later be used
// to limit the object placement close to the bed.
@ -2802,7 +2906,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
}
else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.volume_idx != -1))
else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) && (m_mouse.drag.move_volume_idx != -1))
{
m_mouse.dragging = true;
@ -2825,27 +2929,34 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Calculate the translation vector.
Vectorf3 vector = m_mouse.drag.start_position_3D.vector_to(cur_pos);
// Get the volume being dragged.
GLVolume* volume = m_volumes.volumes[m_mouse.drag.volume_idx];
GLVolume* volume = m_volumes.volumes[m_mouse.drag.move_volume_idx];
// Get all volumes belonging to the same group, if any.
std::vector<GLVolume*> volumes;
if (volume->drag_group_id == -1)
int group_id = m_mouse.drag.move_with_shift ? volume->select_group_id : volume->drag_group_id;
if (group_id == -1)
volumes.push_back(volume);
else
{
for (GLVolume* v : m_volumes.volumes)
{
if ((v != nullptr) && (v->drag_group_id == volume->drag_group_id))
volumes.push_back(v);
if (v != nullptr)
{
if ((m_mouse.drag.move_with_shift && (v->select_group_id == group_id)) || (v->drag_group_id == group_id))
volumes.push_back(v);
}
}
}
// Apply new temporary volume origin and ignore Z.
for (GLVolume* v : volumes)
{
v->origin.translate(vector.x, vector.y, 0.0);
Pointf3 origin = v->get_origin();
origin.translate(vector.x, vector.y, 0.0);
v->set_origin(origin);
}
m_mouse.drag.start_position_3D = cur_pos;
m_gizmos.refresh();
m_dirty = true;
}
@ -2856,7 +2967,59 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
const Pointf3& cur_pos = _mouse_to_bed_3d(pos);
m_gizmos.update(Pointf(cur_pos.x, cur_pos.y));
m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
std::vector<GLVolume*> volumes;
if (m_mouse.drag.gizmo_volume_idx != -1)
{
GLVolume* volume = m_volumes.volumes[m_mouse.drag.gizmo_volume_idx];
// Get all volumes belonging to the same group, if any.
if (volume->select_group_id == -1)
volumes.push_back(volume);
else
{
for (GLVolume* v : m_volumes.volumes)
{
if ((v != nullptr) && (v->select_group_id == volume->select_group_id))
volumes.push_back(v);
}
}
}
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
// Apply new temporary scale factor
float scale_factor = m_gizmos.get_scale();
for (GLVolume* v : volumes)
{
v->set_scale_factor(scale_factor);
}
break;
}
case Gizmos::Rotate:
{
// Apply new temporary angle_z
float angle_z = m_gizmos.get_angle_z();
for (GLVolume* v : volumes)
{
v->set_angle_z(angle_z);
}
break;
}
default:
break;
}
if (!volumes.empty())
{
const BoundingBoxf3& bb = volumes[0]->transformed_bounding_box();
const Pointf3& size = bb.size();
m_on_update_geometry_info_callback.call(size.x, size.y, size.z, m_gizmos.get_scale());
}
if ((m_gizmos.get_current_type() != Gizmos::Rotate) && (volumes.size() > 1))
m_gizmos.refresh();
m_dirty = true;
}
else if (evt.Dragging() && !gizmos_overlay_contains_mouse)
@ -2914,19 +3077,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (layer_editing_object_idx != -1)
m_on_model_update_callback.call();
}
else if ((m_mouse.drag.volume_idx != -1) && m_mouse.dragging)
else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging)
{
// get all volumes belonging to the same group, if any
std::vector<int> volume_idxs;
int vol_id = m_mouse.drag.volume_idx;
int group_id = m_volumes.volumes[vol_id]->drag_group_id;
int vol_id = m_mouse.drag.move_volume_idx;
int group_id = m_mouse.drag.move_with_shift ? m_volumes.volumes[vol_id]->select_group_id : m_volumes.volumes[vol_id]->drag_group_id;
if (group_id == -1)
volume_idxs.push_back(vol_id);
else
{
for (int i = 0; i < (int)m_volumes.volumes.size(); ++i)
{
if (m_volumes.volumes[i]->drag_group_id == group_id)
if ((m_mouse.drag.move_with_shift && (m_volumes.volumes[i]->select_group_id == group_id)) || (m_volumes.volumes[i]->drag_group_id == group_id))
volume_idxs.push_back(i);
}
}
@ -2944,10 +3107,26 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
else if (evt.LeftUp() && m_gizmos.is_dragging())
{
switch (m_gizmos.get_current_type())
{
case Gizmos::Scale:
{
m_on_gizmo_scale_uniformly_callback.call((double)m_gizmos.get_scale());
break;
}
case Gizmos::Rotate:
{
m_on_gizmo_rotate_callback.call((double)m_gizmos.get_angle_z());
break;
}
default:
break;
}
m_gizmos.stop_dragging();
}
m_mouse.drag.volume_idx = -1;
m_mouse.drag.move_volume_idx = -1;
m_mouse.drag.gizmo_volume_idx = -1;
m_mouse.set_start_position_3D_as_invalid();
m_mouse.set_start_position_2D_as_invalid();
m_mouse.dragging = false;
@ -3200,6 +3379,8 @@ void GLCanvas3D::_deregister_callbacks()
m_on_wipe_tower_moved_callback.deregister_callback();
m_on_enable_action_buttons_callback.deregister_callback();
m_on_gizmo_scale_uniformly_callback.deregister_callback();
m_on_gizmo_rotate_callback.deregister_callback();
m_on_update_geometry_info_callback.deregister_callback();
}
void GLCanvas3D::_mark_volumes_for_layer_height() const
@ -3259,7 +3440,6 @@ void GLCanvas3D::_picking_pass() const
if (m_multisample_allowed)
::glDisable(GL_MULTISAMPLE);
::glDisable(GL_LIGHTING);
::glDisable(GL_BLEND);
::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -3315,8 +3495,6 @@ void GLCanvas3D::_render_background() const
static const float COLOR[3] = { 10.0f / 255.0f, 98.0f / 255.0f, 144.0f / 255.0f };
::glDisable(GL_LIGHTING);
::glPushMatrix();
::glLoadIdentity();
::glMatrixMode(GL_PROJECTION);
@ -3395,6 +3573,8 @@ void GLCanvas3D::_render_objects() const
if (m_picking_enabled)
::glEnable(GL_CULL_FACE);
}
::glDisable(GL_LIGHTING);
}
void GLCanvas3D::_render_cutting_plane() const
@ -3459,6 +3639,7 @@ void GLCanvas3D::_render_legend_texture() const
float t = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
float r = l + (float)w * inv_zoom;
float b = t - (float)h * inv_zoom;
GLTexture::render_texture(tex_id, l, r, b, t);
::glPopMatrix();
@ -3503,9 +3684,7 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
if (fake_colors)
::glDisable(GL_LIGHTING);
else
if (!fake_colors)
::glEnable(GL_LIGHTING);
// do not cull backfaces to show broken geometry, if any
@ -3543,6 +3722,9 @@ void GLCanvas3D::_render_volumes(bool fake_colors) const
::glDisable(GL_BLEND);
::glEnable(GL_CULL_FACE);
if (!fake_colors)
::glDisable(GL_LIGHTING);
}
void GLCanvas3D::_render_gizmo() const
@ -3668,6 +3850,35 @@ int GLCanvas3D::_get_first_selected_object_id() const
return -1;
}
int GLCanvas3D::_get_first_selected_volume_id() const
{
if (m_print != nullptr)
{
int objects_count = (int)m_print->objects.size();
for (const GLVolume* vol : m_volumes.volumes)
{
if ((vol != nullptr) && vol->selected)
{
int object_id = vol->select_group_id / 1000000;
// Objects with object_id >= 1000 have a specific meaning, for example the wipe tower proxy.
if ((object_id < 10000) && (object_id < objects_count))
{
int volume_id = 0;
for (int i = 0; i < object_id; ++i)
{
const PrintObject* obj = m_print->objects[i];
const ModelObject* model = obj->model_object();
volume_id += model->instances.size();
}
return volume_id;
}
}
}
}
return -1;
}
static inline int hex_digit_to_int(const char c)
{
return
@ -4273,13 +4484,14 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
// Move a regular object.
ModelObject* model_object = m_model->objects[obj_idx];
model_object->instances[instance_idx]->offset.translate(volume->origin.x, volume->origin.y);
const Pointf3& origin = volume->get_origin();
model_object->instances[instance_idx]->offset = Pointf(origin.x, origin.y);
model_object->invalidate_bounding_box();
object_moved = true;
}
else if (obj_idx == 1000)
// Move a wipe tower proxy.
wipe_tower_origin = volume->origin;
wipe_tower_origin = volume->get_origin();
}
if (object_moved)
@ -4302,21 +4514,6 @@ void GLCanvas3D::_on_select(int volume_idx)
m_on_select_object_callback.call(id);
}
void GLCanvas3D::_update_gizmos_data()
{
int id = _get_first_selected_object_id();
if ((id != -1) && (m_model != nullptr))
{
ModelObject* model_object = m_model->objects[id];
if (model_object != nullptr)
{
ModelInstance* model_instance = model_object->instances[0];
if (model_instance != nullptr)
m_gizmos.update_data(model_instance->scaling_factor);
}
}
}
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
{
static const float INV_255 = 1.0f / 255.0f;

View file

@ -225,6 +225,7 @@ public:
void stop_using() const;
void set_uniform(const std::string& name, float value) const;
void set_uniform(const std::string& name, const float* matrix) const;
const GLShader* get_shader() const;
@ -302,7 +303,10 @@ public:
Point start_position_2D;
Pointf3 start_position_3D;
Vectorf3 volume_center_offset;
int volume_idx;
bool move_with_shift;
int move_volume_idx;
int gizmo_volume_idx;
public:
Drag();
@ -323,6 +327,7 @@ public:
class Gizmos
{
static const float OverlayTexturesScale;
static const float OverlayOffsetX;
static const float OverlayGapY;
@ -360,14 +365,21 @@ public:
bool overlay_contains_mouse(const GLCanvas3D& canvas, const Pointf& mouse_pos) const;
bool grabber_contains_mouse() const;
void update(const Pointf& mouse_pos);
void update_data(float scale);
void refresh();
EType get_current_type() const;
bool is_running() const;
bool is_dragging() const;
void start_dragging();
void stop_dragging();
float get_scale() const;
void set_scale(float scale);
float get_angle_z() const;
void set_angle_z(float angle_z);
void render(const GLCanvas3D& canvas, const BoundingBoxf3& box) const;
void render_current_gizmo_for_picking_pass(const BoundingBoxf3& box) const;
@ -439,6 +451,8 @@ private:
PerlCallback m_on_wipe_tower_moved_callback;
PerlCallback m_on_enable_action_buttons_callback;
PerlCallback m_on_gizmo_scale_uniformly_callback;
PerlCallback m_on_gizmo_rotate_callback;
PerlCallback m_on_update_geometry_info_callback;
public:
GLCanvas3D(wxGLCanvas* canvas);
@ -507,6 +521,7 @@ public:
void set_viewport_from_scene(const GLCanvas3D& other);
void update_volumes_colors_by_extruder();
void update_gizmos_data();
void render();
@ -545,6 +560,8 @@ public:
void register_on_wipe_tower_moved_callback(void* callback);
void register_on_enable_action_buttons_callback(void* callback);
void register_on_gizmo_scale_uniformly_callback(void* callback);
void register_on_gizmo_rotate_callback(void* callback);
void register_on_update_geometry_info_callback(void* callback);
void bind_event_handlers();
void unbind_event_handlers();
@ -605,6 +622,7 @@ private:
void _stop_timer();
int _get_first_selected_object_id() const;
int _get_first_selected_volume_id() const;
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
@ -625,8 +643,6 @@ private:
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);
void _update_gizmos_data();
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
};

View file

@ -78,35 +78,32 @@ std::string GLCanvas3DManager::GLInfo::to_string(bool format_as_html, bool exten
std::stringstream out;
std::string h2_start = format_as_html ? "<b>" : "";
std::string h2_end = format_as_html ? "</b>" : "";
std::string b_start = format_as_html ? "<b>" : "";
std::string b_end = format_as_html ? "</b>" : "";
std::string h2_end = format_as_html ? "</b>" : "";
std::string b_start = format_as_html ? "<b>" : "";
std::string b_end = format_as_html ? "</b>" : "";
std::string line_end = format_as_html ? "<br>" : "\n";
out << h2_start << "OpenGL installation" << h2_end << line_end;
out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end;
out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end;
out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end;
out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end;
out << b_start << "GL version: " << b_end << (version.empty() ? "N/A" : version) << line_end;
out << b_start << "Vendor: " << b_end << (vendor.empty() ? "N/A" : vendor) << line_end;
out << b_start << "Renderer: " << b_end << (renderer.empty() ? "N/A" : renderer) << line_end;
out << b_start << "GLSL version: " << b_end << (glsl_version.empty() ? "N/A" : glsl_version) << line_end;
if (extensions)
{
out << h2_start << "Installed extensions:" << h2_end << line_end;
std::vector<std::string> extensions_list;
GLint num_extensions;
::glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLint i = 0; i < num_extensions; ++i)
{
const char* e = (const char*)::glGetStringi(GL_EXTENSIONS, i);
extensions_list.push_back(e);
}
std::string extensions_str = (const char*)::glGetString(GL_EXTENSIONS);
boost::split(extensions_list, extensions_str, boost::is_any_of(" "), boost::token_compress_off);
std::sort(extensions_list.begin(), extensions_list.end());
for (const std::string& ext : extensions_list)
if (!extensions_list.empty())
{
out << ext << line_end;
out << h2_start << "Installed extensions:" << h2_end << line_end;
std::sort(extensions_list.begin(), extensions_list.end());
for (const std::string& ext : extensions_list)
{
out << ext << line_end;
}
}
}
@ -467,6 +464,13 @@ void GLCanvas3DManager::update_volumes_colors_by_extruder(wxGLCanvas* canvas)
it->second->update_volumes_colors_by_extruder();
}
void GLCanvas3DManager::update_gizmos_data(wxGLCanvas* canvas)
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->update_gizmos_data();
}
void GLCanvas3DManager::render(wxGLCanvas* canvas) const
{
CanvasesMap::const_iterator it = _get_canvas(canvas);
@ -658,6 +662,20 @@ void GLCanvas3DManager::register_on_gizmo_scale_uniformly_callback(wxGLCanvas* c
it->second->register_on_gizmo_scale_uniformly_callback(callback);
}
void GLCanvas3DManager::register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_on_gizmo_rotate_callback(callback);
}
void GLCanvas3DManager::register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->register_on_update_geometry_info_callback(callback);
}
GLCanvas3DManager::CanvasesMap::iterator GLCanvas3DManager::_get_canvas(wxGLCanvas* canvas)
{
return (canvas == nullptr) ? m_canvases.end() : m_canvases.find(canvas);

View file

@ -120,6 +120,7 @@ public:
void set_viewport_from_scene(wxGLCanvas* canvas, wxGLCanvas* other);
void update_volumes_colors_by_extruder(wxGLCanvas* canvas);
void update_gizmos_data(wxGLCanvas* canvas);
void render(wxGLCanvas* canvas) const;
@ -152,6 +153,8 @@ public:
void register_on_wipe_tower_moved_callback(wxGLCanvas* canvas, void* callback);
void register_on_enable_action_buttons_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_scale_uniformly_callback(wxGLCanvas* canvas, void* callback);
void register_on_gizmo_rotate_callback(wxGLCanvas* canvas, void* callback);
void register_on_update_geometry_info_callback(wxGLCanvas* canvas, void* callback);
private:
CanvasesMap::iterator _get_canvas(wxGLCanvas* canvas);

View file

@ -90,9 +90,10 @@ GLGizmoBase::EState GLGizmoBase::get_state() const
void GLGizmoBase::set_state(GLGizmoBase::EState state)
{
m_state = state;
on_set_state();
}
unsigned int GLGizmoBase::get_textures_id() const
unsigned int GLGizmoBase::get_texture_id() const
{
return m_textures[m_state].get_id();
}
@ -118,12 +119,22 @@ void GLGizmoBase::start_dragging()
on_start_dragging();
}
void GLGizmoBase::stop_dragging()
{
on_stop_dragging();
}
void GLGizmoBase::update(const Pointf& mouse_pos)
{
if (m_hover_id != -1)
on_update(mouse_pos);
}
void GLGizmoBase::refresh()
{
on_refresh();
}
void GLGizmoBase::render(const BoundingBoxf3& box) const
{
on_render(box);
@ -134,13 +145,29 @@ void GLGizmoBase::render_for_picking(const BoundingBoxf3& box) const
on_render_for_picking(box);
}
void GLGizmoBase::on_set_state()
{
// do nothing
}
void GLGizmoBase::on_start_dragging()
{
// do nothing
}
void GLGizmoBase::on_stop_dragging()
{
// do nothing
}
void GLGizmoBase::on_refresh()
{
// do nothing
}
void GLGizmoBase::render_grabbers() const
{
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
m_grabbers[i].render(m_hover_id == i);
}
@ -162,9 +189,23 @@ GLGizmoRotate::GLGizmoRotate()
, m_angle_z(0.0f)
, m_center(Pointf(0.0, 0.0))
, m_radius(0.0f)
, m_keep_radius(false)
{
}
float GLGizmoRotate::get_angle_z() const
{
return m_angle_z;
}
void GLGizmoRotate::set_angle_z(float angle_z)
{
if (std::abs(angle_z - 2.0f * PI) < EPSILON)
angle_z = 0.0f;
m_angle_z = angle_z;
}
bool GLGizmoRotate::on_init()
{
std::string path = resources_dir() + "/icons/overlay/";
@ -186,6 +227,11 @@ bool GLGizmoRotate::on_init()
return true;
}
void GLGizmoRotate::on_set_state()
{
m_keep_radius = (m_state == On) ? false : true;
}
void GLGizmoRotate::on_update(const Pointf& mouse_pos)
{
Vectorf orig_dir(1.0, 0.0);
@ -194,6 +240,7 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
if (cross(orig_dir, new_dir) < 0.0)
theta = 2.0 * (coordf_t)PI - theta;
// snap
if (length(m_center.vector_to(mouse_pos)) < 2.0 * (double)m_radius / 3.0)
{
coordf_t step = 2.0 * (coordf_t)PI / (coordf_t)SnapRegionsCount;
@ -202,18 +249,26 @@ void GLGizmoRotate::on_update(const Pointf& mouse_pos)
if (theta == 2.0 * (coordf_t)PI)
theta = 0.0;
m_angle_z = (float)theta;
}
void GLGizmoRotate::on_refresh()
{
m_keep_radius = false;
}
void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
{
::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
const Pointf3& size = box.size();
m_center = box.center();
m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
if (!m_keep_radius)
{
m_radius = Offset + ::sqrt(sqr(0.5f * size.x) + sqr(0.5f * size.y));
m_keep_radius = true;
}
::glLineWidth(2.0f);
::glColor3fv(BaseColor);
@ -230,7 +285,6 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
{
::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
m_grabbers[0].color[0] = 1.0f;
@ -399,7 +453,6 @@ void GLGizmoScale::on_update(const Pointf& mouse_pos)
void GLGizmoScale::on_render(const BoundingBoxf3& box) const
{
::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
coordf_t min_x = box.min.x - (coordf_t)Offset;
@ -438,7 +491,6 @@ void GLGizmoScale::on_render_for_picking(const BoundingBoxf3& box) const
{
static const GLfloat INV_255 = 1.0f / 255.0f;
::glDisable(GL_LIGHTING);
::glDisable(GL_DEPTH_TEST);
for (unsigned int i = 0; i < 4; ++i)

View file

@ -57,22 +57,27 @@ public:
EState get_state() const;
void set_state(EState state);
unsigned int get_textures_id() const;
unsigned int get_texture_id() const;
int get_textures_size() const;
int get_hover_id() const;
void set_hover_id(int id);
void start_dragging();
void stop_dragging();
void update(const Pointf& mouse_pos);
void refresh();
void render(const BoundingBoxf3& box) const;
void render_for_picking(const BoundingBoxf3& box) const;
protected:
virtual bool on_init() = 0;
virtual void on_set_state();
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const Pointf& mouse_pos) = 0;
virtual void on_refresh();
virtual void on_render(const BoundingBoxf3& box) const = 0;
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
@ -96,13 +101,19 @@ class GLGizmoRotate : public GLGizmoBase
mutable Pointf m_center;
mutable float m_radius;
mutable bool m_keep_radius;
public:
GLGizmoRotate();
float get_angle_z() const;
void set_angle_z(float angle_z);
protected:
virtual bool on_init();
virtual void on_set_state();
virtual void on_update(const Pointf& mouse_pos);
virtual void on_refresh();
virtual void on_render(const BoundingBoxf3& box) const;
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
@ -120,9 +131,9 @@ class GLGizmoScale : public GLGizmoBase
static const float Offset;
float m_scale;
float m_starting_scale;
Pointf m_starting_drag_position;
float m_starting_scale;
public:
GLGizmoScale();

View file

@ -214,6 +214,17 @@ bool GLShader::set_uniform(const char *name, float value) const
return false;
}
bool GLShader::set_uniform(const char* name, const float* matrix) const
{
int id = get_uniform_location(name);
if (id >= 0)
{
::glUniformMatrix4fv(id, 1, GL_FALSE, (const GLfloat*)matrix);
return true;
}
return false;
}
/*
# Set shader vector
sub SetVector

View file

@ -25,6 +25,7 @@ public:
int get_uniform_location(const char *name) const;
bool set_uniform(const char *name, float value) const;
bool set_uniform(const char* name, const float* matrix) const;
void enable() const;
void disable() const;

View file

@ -72,9 +72,10 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap
}
// sends data to gpu
::glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
::glGenTextures(1, &m_id);
::glBindTexture(GL_TEXTURE_2D, m_id);
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
if (generate_mipmaps)
{
// we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards
@ -127,27 +128,25 @@ const std::string& GLTexture::get_source() const
void GLTexture::render_texture(unsigned int tex_id, float left, float right, float bottom, float top)
{
::glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
::glDisable(GL_LIGHTING);
::glEnable(GL_BLEND);
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
::glEnable(GL_TEXTURE_2D);
::glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
::glBindTexture(GL_TEXTURE_2D, (GLuint)tex_id);
::glBegin(GL_QUADS);
::glTexCoord2d(0.0f, 1.0f); ::glVertex3f(left, bottom, 0.0f);
::glTexCoord2d(1.0f, 1.0f); ::glVertex3f(right, bottom, 0.0f);
::glTexCoord2d(1.0f, 0.0f); ::glVertex3f(right, top, 0.0f);
::glTexCoord2d(0.0f, 0.0f); ::glVertex3f(left, top, 0.0f);
::glTexCoord2f(0.0f, 1.0f); ::glVertex2f(left, bottom);
::glTexCoord2f(1.0f, 1.0f); ::glVertex2f(right, bottom);
::glTexCoord2f(1.0f, 0.0f); ::glVertex2f(right, top);
::glTexCoord2f(0.0f, 0.0f); ::glVertex2f(left, top);
::glEnd();
::glBindTexture(GL_TEXTURE_2D, 0);
::glDisable(GL_TEXTURE_2D);
::glDisable(GL_BLEND);
::glEnable(GL_LIGHTING);
}
void GLTexture::_generate_mipmaps(wxImage& image)
@ -182,7 +181,7 @@ void GLTexture::_generate_mipmaps(wxImage& image)
data[data_id + 3] = (img_alpha != nullptr) ? img_alpha[i] : 255;
}
::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)w, (GLsizei)h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data());
}
}

View file

@ -56,8 +56,9 @@
#include "../Utils/PresetUpdater.hpp"
#include "../Config/Snapshot.hpp"
#include "3DScene.hpp"
#include "3DScene.hpp"
#include "libslic3r/I18N.hpp"
namespace Slic3r { namespace GUI {
@ -110,6 +111,7 @@ wxNotebook *g_wxTabPanel = nullptr;
AppConfig *g_AppConfig = nullptr;
PresetBundle *g_PresetBundle= nullptr;
PresetUpdater *g_PresetUpdater = nullptr;
_3DScene *g_3DScene = nullptr;
wxColour g_color_label_modified;
wxColour g_color_label_sys;
wxColour g_color_label_default;
@ -118,6 +120,9 @@ std::vector<Tab *> g_tabs_list;
wxLocale* g_wxLocale;
wxFont g_small_font;
wxFont g_bold_font;
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
double m_brim_width = 0.0;
wxButton* g_wiping_dialog_button = nullptr;
@ -150,10 +155,25 @@ void update_label_colours_from_appconfig()
}
}
static void init_fonts()
{
g_small_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
g_bold_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold();
#ifdef __WXMAC__
g_small_font.SetPointSize(11);
g_bold_font.SetPointSize(13);
#endif /*__WXMAC__*/
}
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
void set_wxapp(wxApp *app)
{
g_wxApp = app;
// Let the libslic3r know the callback, which will translate messages on demand.
Slic3r::I18N::set_translate_callback(libslic3r_translate_callback);
init_label_colours();
init_fonts();
}
void set_main_frame(wxFrame *main_frame)
@ -181,6 +201,11 @@ void set_preset_updater(PresetUpdater *updater)
g_PresetUpdater = updater;
}
void set_3DScene(_3DScene *scene)
{
g_3DScene = scene;
}
std::vector<Tab *>& get_tabs_list()
{
return g_tabs_list;
@ -317,7 +342,7 @@ void add_config_menu(wxMenuBar *menu, int event_preferences_changed, int event_l
auto local_menu = new wxMenu();
wxWindowID config_id_base = wxWindow::NewControlId((int)ConfigMenuCnt);
auto config_wizard_name = _(ConfigWizard::name().wx_str());
const auto config_wizard_name = _(ConfigWizard::name().wx_str());
const auto config_wizard_tooltip = wxString::Format(_(L("Run %s")), config_wizard_name);
// Cmd+, is standard on OS X - what about other operating systems?
local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
@ -671,6 +696,14 @@ void set_label_clr_sys(const wxColour& clr) {
g_AppConfig->save();
}
const wxFont& small_font(){
return g_small_font;
}
const wxFont& bold_font(){
return g_bold_font;
}
const wxColour& get_label_clr_default() {
return g_color_label_default;
}

View file

@ -11,7 +11,7 @@
class wxApp;
class wxWindow;
class wxFrame;
class wxWindow;
class wxFont;
class wxMenuBar;
class wxNotebook;
class wxComboCtrl;
@ -32,12 +32,16 @@ class AppConfig;
class PresetUpdater;
class DynamicPrintConfig;
class TabIface;
class _3DScene;
#define _(s) Slic3r::translate((s))
inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); }
inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); }
inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); }
inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); }
#define _(s) Slic3r::GUI::I18N::translate((s))
namespace GUI { namespace I18N {
inline wxString translate(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)); }
inline wxString translate(const wchar_t *s) { return wxGetTranslation(s); }
inline wxString translate(const std::string &s) { return wxGetTranslation(wxString(s.c_str(), wxConvUTF8)); }
inline wxString translate(const std::wstring &s) { return wxGetTranslation(s.c_str()); }
} }
// !!! If you needed to translate some wxString,
// !!! please use _(L(string))
@ -87,6 +91,7 @@ void set_tab_panel(wxNotebook *tab_panel);
void set_app_config(AppConfig *app_config);
void set_preset_bundle(PresetBundle *preset_bundle);
void set_preset_updater(PresetUpdater *updater);
void set_3DScene(_3DScene *scene);
AppConfig* get_app_config();
wxApp* get_app();
@ -99,6 +104,9 @@ unsigned get_colour_approx_luma(const wxColour &colour);
void set_label_clr_modified(const wxColour& clr);
void set_label_clr_sys(const wxColour& clr);
const wxFont& small_font();
const wxFont& bold_font();
extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change);
// This is called when closing the application, when loading a config file or when starting the config wizard

View file

@ -31,6 +31,8 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("slider") == 0) {
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
} else if (opt.gui_type.compare("legend") == 0) { // StaticText
m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id)));
} else {
switch (opt.type) {
case coFloatOrPercent:
@ -86,7 +88,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
if (!this->m_disabled)
this->back_to_sys_value(opt_id);
};
if (!m_is_tab_opt) {
if (!m_show_modified_btns) {
field->m_Undo_btn->Hide();
field->m_Undo_to_sys_btn->Hide();
}
@ -199,7 +201,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
ConfigOptionDef option = opt.opt;
// add label if any
if (option.label != "") {
wxString str_label = L_str(option.label);
wxString str_label = _(option.label);
//! To correct translation by context have to use wxGETTEXT_IN_CONTEXT macro from wxWidget 3.1.1
// wxString str_label = (option.label == "Top" || option.label == "Bottom") ?
// wxGETTEXT_IN_CONTEXT("Layers", wxString(option.label.c_str()):
@ -220,7 +222,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// add sidetext if any
if (option.sidetext != "") {
auto sidetext = new wxStaticText(parent(), wxID_ANY, L_str(option.sidetext), wxDefaultPosition, wxDefaultSize);
auto sidetext = new wxStaticText(parent(), wxID_ANY, _(option.sidetext), wxDefaultPosition, wxDefaultSize);
sidetext->SetFont(sidetext_font);
sizer->Add(sidetext, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, 4);
}
@ -242,7 +244,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
}
Line OptionsGroup::create_single_option_line(const Option& option) const {
Line retval{ L_str(option.opt.label), L_str(option.opt.tooltip) };
Line retval{ _(option.opt.label), _(option.opt.tooltip) };
Option tmp(option);
tmp.opt.label = std::string("");
retval.append_option(tmp);

View file

@ -127,9 +127,15 @@ public:
inline void enable() { for (auto& field : m_fields) field.second->enable(); }
inline void disable() { for (auto& field : m_fields) field.second->disable(); }
void set_show_modified_btns_val(bool show) {
m_show_modified_btns = show;
}
OptionsGroup(wxWindow* _parent, const wxString& title, bool is_tab_opt=false) :
m_parent(_parent), title(title), m_is_tab_opt(is_tab_opt), staticbox(title!="") {
sizer = (staticbox ? new wxStaticBoxSizer(new wxStaticBox(_parent, wxID_ANY, title), wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt), staticbox(title!="") {
auto stb = new wxStaticBox(_parent, wxID_ANY, title);
stb->SetFont(bold_font());
sizer = (staticbox ? new wxStaticBoxSizer(stb/*new wxStaticBox(_parent, wxID_ANY, title)*/, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
auto num_columns = 1U;
if (label_width != 0) num_columns++;
if (extra_column != nullptr) num_columns++;
@ -156,7 +162,7 @@ protected:
bool m_disabled {false};
wxGridSizer* m_grid_sizer {nullptr};
// "true" if option is created in preset tabs
bool m_is_tab_opt{ false };
bool m_show_modified_btns{ false };
// This panel is needed for correct showing of the ToolTips for Button, StaticText and CheckBox
// Tooltips on GTK doesn't work inside wxStaticBoxSizer unless you insert a panel

View file

@ -234,12 +234,12 @@ std::string Preset::label() const
bool Preset::is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const
{
auto *condition = dynamic_cast<const ConfigOptionString*>(this->config.option("compatible_printers_condition"));
auto &condition = this->compatible_printers_condition();
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(this->config.option("compatible_printers"));
bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->values.empty();
if (! has_compatible_printers && condition != nullptr && ! condition->value.empty()) {
if (! has_compatible_printers && ! condition.empty()) {
try {
return PlaceholderParser::evaluate_boolean_expression(condition->value, active_printer.config, extra_config);
return PlaceholderParser::evaluate_boolean_expression(condition, active_printer.config, extra_config);
} catch (const std::runtime_error &err) {
//FIXME in case of an error, return "compatible with everything".
printf("Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s:\n%s\n", active_printer.name.c_str(), err.what());
@ -298,7 +298,8 @@ const std::vector<std::string>& Preset::print_options()
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "bridge_flow_ratio", "clip_multipart_objects",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers", "compatible_printers_condition","inherits"
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging", "compatible_printers",
"compatible_printers_condition","inherits"
};
return s_opts;
}
@ -308,10 +309,10 @@ const std::vector<std::string>& Preset::filament_options()
static std::vector<std::string> s_opts {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
"extrusion_multiplier", "filament_density", "filament_cost", "filament_loading_speed", "filament_unloading_speed", "filament_toolchange_delay",
"filament_ramming_parameters", "temperature", "first_layer_temperature", "bed_temperature",
"first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers",
"fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed", "start_filament_gcode", "end_filament_gcode","compatible_printers",
"compatible_printers_condition", "inherits"
"filament_cooling_moves", "filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "temperature",
"first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed", "max_fan_speed",
"bridge_fan_speed", "disable_fan_first_layers", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
"start_filament_gcode", "end_filament_gcode","compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}
@ -325,7 +326,12 @@ const std::vector<std::string>& Preset::printer_options()
"octoprint_host", "octoprint_apikey", "octoprint_cafile", "use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
"cooling_tube_length", "parking_pos_retraction", "max_print_height", "default_print_profile", "inherits",
"cooling_tube_length", "parking_pos_retraction", "extra_loading_move", "max_print_height", "default_print_profile", "inherits",
"silent_mode", "machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e"
};
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
}
@ -424,7 +430,90 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
{
DynamicPrintConfig cfg(this->default_preset().config);
cfg.apply_only(config, cfg.keys(), true);
return this->load_preset(path, name, std::move(cfg));
return this->load_preset(path, name, std::move(cfg), select);
}
static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const DynamicPrintConfig &cfg2)
{
t_config_option_keys diff = cfg1.diff(cfg2);
// Following keys are used by the UI, not by the slicing core, therefore they are not important
// when comparing profiles for equality. Ignore them.
for (const char *key : { "compatible_printers", "compatible_printers_condition", "inherits",
"print_settings_id", "filament_settings_id", "printer_settings_id",
"printer_model", "printer_variant", "default_print_profile", "default_filament_profile" })
diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end());
// Preset with the same name as stored inside the config exists.
return diff.empty();
}
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
// In case
Preset& PresetCollection::load_external_preset(
// Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
const std::string &path,
// Name of the profile, derived from the source file name.
const std::string &name,
// Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
const std::string &original_name,
// Config to initialize the preset from.
const DynamicPrintConfig &config,
// Select the preset after loading?
bool select)
{
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
DynamicPrintConfig cfg(this->default_preset().config);
cfg.apply_only(config, cfg.keys(), true);
// Is there a preset already loaded with the name stored inside the config?
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select)
this->select_preset(it - m_presets.begin());
return *it;
}
// Update the "inherits" field.
std::string &inherits = Preset::inherits(cfg);
if (it != m_presets.end() && inherits.empty()) {
// There is a profile with the same name already loaded. Should we update the "inherits" field?
if (it->vendor == nullptr)
inherits = it->inherits();
else
inherits = it->name;
}
// The external preset does not match an internal preset, load the external preset.
std::string new_name;
for (size_t idx = 0;; ++ idx) {
std::string suffix;
if (original_name.empty()) {
if (idx > 0)
suffix = " (" + std::to_string(idx) + ")";
} else {
if (idx == 0)
suffix = " (" + original_name + ")";
else
suffix = " (" + original_name + "-" + std::to_string(idx) + ")";
}
new_name = name + suffix;
it = this->find_preset_internal(new_name);
if (it == m_presets.end() || it->name != new_name)
// Unique profile name. Insert a new profile.
break;
if (profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select)
this->select_preset(it - m_presets.begin());
return *it;
}
// Form another profile name.
}
// Insert a new profile.
Preset &preset = this->load_preset(path, new_name, std::move(cfg), select);
preset.is_external = true;
if (&this->get_selected_preset() == &preset)
this->get_edited_preset().is_external = true;
return preset;
}
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select)
@ -460,7 +549,7 @@ void PresetCollection::save_current_preset(const std::string &new_name)
} else {
// Creating a new preset.
Preset &preset = *m_presets.insert(it, m_edited_preset);
std::string &inherits = preset.config.opt_string("inherits", true);
std::string &inherits = preset.inherits();
std::string old_name = preset.name;
preset.name = new_name;
preset.file = this->path_from_name(new_name);
@ -475,7 +564,6 @@ void PresetCollection::save_current_preset(const std::string &new_name)
// Inherited from a user preset. Just maintain the "inherited" flag,
// meaning it will inherit from either the system preset, or the inherited user preset.
}
preset.inherits = inherits;
preset.is_default = false;
preset.is_system = false;
preset.is_external = false;
@ -513,20 +601,20 @@ bool PresetCollection::load_bitmap_default(const std::string &file_name)
const Preset* PresetCollection::get_selected_preset_parent() const
{
auto *inherits = dynamic_cast<const ConfigOptionString*>(this->get_edited_preset().config.option("inherits"));
if (inherits == nullptr || inherits->value.empty())
return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; // nullptr;
const Preset* preset = this->find_preset(inherits->value, false);
const std::string &inherits = this->get_edited_preset().inherits();
if (inherits.empty())
return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset;
}
const Preset* PresetCollection::get_preset_parent(const Preset& child) const
{
auto *inherits = dynamic_cast<const ConfigOptionString*>(child.config.option("inherits"));
if (inherits == nullptr || inherits->value.empty())
const std::string &inherits = child.inherits();
if (inherits.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr;
const Preset* preset = this->find_preset(inherits->value, false);
const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
}
@ -601,6 +689,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
// Otherwise fill in the list from scratch.
ui->Freeze();
ui->Clear();
size_t selected_preset_item = 0;
const Preset &selected_preset = this->get_selected_preset();
// Show wide icons if the currently selected preset is not compatible with the current printer,
@ -641,7 +730,7 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str()),
(bmp == 0) ? (m_bitmap_main_frame ? *m_bitmap_main_frame : wxNullBitmap) : *bmp);
if (i == m_idx_selected)
ui->SetSelection(ui->GetCount() - 1);
selected_preset_item = ui->GetCount() - 1;
}
else
{
@ -658,10 +747,13 @@ void PresetCollection::update_platter_ui(wxBitmapComboBox *ui)
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected)
ui->SetSelection(ui->GetCount() - 1);
selected_preset_item = ui->GetCount() - 1;
}
}
ui->Thaw();
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
}
size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompatible)
@ -719,6 +811,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
}
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
return selected_preset_item;
}
@ -763,7 +856,7 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
std::initializer_list<const char*> optional_keys { "compatible_printers", "compatible_printers_condition" };
std::initializer_list<const char*> optional_keys { "compatible_printers" };
for (auto &opt_key : optional_keys) {
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
@ -772,17 +865,6 @@ std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, c
return changed;
}
std::vector<std::string> PresetCollection::system_equal_options() const
{
const Preset *edited = &this->get_edited_preset();
const Preset *reference = this->get_selected_preset_parent();
std::vector<std::string> equal;
if (edited != nullptr && reference != nullptr) {
equal = reference->config.equal(edited->config);
}
return equal;
}
// Select a new preset. This resets all the edits done to the currently selected preset.
// If the preset with index idx does not exist, a first visible preset is selected.
Preset& PresetCollection::select_preset(size_t idx)

View file

@ -113,9 +113,6 @@ public:
// or a Configuration file bundling the Print + Filament + Printer presets (in that case is_external and possibly is_system will be true),
// or it could be a G-code (again, is_external will be true).
std::string file;
// A user profile may inherit its settings either from a system profile, or from a user profile.
// A system profile shall never derive from any other profile, as the system profile hierarchy is being flattened during loading.
std::string inherits;
// If this is a system profile, then there should be a vendor data available to display at the UI.
const VendorProfile *vendor = nullptr;
@ -142,6 +139,16 @@ public:
bool is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const;
bool is_compatible_with_printer(const Preset &active_printer) const;
// Returns the name of the preset, from which this preset inherits.
static std::string& inherits(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("inherits", true)->value; }
std::string& inherits() { return Preset::inherits(this->config); }
const std::string& inherits() const { return Preset::inherits(const_cast<Preset*>(this)->config); }
// Returns the "compatible_printers_condition".
static std::string& compatible_printers_condition(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("compatible_printers_condition", true)->value; }
std::string& compatible_printers_condition() { return Preset::compatible_printers_condition(this->config); }
const std::string& compatible_printers_condition() const { return Preset::compatible_printers_condition(const_cast<Preset*>(this)->config); }
// Mark this preset as compatible if it is compatible with active_printer.
bool update_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config);
@ -200,6 +207,18 @@ public:
Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true);
Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true);
Preset& load_external_preset(
// Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
const std::string &path,
// Name of the profile, derived from the source file name.
const std::string &name,
// Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
const std::string &original_name,
// Config to initialize the preset from.
const DynamicPrintConfig &config,
// Select the preset after loading?
bool select = true);
// Save the preset under a new name. If the name is different from the old one,
// a new preset is stored into the list of presets.
// All presets are marked as not modified and the new preset is activated.
@ -312,8 +331,6 @@ public:
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_different_from_parent_options(const bool is_printer_type = false) const
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), is_printer_type); }
// Compare the content of get_selected_preset() with get_selected_preset_parent() configs, return the list of keys where they equal.
std::vector<std::string> system_equal_options() const;
// Update the choice UI from the list of presets.
// If show_incompatible, all presets are shown, otherwise only the compatible presets are shown.
@ -349,9 +366,10 @@ private:
PresetCollection(const PresetCollection &other);
PresetCollection& operator=(const PresetCollection &other);
// Find a preset in the sorted list of presets.
// Find a preset position in the sorted list of presets.
// The "-- default -- " preset is always the first, so it needs
// to be handled differently.
// If a preset does not exist, an iterator is returned indicating where to insert a preset with the same name.
std::deque<Preset>::iterator find_preset_internal(const std::string &name)
{
Preset key(m_type, name);

View file

@ -52,26 +52,37 @@ PresetBundle::PresetBundle() :
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
wxImage::AddHandler(new wxPNGHandler);
// The following keys are handled by the UI, they do not have a counterpart in any StaticPrintConfig derived classes,
// therefore they need to be handled differently. As they have no counterpart in StaticPrintConfig, they are not being
// initialized based on PrintConfigDef(), but to empty values (zeros, empty vectors, empty strings).
//
// "compatible_printers", "compatible_printers_condition", "inherits",
// "print_settings_id", "filament_settings_id", "printer_settings_id",
// "printer_vendor", "printer_model", "printer_variant", "default_print_profile", "default_filament_profile"
// Create the ID config keys, as they are not part of the Static print config classes.
this->prints.default_preset().config.opt_string("print_settings_id", true);
this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values.assign(1, std::string());
this->printers.default_preset().config.opt_string("printer_settings_id", true);
// "compatible printers" are not mandatory yet.
//FIXME Rename "compatible_printers" and "compatible_printers_condition", as they are defined in both print and filament profiles,
// therefore they are clashing when generating a a config file, G-code or AMF/3MF.
// this->filaments.default_preset().config.optptr("compatible_printers", true);
// this->filaments.default_preset().config.optptr("compatible_printers_condition", true);
// this->prints.default_preset().config.optptr("compatible_printers", true);
// this->prints.default_preset().config.optptr("compatible_printers_condition", true);
// Create the "printer_vendor", "printer_model" and "printer_variant" keys.
this->prints.default_preset().config.optptr("print_settings_id", true);
this->prints.default_preset().compatible_printers_condition();
this->prints.default_preset().inherits();
this->filaments.default_preset().config.option<ConfigOptionStrings>("filament_settings_id", true)->values = { "" };
this->filaments.default_preset().compatible_printers_condition();
this->filaments.default_preset().inherits();
this->printers.default_preset().config.optptr("printer_settings_id", true);
this->printers.default_preset().config.optptr("printer_vendor", true);
this->printers.default_preset().config.optptr("printer_model", true);
this->printers.default_preset().config.optptr("printer_variant", true);
// Load the default preset bitmaps.
this->printers.default_preset().config.optptr("default_print_profile", true);
this->printers.default_preset().config.option<ConfigOptionStrings>("default_filament_profile", true)->values = { "" };
this->printers.default_preset().inherits();
// Load the default preset bitmaps.
this->prints .load_bitmap_default("cog.png");
this->filaments.load_bitmap_default("spool.png");
this->printers .load_bitmap_default("printer_empty.png");
this->load_compatible_bitmaps();
// Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
this->prints .select_preset(0);
this->filaments.select_preset(0);
@ -372,9 +383,16 @@ DynamicPrintConfig PresetBundle::full_config() const
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(out.option("nozzle_diameter"));
size_t num_extruders = nozzle_diameter->values.size();
// Collect the "compatible_printers_condition" and "inherits" values over all presets (print, filaments, printers) into a single vector.
std::vector<std::string> compatible_printers_condition;
std::vector<std::string> inherits;
compatible_printers_condition.emplace_back(this->prints.get_edited_preset().compatible_printers_condition());
inherits .emplace_back(this->prints.get_edited_preset().inherits());
if (num_extruders <= 1) {
out.apply(this->filaments.get_edited_preset().config);
compatible_printers_condition.emplace_back(this->filaments.get_edited_preset().compatible_printers_condition());
inherits .emplace_back(this->filaments.get_edited_preset().inherits());
} else {
// Retrieve filament presets and build a single config object for them.
// First collect the filament configurations based on the user selection of this->filament_presets.
@ -384,11 +402,15 @@ DynamicPrintConfig PresetBundle::full_config() const
filament_configs.emplace_back(&this->filaments.find_preset(filament_preset_name, true)->config);
while (filament_configs.size() < num_extruders)
filament_configs.emplace_back(&this->filaments.first_visible().config);
for (const DynamicPrintConfig *cfg : filament_configs) {
compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(*const_cast<DynamicPrintConfig*>(cfg)));
inherits .emplace_back(Preset::inherits(*const_cast<DynamicPrintConfig*>(cfg)));
}
// Option values to set a ConfigOptionVector from.
std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr);
// loop through options and apply them to the resulting config.
for (const t_config_option_key &key : this->filaments.default_preset().config.keys()) {
if (key == "compatible_printers" || key == "compatible_printers_condition")
if (key == "compatible_printers")
continue;
// Get a destination option.
ConfigOption *opt_dst = out.option(key, false);
@ -406,9 +428,13 @@ DynamicPrintConfig PresetBundle::full_config() const
}
}
//FIXME These two value types clash between the print and filament profiles. They should be renamed.
// Don't store the "compatible_printers_condition" for the printer profile, there is none.
inherits.emplace_back(this->printers.get_edited_preset().inherits());
// These two value types clash between the print and filament profiles. They should be renamed.
out.erase("compatible_printers");
out.erase("compatible_printers_condition");
out.erase("inherits");
static const char *keys[] = { "perimeter", "infill", "solid_infill", "support_material", "support_material_interface" };
for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++ i) {
@ -418,6 +444,25 @@ DynamicPrintConfig PresetBundle::full_config() const
opt->value = boost::algorithm::clamp<int>(opt->value, 0, int(num_extruders));
}
out.option<ConfigOptionString >("print_settings_id", true)->value = this->prints.get_selected_preset().name;
out.option<ConfigOptionStrings>("filament_settings_id", true)->values = this->filament_presets;
out.option<ConfigOptionString >("printer_settings_id", true)->value = this->printers.get_selected_preset().name;
// Serialize the collected "compatible_printers_condition" and "inherits" fields.
// There will be 1 + num_exturders fields for "inherits" and 2 + num_extruders for "compatible_printers_condition" stored.
// The vector will not be stored if all fields are empty strings.
auto add_if_some_non_empty = [&out](std::vector<std::string> &&values, const std::string &key) {
bool nonempty = false;
for (const std::string &v : values)
if (! v.empty()) {
nonempty = true;
break;
}
if (nonempty)
out.set_key_value(key, new ConfigOptionStrings(std::move(values)));
};
add_if_some_non_empty(std::move(compatible_printers_condition), "compatible_printers_condition_cummulative");
add_if_some_non_empty(std::move(inherits), "inherits_cummulative");
return out;
}
@ -496,6 +541,18 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
}
size_t num_extruders = std::min(config.option<ConfigOptionFloats>("nozzle_diameter" )->values.size(),
config.option<ConfigOptionFloats>("filament_diameter")->values.size());
// Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which
// accumulate values over all presets (print, filaments, printers).
// These values will be distributed into their particular presets when loading.
std::vector<std::string> compatible_printers_condition_values = std::move(config.option<ConfigOptionStrings>("compatible_printers_condition_cummulative", true)->values);
std::vector<std::string> inherits_values = std::move(config.option<ConfigOptionStrings>("inherits_cummulative", true)->values);
std::string &compatible_printers_condition = Preset::compatible_printers_condition(config);
std::string &inherits = Preset::inherits(config);
compatible_printers_condition_values.resize(num_extruders + 2, std::string());
inherits_values.resize(num_extruders + 2, std::string());
// 1) Create a name from the file name.
// Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles.
std::string name = is_external ? boost::filesystem::path(name_or_path).filename().string() : name_or_path;
@ -504,24 +561,31 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// First load the print and printer presets.
for (size_t i_group = 0; i_group < 2; ++ i_group) {
PresetCollection &presets = (i_group == 0) ? this->prints : this->printers;
Preset &preset = presets.load_preset(is_external ? name_or_path : presets.path_from_name(name), name, config);
if (is_external)
preset.is_external = true;
// Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles.
size_t idx = (i_group == 0) ? 0 : num_extruders + 1;
inherits = inherits_values[idx];
compatible_printers_condition = compatible_printers_condition_values[idx];
if (is_external)
presets.load_external_preset(name_or_path, name,
config.opt_string((i_group == 0) ? "print_settings_id" : "printer_settings_id", true),
config);
else
preset.save();
presets.load_preset(presets.path_from_name(name), name, config).save();
}
// 3) Now load the filaments. If there are multiple filament presets, split them and load them.
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter"));
auto *filament_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_diameter"));
size_t num_extruders = std::min(nozzle_diameter->values.size(), filament_diameter->values.size());
auto old_filament_profile_names = config.option<ConfigOptionStrings>("filament_settings_id", true);
old_filament_profile_names->values.resize(num_extruders, std::string());
config.option<ConfigOptionStrings>("default_filament_profile", true)->values.resize(num_extruders, std::string());
if (num_extruders <= 1) {
Preset &preset = this->filaments.load_preset(
is_external ? name_or_path : this->filaments.path_from_name(name), name, config);
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
inherits = inherits_values[1];
compatible_printers_condition = compatible_printers_condition_values[1];
if (is_external)
preset.is_external = true;
this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config);
else
preset.save();
this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save();
this->filament_presets.clear();
this->filament_presets.emplace_back(name);
} else {
@ -543,21 +607,30 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
// Load the configs into this->filaments and make them active.
this->filament_presets.clear();
for (size_t i = 0; i < configs.size(); ++ i) {
char suffix[64];
if (i == 0)
suffix[0] = 0;
else
sprintf(suffix, " (%d)", i);
std::string new_name = name + suffix;
DynamicPrintConfig &cfg = configs[i];
// Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets.
cfg.opt_string("compatible_printers_condition", true) = compatible_printers_condition_values[i + 1];
cfg.opt_string("inherits", true) = inherits_values[i + 1];
// Load all filament presets, but only select the first one in the preset dialog.
Preset &preset = this->filaments.load_preset(
is_external ? name_or_path : this->filaments.path_from_name(new_name),
new_name, std::move(configs[i]), i == 0);
Preset *loaded = nullptr;
if (is_external)
preset.is_external = true;
else
preset.save();
this->filament_presets.emplace_back(new_name);
loaded = &this->filaments.load_external_preset(name_or_path, name,
(i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "",
std::move(cfg), i == 0);
else {
// Used by the config wizard when creating a custom setup.
// Therefore this block should only be called for a single extruder.
char suffix[64];
if (i == 0)
suffix[0] = 0;
else
sprintf(suffix, "%d", i);
std::string new_name = name + suffix;
loaded = &this->filaments.load_preset(this->filaments.path_from_name(new_name),
new_name, std::move(cfg), i == 0);
loaded->save();
}
this->filament_presets.emplace_back(loaded->name);
}
}
@ -1108,6 +1181,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
// Fill in the list from scratch.
ui->Freeze();
ui->Clear();
size_t selected_preset_item = 0;
const Preset *selected_preset = this->filaments.find_preset(this->filament_presets[idx_extruder]);
// Show wide icons if the currently selected preset is not compatible with the current printer,
// and draw a red flag in front of the selected preset.
@ -1159,7 +1233,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
ui->Append(wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str()),
(bitmap == 0) ? wxNullBitmap : *bitmap);
if (selected)
ui->SetSelection(ui->GetCount() - 1);
selected_preset_item = ui->GetCount() - 1;
}
else
{
@ -1178,9 +1252,11 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, wxBitma
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected_str)
ui->SetSelection(ui->GetCount() - 1);
selected_preset_item = ui->GetCount() - 1;
}
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
}

View file

@ -1292,6 +1292,10 @@ void TabFilament::build()
optgroup->append_single_option_line("filament_loading_speed");
optgroup->append_single_option_line("filament_unloading_speed");
optgroup->append_single_option_line("filament_toolchange_delay");
optgroup->append_single_option_line("filament_cooling_moves");
optgroup->append_single_option_line("filament_cooling_initial_speed");
optgroup->append_single_option_line("filament_cooling_final_speed");
line = { _(L("Ramming")), "" };
line.widget = [this](wxWindow* parent){
auto ramming_dialog_btn = new wxButton(parent, wxID_ANY, _(L("Ramming settings"))+dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
@ -1601,6 +1605,22 @@ void TabPrinter::build()
optgroup = page->new_optgroup(_(L("Firmware")));
optgroup->append_single_option_line("gcode_flavor");
optgroup->append_single_option_line("silent_mode");
optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value){
wxTheApp->CallAfter([this, opt_key, value](){
if (opt_key.compare("silent_mode") == 0) {
bool val = boost::any_cast<bool>(value);
if (m_use_silent_mode != val) {
m_rebuild_kinematics_page = true;
m_use_silent_mode = val;
}
}
build_extruder_pages();
update_dirty();
on_value_change(opt_key, value);
});
};
optgroup = page->new_optgroup(_(L("Advanced")));
optgroup->append_single_option_line("use_relative_e_distances");
@ -1682,8 +1702,94 @@ void TabPrinter::extruders_count_changed(size_t extruders_count){
on_value_change("extruders_count", extruders_count);
}
void TabPrinter::build_extruder_pages(){
void TabPrinter::append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key)
{
auto option = optgroup->get_option(opt_key, 0);
auto line = Line{ option.opt.full_label, "" };
line.append_option(option);
if (m_use_silent_mode)
line.append_option(optgroup->get_option(opt_key, 1));
optgroup->append_line(line);
}
PageShp TabPrinter::build_kinematics_page()
{
auto page = add_options_page(_(L("Machine limits")), "cog.png", true);
if (m_use_silent_mode) {
// Legend for OptionsGroups
auto optgroup = page->new_optgroup(_(L("")));
optgroup->set_show_modified_btns_val(false);
optgroup->label_width = 230;
auto line = Line{ "", "" };
ConfigOptionDef def;
def.type = coString;
def.width = 150;
def.gui_type = "legend";
def.tooltip = L("Values in this column are for Full Power mode");
def.default_value = new ConfigOptionString{ L("Full Power") };
auto option = Option(def, "full_power_legend");
line.append_option(option);
def.tooltip = L("Values in this column are for Silent mode");
def.default_value = new ConfigOptionString{ L("Silent") };
option = Option(def, "silent_legend");
line.append_option(option);
optgroup->append_line(line);
}
std::vector<std::string> axes{ "x", "y", "z", "e" };
auto optgroup = page->new_optgroup(_(L("Maximum accelerations")));
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_acceleration_" + axis);
}
optgroup = page->new_optgroup(_(L("Maximum feedrates")));
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_feedrate_" + axis);
}
optgroup = page->new_optgroup(_(L("Starting Acceleration")));
append_option_line(optgroup, "machine_max_acceleration_extruding");
append_option_line(optgroup, "machine_max_acceleration_retracting");
optgroup = page->new_optgroup(_(L("Advanced")));
append_option_line(optgroup, "machine_min_extruding_rate");
append_option_line(optgroup, "machine_min_travel_rate");
for (const std::string &axis : axes) {
append_option_line(optgroup, "machine_max_jerk_" + axis);
}
return page;
}
void TabPrinter::build_extruder_pages()
{
size_t n_before_extruders = 2; // Count of pages before Extruder pages
bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin;
// Add/delete Kinematics page according to is_marlin_flavor
size_t existed_page = 0;
for (int i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already
if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) {
if (!is_marlin_flavor || m_rebuild_kinematics_page)
m_pages.erase(m_pages.begin() + i);
else
existed_page = i;
break;
}
if (existed_page < n_before_extruders && is_marlin_flavor){
auto page = build_kinematics_page();
m_pages.insert(m_pages.begin() + n_before_extruders, page);
}
if (is_marlin_flavor)
n_before_extruders++;
size_t n_after_single_extruder_MM = 2; // Count of pages after single_extruder_multi_material page
if (m_extruders_count_old == m_extruders_count ||
@ -1704,6 +1810,7 @@ void TabPrinter::build_extruder_pages(){
optgroup->append_single_option_line("cooling_tube_retraction");
optgroup->append_single_option_line("cooling_tube_length");
optgroup->append_single_option_line("parking_pos_retraction");
optgroup->append_single_option_line("extra_loading_move");
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
m_has_single_extruder_MM_page = true;
}
@ -1757,7 +1864,6 @@ void TabPrinter::build_extruder_pages(){
m_pages.begin() + n_before_extruders + m_extruders_count_old);
m_extruders_count_old = m_extruders_count;
rebuild_page_tree();
}
@ -1792,6 +1898,17 @@ void TabPrinter::update(){
get_field("toolchange_gcode")->toggle(have_multiple_extruders);
get_field("single_extruder_multi_material")->toggle(have_multiple_extruders);
bool is_marlin_flavor = m_config->option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor")->value == gcfMarlin;
const std::string &printer_model = m_config->opt_string("printer_model");
bool can_use_silent_mode = printer_model.empty() ? false : printer_model == "MK3"; // "true" only for MK3 printers
get_field("silent_mode")->toggle(can_use_silent_mode && is_marlin_flavor);
if (can_use_silent_mode && m_use_silent_mode != m_config->opt_bool("silent_mode")) {
m_rebuild_kinematics_page = true;
m_use_silent_mode = m_config->opt_bool("silent_mode");
}
for (size_t i = 0; i < m_extruders_count; ++i) {
bool have_retract_length = m_config->opt_float("retract_length", i) > 0;
@ -1910,7 +2027,8 @@ void Tab::rebuild_page_tree()
auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID());
m_treectrl->SetItemTextColour(itemId, p->get_item_colour());
if (p->title() == selected) {
m_disable_tree_sel_changed_event = 1;
if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange
m_disable_tree_sel_changed_event = 1;
m_treectrl->SelectItem(itemId);
m_disable_tree_sel_changed_event = 0;
have_selection = 1;

View file

@ -175,7 +175,7 @@ protected:
std::vector<std::string> m_reload_dependent_tabs = {};
enum OptStatus { osSystemValue = 1, osInitValue = 2 };
std::map<std::string, int> m_options_list;
int m_opt_status_value;
int m_opt_status_value = 0;
t_icon_descriptions m_icon_descriptions = {};
@ -316,6 +316,9 @@ public:
class TabPrinter : public Tab
{
bool m_has_single_extruder_MM_page = false;
bool m_use_silent_mode = false;
void append_option_line(ConfigOptionsGroupShp optgroup, const std::string opt_key);
bool m_rebuild_kinematics_page = false;
public:
wxButton* m_serial_test_btn;
wxButton* m_octoprint_host_test_btn;
@ -333,6 +336,7 @@ public:
void update() override;
void update_serial_ports();
void extruders_count_changed(size_t extruders_count);
PageShp build_kinematics_page();
void build_extruder_pages();
void on_preset_loaded() override;
void init_options_list() override;