Merge branch 'master' into ys_menuicons

This commit is contained in:
bubnikv 2019-05-07 09:32:29 +02:00
commit 347a65bc4a
41 changed files with 2572 additions and 1562 deletions

View File

@ -234,7 +234,11 @@ if(SLIC3R_STATIC)
endif()
#set(Boost_DEBUG ON)
# set(Boost_COMPILER "-vc120")
find_package(Boost REQUIRED COMPONENTS system filesystem thread log locale regex)
if(NOT WIN32)
# boost::process was introduced first in version 1.64.0
set(MINIMUM_BOOST_VERSION "1.64.0")
endif()
find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS system filesystem thread log locale regex)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
if (APPLE)

File diff suppressed because it is too large Load Diff

View File

@ -201,12 +201,13 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
gcodegen.writer().toolchange(new_extruder_id);
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
// Always append the filament start G-code even if the extruder did not switch,
// because the wipe tower resets the linear advance and we want it to be re-enabled.
const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id);
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only.
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);

View File

@ -1451,6 +1451,50 @@ std::string ModelObject::get_export_filename() const
return ret;
}
stl_stats ModelObject::get_object_stl_stats() const
{
if (this->volumes.size() == 1)
return this->volumes[0]->mesh.stl.stats;
stl_stats full_stats = this->volumes[0]->mesh.stl.stats;
// fill full_stats from all objet's meshes
for (ModelVolume* volume : this->volumes)
{
if (volume->id() == this->volumes[0]->id())
continue;
const stl_stats& stats = volume->mesh.stl.stats;
// initialize full_stats (for repaired errors)
full_stats.degenerate_facets += stats.degenerate_facets;
full_stats.edges_fixed += stats.edges_fixed;
full_stats.facets_removed += stats.facets_removed;
full_stats.facets_added += stats.facets_added;
full_stats.facets_reversed += stats.facets_reversed;
full_stats.backwards_edges += stats.backwards_edges;
// another used satistics value
if (volume->is_model_part()) {
full_stats.volume += stats.volume;
full_stats.number_of_parts += stats.number_of_parts;
}
}
return full_stats;
}
int ModelObject::get_mesh_errors_count(const int vol_idx /*= -1*/) const
{
if (vol_idx >= 0)
return this->volumes[vol_idx]->get_mesh_errors_count();
const stl_stats& stats = get_object_stl_stats();
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
}
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
@ -1516,6 +1560,14 @@ void ModelVolume::calculate_convex_hull()
m_convex_hull = mesh.convex_hull_3d();
}
int ModelVolume::get_mesh_errors_count() const
{
const stl_stats& stats = this->mesh.stl.stats;
return stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
}
const TriangleMesh& ModelVolume::get_convex_hull() const
{
return m_convex_hull;

View File

@ -277,6 +277,11 @@ public:
std::string get_export_filename() const;
// Get full stl statistics for all object's meshes
stl_stats get_object_stl_stats() const;
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
protected:
friend class Print;
friend class SLAPrint;
@ -370,6 +375,8 @@ public:
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
// Get count of errors in the mesh
int get_mesh_errors_count() const;
// Helpers for loading / storing into AMF / 3MF files.
static ModelVolumeType type_from_string(const std::string &s);

View File

@ -1473,7 +1473,7 @@ void Print::process()
BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info();
for (PrintObject *obj : m_objects)
obj->make_perimeters();
this->set_status(70, "Infilling layers");
this->set_status(70, L("Infilling layers"));
for (PrintObject *obj : m_objects)
obj->infill();
for (PrintObject *obj : m_objects)
@ -1481,7 +1481,7 @@ void Print::process()
if (this->set_started(psSkirt)) {
m_skirt.clear();
if (this->has_skirt()) {
this->set_status(88, "Generating skirt");
this->set_status(88, L("Generating skirt"));
this->_make_skirt();
}
this->set_done(psSkirt);
@ -1489,7 +1489,7 @@ void Print::process()
if (this->set_started(psBrim)) {
m_brim.clear();
if (m_config.brim_width > 0) {
this->set_status(88, "Generating brim");
this->set_status(88, L("Generating brim"));
this->_make_brim();
}
this->set_done(psBrim);
@ -1497,7 +1497,7 @@ void Print::process()
if (this->set_started(psWipeTower)) {
m_wipe_tower_data.clear();
if (this->has_wipe_tower()) {
//this->set_status(95, "Generating wipe tower");
//this->set_status(95, L("Generating wipe tower"));
this->_make_wipe_tower();
}
this->set_done(psWipeTower);
@ -1514,7 +1514,8 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa
// output everything to a G-code file
// The following call may die if the output_filename_format template substitution fails.
std::string path = this->output_filepath(path_template);
std::string message = "Exporting G-code";
std::string message = L("Exporting G-code");
// #ys_FIXME_localization
if (! path.empty() && preview_data == nullptr) {
// Only show the path if preview_data is not set -> running from command line.
message += " to ";

View File

@ -92,9 +92,9 @@ void PrintConfigDef::init_common_params()
def->default_value = new ConfigOptionString("");
def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA File";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
def->label = L("HTTPS CA File");
def->tooltip = L("Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.");
def->mode = comAdvanced;
def->default_value = new ConfigOptionString("");
}
@ -145,6 +145,7 @@ void PrintConfigDef::init_fff_params()
def->default_value = new ConfigOptionString("");
def = this->add("bottom_solid_layers", coInt);
//TRN To be shown in Print Settings "Bottom solid layers"
def->label = L("Bottom");
def->category = L("Layers and Perimeters");
def->tooltip = L("Number of solid layers to generate on bottom surfaces.");
@ -913,10 +914,10 @@ void PrintConfigDef::init_fff_params()
def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
def = this->add("gcode_label_objects", coBool);
def->label = "Label objects";
def->tooltip = "Enable this to add comments into the G-Code labeling print moves with what object they belong to,"
def->label = L("Label objects");
def->tooltip = L("Enable this to add comments into the G-Code labeling print moves with what object they belong to,"
" which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with "
"Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill.";
"Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill.");
def->mode = comAdvanced;
def->default_value = new ConfigOptionBool(0);
@ -2038,6 +2039,7 @@ void PrintConfigDef::init_fff_params()
def->default_value = new ConfigOptionFloatOrPercent(15, false);
def = this->add("top_solid_layers", coInt);
//TRN To be shown in Print Settings "Top solid layers"
def->label = L("Top");
def->category = L("Layers and Perimeters");
def->tooltip = L("Number of solid layers to generate on top surfaces.");
@ -2141,7 +2143,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("wipe_tower_rotation_angle", coFloat);
def->label = L("Wipe tower rotation angle");
def->tooltip = L("Wipe tower rotation angle with respect to x-axis ");
def->sidetext = L("degrees");
def->sidetext = L("°");
def->mode = comAdvanced;
def->default_value = new ConfigOptionFloat(0.);

View File

@ -2,6 +2,7 @@
#include "BoundingBox.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "I18N.hpp"
#include "SupportMaterial.hpp"
#include "Surface.hpp"
#include "Slicing.hpp"
@ -17,6 +18,10 @@
#include <Shiny/Shiny.h>
//! macro used to mark string used at localization,
//! return same string
#define L(s) Slic3r::I18N::translate(s)
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
#define SLIC3R_DEBUG
#endif
@ -102,7 +107,7 @@ void PrintObject::slice()
{
if (! this->set_started(posSlice))
return;
m_print->set_status(10, "Processing triangulated mesh");
m_print->set_status(10, L("Processing triangulated mesh"));
std::vector<coordf_t> layer_height_profile;
this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile);
m_print->throw_if_canceled();
@ -133,7 +138,7 @@ void PrintObject::make_perimeters()
if (! this->set_started(posPerimeters))
return;
m_print->set_status(20, "Generating perimeters");
m_print->set_status(20, L("Generating perimeters"));
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
// merge slices if they were split into types
@ -243,7 +248,7 @@ void PrintObject::prepare_infill()
if (! this->set_started(posPrepareInfill))
return;
m_print->set_status(30, "Preparing infill");
m_print->set_status(30, L("Preparing infill"));
// This will assign a type (top/bottom/internal) to $layerm->slices.
// Then the classifcation of $layerm->slices is transfered onto
@ -383,7 +388,7 @@ void PrintObject::generate_support_material()
if (this->set_started(posSupportMaterial)) {
this->clear_support_layers();
if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) {
m_print->set_status(85, "Generating support material");
m_print->set_status(85, L("Generating support material"));
this->_generate_support_material();
m_print->throw_if_canceled();
} else {

View File

@ -176,6 +176,8 @@ private:
{
return level<T>(r1) < level<T>(r2);
});
if(it == cont.end()) return it;
T diff = std::abs(level<T>(*it) - lvl);

View File

@ -11,6 +11,8 @@
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
#define ENABLE_RENDER_SELECTION_CENTER 0
// Shows an imgui dialog with render related data
#define ENABLE_RENDER_STATISTICS 0
//====================

View File

@ -1847,116 +1847,101 @@ TriangleMesh make_cube(double x, double y, double z) {
// Generate the mesh for a cylinder and return it, using
// the generated angle to calculate the top mesh triangles.
// Default is 360 sides, angle fa is in radians.
TriangleMesh make_cylinder(double r, double h, double fa) {
Pointf3s vertices;
std::vector<Vec3crd> facets;
TriangleMesh make_cylinder(double r, double h, double fa)
{
size_t n_steps = (size_t)ceil(2. * PI / fa);
double angle_step = 2. * PI / n_steps;
Pointf3s vertices;
std::vector<Vec3crd> facets;
vertices.reserve(2 * n_steps + 2);
facets.reserve(4 * n_steps);
// 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Vec3d(0.0, 0.0, 0.0));
vertices.emplace_back(Vec3d(0.0, 0.0, h));
// adjust via rounding to get an even multiple for any provided angle.
double angle = (2*PI / floor(2*PI / fa));
// for each line along the polygon approximating the top/bottom of the
// circle, generate four points and four facets (2 for the wall, 2 for the
// top and bottom.
// Special case: Last line shares 2 vertices with the first line.
unsigned id = vertices.size() - 1;
vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, 0));
vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, h));
for (double i = 0; i < 2*PI; i+=angle) {
Vec2d p = Eigen::Rotation2Dd(i) * Eigen::Vector2d(0, r);
Vec2d p = Eigen::Rotation2Dd(0.) * Eigen::Vector2d(0, r);
vertices.emplace_back(Vec3d(p(0), p(1), 0.));
vertices.emplace_back(Vec3d(p(0), p(1), h));
for (size_t i = 1; i < n_steps; ++i) {
p = Eigen::Rotation2Dd(angle_step * i) * Eigen::Vector2d(0, r);
vertices.emplace_back(Vec3d(p(0), p(1), 0.));
vertices.emplace_back(Vec3d(p(0), p(1), h));
id = vertices.size() - 1;
int id = (int)vertices.size() - 1;
facets.emplace_back(Vec3crd( 0, id - 1, id - 3)); // top
facets.emplace_back(Vec3crd(id, 1, id - 2)); // bottom
facets.emplace_back(Vec3crd(id, id - 2, id - 3)); // upper-right of side
facets.emplace_back(Vec3crd(id, id - 2, id - 3)); // upper-right of side
facets.emplace_back(Vec3crd(id, id - 3, id - 1)); // bottom-left of side
}
// Connect the last set of vertices with the first.
facets.emplace_back(Vec3crd( 2, 0, id - 1));
facets.emplace_back(Vec3crd( 1, 3, id));
facets.emplace_back(Vec3crd(id, 3, 2));
facets.emplace_back(Vec3crd(id, 2, id - 1));
int id = (int)vertices.size() - 1;
facets.emplace_back(Vec3crd( 0, 2, id - 1));
facets.emplace_back(Vec3crd( 3, 1, id));
facets.emplace_back(Vec3crd(id, 2, 3));
facets.emplace_back(Vec3crd(id, id - 1, 2));
TriangleMesh mesh(vertices, facets);
return mesh;
return TriangleMesh(std::move(vertices), std::move(facets));
}
// Generates mesh for a sphere centered about the origin, using the generated angle
// to determine the granularity.
// Default angle is 1 degree.
TriangleMesh make_sphere(double rho, double fa) {
Pointf3s vertices;
std::vector<Vec3crd> facets;
//FIXME better to discretize an Icosahedron recursively http://www.songho.ca/opengl/gl_sphere.html
TriangleMesh make_sphere(double radius, double fa)
{
int sectorCount = ceil(2. * M_PI / fa);
int stackCount = ceil(M_PI / fa);
float sectorStep = 2. * M_PI / sectorCount;
float stackStep = M_PI / stackCount;
// Algorithm:
// Add points one-by-one to the sphere grid and form facets using relative coordinates.
// Sphere is composed effectively of a mesh of stacked circles.
Pointf3s vertices;
vertices.reserve((stackCount - 1) * sectorCount + 2);
for (int i = 0; i <= stackCount; ++ i) {
// from pi/2 to -pi/2
double stackAngle = 0.5 * M_PI - stackStep * i;
double xy = radius * cos(stackAngle);
double z = radius * sin(stackAngle);
if (i == 0 || i == stackCount)
vertices.emplace_back(Vec3d(xy, 0., z));
else
for (int j = 0; j < sectorCount; ++ j) {
// from 0 to 2pi
double sectorAngle = sectorStep * j;
vertices.emplace_back(Vec3d(xy * cos(sectorAngle), xy * sin(sectorAngle), z));
}
}
// adjust via rounding to get an even multiple for any provided angle.
double angle = (2*PI / floor(2*PI / fa));
// Ring to be scaled to generate the steps of the sphere
std::vector<double> ring;
for (double i = 0; i < 2*PI; i+=angle) {
ring.emplace_back(i);
}
const size_t steps = ring.size();
const double increment = (double)(1.0 / (double)steps);
// special case: first ring connects to 0,0,0
// insert and form facets.
vertices.emplace_back(Vec3d(0.0, 0.0, -rho));
size_t id = vertices.size();
for (size_t i = 0; i < ring.size(); i++) {
// Fixed scaling
const double z = -rho + increment*rho*2.0;
// radius of the circle for this step.
const double r = sqrt(abs(rho*rho - z*z));
Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
vertices.emplace_back(Vec3d(b(0), b(1), z));
facets.emplace_back((i == 0) ? Vec3crd(1, 0, ring.size()) : Vec3crd(id, 0, id - 1));
++ id;
}
// General case: insert and form facets for each step, joining it to the ring below it.
for (size_t s = 2; s < steps - 1; s++) {
const double z = -rho + increment*(double)s*2.0*rho;
const double r = sqrt(abs(rho*rho - z*z));
for (size_t i = 0; i < ring.size(); i++) {
Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
vertices.emplace_back(Vec3d(b(0), b(1), z));
if (i == 0) {
// wrap around
facets.emplace_back(Vec3crd(id + ring.size() - 1 , id, id - 1));
facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1));
} else {
facets.emplace_back(Vec3crd(id , id - ring.size(), (id - 1) - ring.size()));
facets.emplace_back(Vec3crd(id, id - 1 - ring.size() , id - 1));
}
id++;
}
}
// special case: last ring connects to 0,0,rho*2.0
// only form facets.
vertices.emplace_back(Vec3d(0.0, 0.0, rho));
for (size_t i = 0; i < ring.size(); i++) {
if (i == 0) {
// third vertex is on the other side of the ring.
facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1));
} else {
facets.emplace_back(Vec3crd(id, id - ring.size() + i, id - ring.size() + (i - 1)));
}
}
id++;
TriangleMesh mesh(vertices, facets);
return mesh;
std::vector<Vec3crd> facets;
facets.reserve(2 * (stackCount - 1) * sectorCount);
for (int i = 0; i < stackCount; ++ i) {
// Beginning of current stack.
int k1 = (i == 0) ? 0 : (1 + (i - 1) * sectorCount);
int k1_first = k1;
// Beginning of next stack.
int k2 = (i == 0) ? 1 : (k1 + sectorCount);
int k2_first = k2;
for (int j = 0; j < sectorCount; ++ j) {
// 2 triangles per sector excluding first and last stacks
int k1_next = k1;
int k2_next = k2;
if (i != 0) {
k1_next = (j + 1 == sectorCount) ? k1_first : (k1 + 1);
facets.emplace_back(Vec3crd(k1, k2, k1_next));
}
if (i + 1 != stackCount) {
k2_next = (j + 1 == sectorCount) ? k2_first : (k2 + 1);
facets.emplace_back(Vec3crd(k1_next, k2, k2_next));
}
k1 = k1_next;
k2 = k2_next;
}
}
return TriangleMesh(std::move(vertices), std::move(facets));
}
}

View File

@ -628,7 +628,7 @@ std::string CLI::output_filepath(const Model &model, IO::ExportFormat format) co
{
std::string ext;
switch (format) {
case IO::AMF: ext = ".amf"; break;
case IO::AMF: ext = ".zip.amf"; break;
case IO::OBJ: ext = ".obj"; break;
case IO::STL: ext = ".stl"; break;
case IO::TMF: ext = ".3mf"; break;

View File

@ -47,6 +47,8 @@ set(SLIC3R_GUI_SOURCES
GUI/Gizmos/GLGizmoFlatten.hpp
GUI/Gizmos/GLGizmoCut.cpp
GUI/Gizmos/GLGizmoCut.hpp
GUI/GLSelectionRectangle.cpp
GUI/GLSelectionRectangle.hpp
GUI/GLTexture.hpp
GUI/GLTexture.cpp
GUI/GLToolbar.hpp

View File

@ -223,7 +223,8 @@ void GLIndexedVertexArray::render(
}
const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::HOVER_SELECT_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::HOVER_DESELECT_COLOR[4] = { 1.0f, 0.75f, 0.75f, 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 };
const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f };
@ -251,7 +252,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, zoom_to_volumes(true)
, shader_outside_printer_detection_enabled(false)
, is_outside(false)
, hover(false)
, hover(HS_None)
, is_modifier(false)
, is_wipe_tower(false)
, is_extrusion_path(false)
@ -291,10 +292,12 @@ void GLVolume::set_render_color()
if (force_native_color)
set_render_color(color, 4);
else {
if (selected)
if (hover == HS_Select)
set_render_color(HOVER_SELECT_COLOR, 4);
else if (hover == HS_Deselect)
set_render_color(HOVER_DESELECT_COLOR, 4);
else if (selected)
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (disabled)
set_render_color(DISABLED_COLOR, 4);
else if (is_outside && shader_outside_printer_detection_enabled)

View File

@ -225,7 +225,8 @@ private:
class GLVolume {
public:
static const float SELECTED_COLOR[4];
static const float HOVER_COLOR[4];
static const float HOVER_SELECT_COLOR[4];
static const float HOVER_DESELECT_COLOR[4];
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
static const float DISABLED_COLOR[4];
@ -233,6 +234,13 @@ public:
static const float SLA_SUPPORT_COLOR[4];
static const float SLA_PAD_COLOR[4];
enum EHoverState : unsigned char
{
HS_None,
HS_Select,
HS_Deselect
};
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
~GLVolume();
@ -296,8 +304,8 @@ public:
bool shader_outside_printer_detection_enabled;
// Wheter or not this volume is outside print volume.
bool is_outside;
// Boolean: Is mouse over this object?
bool hover;
// Is mouse or rectangle selection over this object to select/deselect it ?
EHoverState hover;
// Wheter or not this volume has been generated from a modifier
bool is_modifier;
// Wheter or not this volume has been generated from the wipe tower

View File

@ -28,6 +28,7 @@
#include <boost/filesystem.hpp>
#include <boost/log/trivial.hpp>
#include <boost/nowide/cstdio.hpp>
#include "I18N.hpp"
namespace Slic3r {
@ -81,13 +82,14 @@ void BackgroundSlicingProcess::process_fff()
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
if (copy_file(m_temp_output_path, export_path) != 0)
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
m_print->set_status(95, "Running post-processing scripts");
m_print->set_status(95, L("Running post-processing scripts"));
run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, "G-code file exported to " + export_path);
// #ys_FIXME_localization
m_print->set_status(100, L("G-code file exported to ") + export_path);
} else if (! m_upload_job.empty()) {
prepare_upload();
} else {
m_print->set_status(100, "Slicing complete");
m_print->set_status(100, L("Slicing complete"));
}
this->set_step_done(bspsGCodeFinalize);
}
@ -101,11 +103,12 @@ void BackgroundSlicingProcess::process_sla()
if (! m_export_path.empty()) {
const std::string export_path = m_sla_print->print_statistics().finalize_output_path(m_export_path);
m_sla_print->export_raster(export_path);
m_print->set_status(100, "Masked SLA file exported to " + export_path);
// #ys_FIXME_localization
m_print->set_status(100, L("Masked SLA file exported to ") + export_path);
} else if (! m_upload_job.empty()) {
prepare_upload();
} else {
m_print->set_status(100, "Slicing complete");
m_print->set_status(100, L("Slicing complete"));
}
this->set_step_done(bspsGCodeFinalize);
}
@ -394,7 +397,7 @@ void BackgroundSlicingProcess::prepare_upload()
/ boost::filesystem::unique_path("." SLIC3R_APP_KEY ".upload.%%%%-%%%%-%%%%-%%%%");
if (m_print == m_fff_print) {
m_print->set_status(95, "Running post-processing scripts");
m_print->set_status(95, L("Running post-processing scripts"));
if (copy_file(m_temp_output_path, source_path.string()) != 0) {
throw std::runtime_error("Copying of the temporary G-code to the output G-code failed");
}
@ -405,7 +408,8 @@ void BackgroundSlicingProcess::prepare_upload()
m_sla_print->export_raster(source_path.string(), m_upload_job.upload_data.upload_path.string());
}
m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str());
// #ys_FIXME_localization
m_print->set_status(100, (boost::format(L("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue")) % m_upload_job.printhost->get_host()).str());
m_upload_job.upload_data.source_path = std::move(source_path);

View File

@ -52,6 +52,9 @@
#include <float.h>
#include <algorithm>
#include <cmath>
#if ENABLE_RENDER_STATISTICS
#include <chrono>
#endif // ENABLE_RENDER_STATISTICS
static const float TRACKBALLSIZE = 0.8f;
static const float GROUND_Z = -0.02f;
@ -258,7 +261,7 @@ void GLCanvas3D::LayersEditing::select_object(const Model &model, int object_id)
// Maximum height of an object changes when the object gets rotated or scaled.
// Changing maximum height of an object will invalidate the layer heigth editing profile.
// m_model_object->raw_bounding_box() is cached, therefore it is cheap even if this method is called frequently.
float new_max_z = (m_model_object == nullptr) ? 0.f : m_model_object->raw_bounding_box().size().z();
float new_max_z = (model_object_new == nullptr) ? 0.f : model_object_new->raw_bounding_box().size().z();
if (m_model_object != model_object_new || this->last_object_id != object_id || m_object_max_z != new_max_z ||
(model_object_new != nullptr && m_model_object->id() != model_object_new->id())) {
m_layer_height_profile.clear();
@ -672,6 +675,7 @@ GLCanvas3D::Mouse::Mouse()
: dragging(false)
, position(DBL_MAX, DBL_MAX)
, scene_position(DBL_MAX, DBL_MAX, DBL_MAX)
, ignore_left_up(false)
{
}
@ -791,7 +795,7 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GL
if (msg_utf8.empty())
return false;
wxString msg = GUI::from_u8(msg_utf8);
wxString msg = _(msg_utf8);//GUI::from_u8(msg_utf8);
wxMemoryDC memDC;
@ -1228,7 +1232,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_initialized(false)
, m_use_VBOs(false)
, m_apply_zoom_to_volumes_filter(false)
, m_hover_volume_id(-1)
, m_legend_texture_enabled(false)
, m_picking_enabled(false)
, m_moving_enabled(false)
@ -1237,6 +1240,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_regenerate_volumes(true)
, m_moving(false)
, m_tab_down(false)
, m_cursor_type(Standard)
, m_color_by("volume")
, m_reload_delayed(false)
, m_render_sla_auxiliaries(true)
@ -1588,6 +1592,10 @@ void GLCanvas3D::render()
if (!_set_current() || !_3DScene::init(m_canvas))
return;
#if ENABLE_RENDER_STATISTICS
auto start_time = std::chrono::high_resolution_clock::now();
#endif // ENABLE_RENDER_STATISTICS
if (m_bed.get_shape().empty())
{
// this happens at startup when no data is still saved under <>\AppData\Roaming\Slic3rPE
@ -1617,8 +1625,15 @@ void GLCanvas3D::render()
wxGetApp().imgui()->new_frame();
// picking pass
_picking_pass();
if (m_picking_enabled)
{
if (m_rectangle_selection.is_dragging())
// picking pass using rectangle selection
_rectangular_selection_picking_pass();
else
// regular picking pass
_picking_pass();
}
// draw scene
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
@ -1654,6 +1669,9 @@ void GLCanvas3D::render()
_render_camera_target();
#endif // ENABLE_SHOW_CAMERA_TARGET
if (m_picking_enabled && m_rectangle_selection.is_dragging())
m_rectangle_selection.render(*this);
// draw overlays
_render_gizmos_overlay();
_render_warning_texture();
@ -1666,9 +1684,26 @@ void GLCanvas3D::render()
if ((m_layers_editing.last_object_id >= 0) && (m_layers_editing.object_max_z() > 0.0f))
m_layers_editing.render_overlay(*this);
#if ENABLE_RENDER_STATISTICS
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_bg_alpha(0.5f);
imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
imgui.text("Last frame: ");
ImGui::SameLine();
imgui.text(std::to_string(m_render_stats.last_frame));
ImGui::SameLine();
imgui.text(" ms");
imgui.end();
#endif // ENABLE_RENDER_STATISTICS
wxGetApp().imgui()->render();
m_canvas->SwapBuffers();
#if ENABLE_RENDER_STATISTICS
auto end_time = std::chrono::high_resolution_clock::now();
m_render_stats.last_frame = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time).count();
#endif // ENABLE_RENDER_STATISTICS
}
void GLCanvas3D::select_all()
@ -2347,9 +2382,51 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
// m_canvas->HandleAsNavigationKey(evt); // XXX: Doesn't work in some cases / on Linux
post_event(SimpleEvent(EVT_GLCANVAS_TAB));
}
else if (keyCode == WXK_SHIFT)
{
if (m_picking_enabled && m_rectangle_selection.is_dragging())
{
_update_selection_from_hover();
m_rectangle_selection.stop_dragging();
m_mouse.ignore_left_up = true;
m_dirty = true;
}
// set_cursor(Standard);
}
else if (keyCode == WXK_ALT)
{
if (m_picking_enabled && m_rectangle_selection.is_dragging())
{
_update_selection_from_hover();
m_rectangle_selection.stop_dragging();
m_mouse.ignore_left_up = true;
m_dirty = true;
}
// set_cursor(Standard);
}
else if (keyCode == WXK_CONTROL)
m_dirty = true;
}
else if (evt.GetEventType() == wxEVT_KEY_DOWN) {
m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers();
if (keyCode == WXK_SHIFT)
{
if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports))
{
m_mouse.ignore_left_up = false;
// set_cursor(Cross);
}
}
else if (keyCode == WXK_ALT)
{
if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports))
{
m_mouse.ignore_left_up = false;
// set_cursor(Cross);
}
}
else if (keyCode == WXK_CONTROL)
m_dirty = true;
}
}
}
@ -2468,6 +2545,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.set_start_position_3D_as_invalid();
m_mouse.set_start_position_2D_as_invalid();
m_mouse.dragging = false;
m_mouse.ignore_left_up = false;
m_dirty = true;
if (m_canvas->HasCapture())
@ -2566,7 +2644,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (top_level_wnd && top_level_wnd->IsActive())
m_canvas->SetFocus();
m_mouse.position = pos.cast<double>();
// 1) forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while
// 1) forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
// the context menu is shown, ensuring it to disappear if the mouse is outside any volume and to
// change the volume hover state if any is under the mouse
// 2) when switching between 3d view and preview the size of the canvas changes if the side panels are visible,
@ -2606,26 +2684,35 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true;
}
}
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
{
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)
{
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
m_dirty = true;
}
}
else
{
// Select volume in this 3D canvas.
// Don't deselect a volume if layer editing is enabled. We want the object to stay selected
// during the scene manipulation.
if (m_picking_enabled && ((m_hover_volume_id != -1) || !is_layers_editing_enabled()))
if (m_picking_enabled && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled()))
{
if (evt.LeftDown() && (m_hover_volume_id != -1))
if (evt.LeftDown() && !m_hover_volume_idxs.empty())
{
bool already_selected = m_selection.contains_volume(m_hover_volume_id);
int volume_idx = get_first_hover_volume_idx();
bool already_selected = m_selection.contains_volume(volume_idx);
bool ctrl_down = evt.CmdDown();
Selection::IndicesList curr_idxs = m_selection.get_volume_idxs();
if (already_selected && ctrl_down)
m_selection.remove(m_hover_volume_id);
m_selection.remove(volume_idx);
else
{
m_selection.add(m_hover_volume_id, !ctrl_down, true);
m_selection.add(volume_idx, !ctrl_down, true);
m_mouse.drag.move_requires_threshold = !already_selected;
if (already_selected)
m_mouse.set_move_start_threshold_position_2D_as_invalid();
@ -2633,6 +2720,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_mouse.drag.move_start_threshold_position_2D = pos;
}
// propagate event through callback
if (curr_idxs != m_selection.get_volume_idxs())
{
m_gizmos.refresh_on_off_state(m_selection);
@ -2643,18 +2731,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
}
}
// propagate event through callback
if (m_hover_volume_id != -1)
if (!m_hover_volume_idxs.empty())
{
if (evt.LeftDown() && m_moving_enabled && (m_mouse.drag.move_volume_idx == -1))
{
// Only accept the initial position, if it is inside the volume bounding box.
BoundingBoxf3 volume_bbox = m_volumes.volumes[m_hover_volume_id]->transformed_bounding_box();
int volume_idx = get_first_hover_volume_idx();
BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
volume_bbox.offset(1.0);
if (volume_bbox.contains(m_mouse.scene_position))
{
// The dragging operation is initiated.
m_mouse.drag.move_volume_idx = m_hover_volume_id;
m_mouse.drag.move_volume_idx = volume_idx;
m_selection.start_dragging();
m_mouse.drag.start_position_3D = m_mouse.scene_position;
m_moving = true;
@ -2671,7 +2759,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
Vec3d cur_pos = m_mouse.drag.start_position_3D;
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
if (m_selection.contains_volume(m_hover_volume_id))
if (m_selection.contains_volume(get_first_hover_volume_idx()))
{
if (m_camera.get_theta() == 90.0f)
{
@ -2709,10 +2797,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_regenerate_volumes = false;
m_selection.translate(cur_pos - m_mouse.drag.start_position_3D);
wxGetApp().obj_manipul()->update_settings_value(m_selection);
m_dirty = true;
}
}
else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging())
{
m_rectangle_selection.dragging(pos.cast<double>());
m_dirty = true;
}
else if (evt.Dragging())
{
m_mouse.dragging = true;
@ -2726,7 +2818,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.LeftIsDown())
{
// if dragging over blank area with left button, rotate
if ((m_hover_volume_id == -1) && m_mouse.is_start_position_3D_defined())
if (m_hover_volume_idxs.empty() && m_mouse.is_start_position_3D_defined())
{
const Vec3d& orig = m_mouse.drag.start_position_3D;
float sign = m_camera.inverted_phi ? -1.0f : 1.0f;
@ -2769,7 +2861,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// of the scene with the background processing data should be performed.
post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
}
else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !is_layers_editing_enabled())
else if (evt.LeftUp() && m_picking_enabled && m_rectangle_selection.is_dragging())
{
if (evt.ShiftDown() || evt.AltDown())
_update_selection_from_hover();
m_rectangle_selection.stop_dragging();
}
else if (evt.LeftUp() && !m_mouse.ignore_left_up && !m_mouse.dragging && m_hover_volume_idxs.empty() && !is_layers_editing_enabled())
{
// deselect and propagate event through callback
if (!evt.ShiftDown() && m_picking_enabled)
@ -2788,18 +2887,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.RightUp())
{
m_mouse.position = pos.cast<double>();
// forces a frame render to ensure that m_hover_volume_id is updated even when the user right clicks while
// forces a frame render to ensure that m_hover_volume_idxs is updated even when the user right clicks while
// the context menu is already shown
render();
if (m_hover_volume_id != -1)
if (!m_hover_volume_idxs.empty())
{
// if right clicking on volume, propagate event through callback (shows context menu)
if (m_volumes.volumes[m_hover_volume_id]->hover
&& !m_volumes.volumes[m_hover_volume_id]->is_wipe_tower // no context menu for the wipe tower
int volume_idx = get_first_hover_volume_idx();
if (!m_volumes.volumes[volume_idx]->is_wipe_tower // no context menu for the wipe tower
&& m_gizmos.get_current_type() != GLGizmosManager::SlaSupports) // disable context menu when the gizmo is open
{
// forces the selection of the volume
m_selection.add(m_hover_volume_id);
m_selection.add(volume_idx);
m_gizmos.refresh_on_off_state(m_selection);
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_gizmos.update_data(*this);
@ -3199,6 +3298,20 @@ double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
return factor * m_bed.get_bounding_box().max_size();
}
void GLCanvas3D::set_cursor(ECursorType type)
{
if ((m_canvas != nullptr) && (m_cursor_type != type))
{
switch (type)
{
case Standard: { m_canvas->SetCursor(*wxSTANDARD_CURSOR); break; }
case Cross: { m_canvas->SetCursor(*wxCROSS_CURSOR); break; }
}
m_cursor_type = type;
}
}
void GLCanvas3D::msw_rescale()
{
m_warning_texture.msw_rescale(*this);
@ -3377,7 +3490,7 @@ bool GLCanvas3D::_init_toolbar()
item.name = "layersediting";
#if ENABLE_SVG_ICONS
item.icon_filename = "layers.svg";
item.icon_filename = "layers_white.svg";
#endif // ENABLE_SVG_ICONS
item.tooltip = GUI::L_str("Layers editing");
item.sprite_id = 10;
@ -3569,10 +3682,10 @@ void GLCanvas3D::_refresh_if_shown_on_screen()
void GLCanvas3D::_picking_pass() const
{
const Vec2d& pos = m_mouse.position;
if (m_picking_enabled && !m_mouse.dragging && (pos != Vec2d(DBL_MAX, DBL_MAX)))
if (m_picking_enabled && !m_mouse.dragging && (m_mouse.position != Vec2d(DBL_MAX, DBL_MAX)))
{
m_hover_volume_idxs.clear();
// Render the object for picking.
// FIXME This cannot possibly work in a multi - sampled context as the color gets mangled by the anti - aliasing.
// Better to use software ray - casting on a bounding - box hierarchy.
@ -3603,27 +3716,98 @@ void GLCanvas3D::_picking_pass() const
GLubyte color[4] = { 0, 0, 0, 0 };
const Size& cnv_size = get_canvas_size();
bool inside = (0 <= pos(0)) && (pos(0) < cnv_size.get_width()) && (0 <= pos(1)) && (pos(1) < cnv_size.get_height());
bool inside = (0 <= m_mouse.position(0)) && (m_mouse.position(0) < cnv_size.get_width()) && (0 <= m_mouse.position(1)) && (m_mouse.position(1) < cnv_size.get_height());
if (inside)
{
glsafe(::glReadPixels(pos(0), cnv_size.get_height() - pos(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color));
volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256;
glsafe(::glReadPixels(m_mouse.position(0), cnv_size.get_height() - m_mouse.position(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color));
volume_id = color[0] + (color[1] << 8) + (color[2] << 16);
}
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
{
m_hover_volume_id = volume_id;
m_hover_volume_idxs.push_back(volume_id);
m_gizmos.set_hover_id(-1);
}
else
{
m_hover_volume_id = -1;
m_gizmos.set_hover_id(inside && volume_id <= GLGizmoBase::BASE_ID ? (GLGizmoBase::BASE_ID - volume_id) : -1);
}
_update_volumes_hover_state();
}
}
void GLCanvas3D::_rectangular_selection_picking_pass() const
{
m_gizmos.set_hover_id(-1);
std::set<int> idxs;
if (m_picking_enabled)
{
if (m_multisample_allowed)
glsafe(::glDisable(GL_MULTISAMPLE));
glsafe(::glDisable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
_render_volumes_for_picking();
if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE));
int width = std::max((int)m_rectangle_selection.get_width(), 1);
int height = std::max((int)m_rectangle_selection.get_height(), 1);
int px_count = width * height;
int left = (int)m_rectangle_selection.get_left();
int top = get_canvas_size().get_height() - (int)m_rectangle_selection.get_top();
if ((left >= 0) && (top >= 0))
{
#define USE_PARALLEL 1
#if USE_PARALLEL
struct Pixel
{
std::array<GLubyte, 4> data;
int id() const { return data[0] + (data[1] << 8) + (data[2] << 16); }
};
std::vector<Pixel> frame(px_count);
glsafe(::glReadPixels(left, top, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)frame.data()));
tbb::spin_mutex mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(0, frame.size(), (size_t)width),
[this, &frame, &idxs, &mutex](const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i < range.end(); ++i)
{
int volume_id = frame[i].id();
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
{
mutex.lock();
idxs.insert(volume_id);
mutex.unlock();
}
}
}
);
#else
std::vector<GLubyte> frame(4 * px_count);
glsafe(::glReadPixels(left, top, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)frame.data()));
for (int i = 0; i < px_count; ++i)
{
int px_id = 4 * i;
int volume_id = frame[px_id] + (frame[px_id + 1] << 8) + (frame[px_id + 2] << 16);
if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size()))
idxs.insert(volume_id);
}
#endif // USE_PARALLEL
}
}
m_hover_volume_idxs.assign(idxs.begin(), idxs.end());
_update_volumes_hover_state();
}
void GLCanvas3D::_render_background() const
{
glsafe(::glPushMatrix());
@ -4109,24 +4293,93 @@ void GLCanvas3D::_update_volumes_hover_state() const
{
for (GLVolume* v : m_volumes.volumes)
{
v->hover = false;
v->hover = GLVolume::HS_None;
}
if (m_hover_volume_id == -1)
if (m_hover_volume_idxs.empty())
return;
GLVolume* volume = m_volumes.volumes[m_hover_volume_id];
if (volume->is_modifier)
volume->hover = true;
else
{
int object_idx = volume->object_idx();
int instance_idx = volume->instance_idx();
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL); // additive select/deselect
bool shift_pressed = wxGetKeyState(WXK_SHIFT); // select by rectangle
bool alt_pressed = wxGetKeyState(WXK_ALT); // deselect by rectangle
for (GLVolume* v : m_volumes.volumes)
if (alt_pressed && (shift_pressed || ctrl_pressed))
{
// illegal combinations of keys
m_hover_volume_idxs.clear();
return;
}
bool selection_modifiers_only = m_selection.is_empty() || m_selection.is_any_modifier();
bool hover_modifiers_only = true;
for (int i : m_hover_volume_idxs)
{
if (!m_volumes.volumes[i]->is_modifier)
{
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
v->hover = true;
hover_modifiers_only = false;
break;
}
}
std::set<std::pair<int, int>> hover_instances;
for (int i : m_hover_volume_idxs)
{
const GLVolume& v = *m_volumes.volumes[i];
hover_instances.insert(std::make_pair(v.object_idx(), v.instance_idx()));
}
bool hover_from_single_instance = hover_instances.size() == 1;
if (hover_modifiers_only && !hover_from_single_instance)
{
// do not allow to select volumes from different instances
m_hover_volume_idxs.clear();
return;
}
for (int i : m_hover_volume_idxs)
{
GLVolume& volume = *m_volumes.volumes[i];
if (volume.hover != GLVolume::HS_None)
continue;
bool deselect = volume.selected && ((ctrl_pressed && !shift_pressed) || alt_pressed);
// (volume->is_modifier && !selection_modifiers_only && !is_ctrl_pressed) -> allows hovering on selected modifiers belonging to selection of type Instance
bool select = (!volume.selected || (volume.is_modifier && !selection_modifiers_only && !ctrl_pressed)) && !alt_pressed;
if (select || deselect)
{
bool as_volume =
volume.is_modifier && hover_from_single_instance && !ctrl_pressed &&
(
(!deselect) ||
(deselect && !m_selection.is_single_full_instance() && (volume.object_idx() == m_selection.get_object_idx()) && (volume.instance_idx() == m_selection.get_instance_idx()))
);
if (as_volume)
{
if (deselect)
volume.hover = GLVolume::HS_Deselect;
else
volume.hover = GLVolume::HS_Select;
}
else
{
int object_idx = volume.object_idx();
int instance_idx = volume.instance_idx();
for (GLVolume* v : m_volumes.volumes)
{
if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx))
{
if (deselect)
v->hover = GLVolume::HS_Deselect;
else
v->hover = GLVolume::HS_Select;
}
}
}
}
}
}
@ -5405,6 +5658,55 @@ void GLCanvas3D::_resize_toolbars() const
}
#endif // !ENABLE_SVG_ICONS
void GLCanvas3D::_update_selection_from_hover()
{
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
if (m_hover_volume_idxs.empty())
{
if (!ctrl_pressed && (m_rectangle_selection.get_state() == GLSelectionRectangle::Select))
m_selection.clear();
return;
}
GLSelectionRectangle::EState state = m_rectangle_selection.get_state();
bool hover_modifiers_only = true;
for (int i : m_hover_volume_idxs)
{
if (!m_volumes.volumes[i]->is_modifier)
{
hover_modifiers_only = false;
break;
}
}
if ((state == GLSelectionRectangle::Select) && !ctrl_pressed)
m_selection.clear();
for (int i : m_hover_volume_idxs)
{
if (state == GLSelectionRectangle::Select)
{
if (hover_modifiers_only)
{
const GLVolume& v = *m_volumes.volumes[i];
m_selection.add_volume(v.object_idx(), v.volume_idx(), v.instance_idx(), false);
}
else
m_selection.add(i, false);
}
else
m_selection.remove(i);
}
m_gizmos.refresh_on_off_state(m_selection);
m_gizmos.update_data(*this);
post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT));
m_dirty = true;
}
const Print* GLCanvas3D::fff_print() const
{
return (m_process == nullptr) ? nullptr : m_process->fff_print();

View File

@ -303,6 +303,7 @@ class GLCanvas3D
Vec2d position;
Vec3d scene_position;
Drag drag;
bool ignore_left_up;
Mouse();
@ -319,7 +320,6 @@ class GLCanvas3D
}
};
private:
struct SlaCap
{
struct Triangles
@ -399,6 +399,23 @@ private:
void render(const GLCanvas3D& canvas) const;
};
#if ENABLE_RENDER_STATISTICS
struct RenderStats
{
long long last_frame;
RenderStats() : last_frame(0) {}
};
#endif // ENABLE_RENDER_STATISTICS
public:
enum ECursorType : unsigned char
{
Standard,
Cross
};
private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
#if ENABLE_RETINA_GL
@ -433,7 +450,7 @@ private:
bool m_initialized;
bool m_use_VBOs;
bool m_apply_zoom_to_volumes_filter;
mutable int m_hover_volume_id;
mutable std::vector<int> m_hover_volume_idxs;
bool m_warning_texture_enabled;
bool m_legend_texture_enabled;
bool m_picking_enabled;
@ -443,6 +460,8 @@ private:
bool m_regenerate_volumes;
bool m_moving;
bool m_tab_down;
ECursorType m_cursor_type;
GLSelectionRectangle m_rectangle_selection;
// Following variable is obsolete and it should be safe to remove it.
// I just don't want to do it now before a release (Lukas Matena 24.3.2019)
@ -454,6 +473,10 @@ private:
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
#if ENABLE_RENDER_STATISTICS
RenderStats m_render_stats;
#endif // ENABLE_RENDER_STATISTICS
public:
GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar);
~GLCanvas3D();
@ -582,7 +605,7 @@ public:
float get_view_toolbar_height() const { return m_view_toolbar.get_height(); }
int get_move_volume_id() const { return m_mouse.drag.move_volume_idx; }
int get_hover_volume_id() const { return m_hover_volume_id; }
int get_first_hover_volume_idx() const { return m_hover_volume_idxs.empty() ? -1 : m_hover_volume_idxs.front(); }
// Returns the view ray line, in world coordinate, at the given mouse position.
Linef3 mouse_ray(const Point& mouse_pos);
@ -594,6 +617,7 @@ public:
double get_size_proportional_to_max_bed_size(double factor) const;
void set_cursor(ECursorType type);
void msw_rescale();
private:
@ -612,6 +636,7 @@ private:
void _refresh_if_shown_on_screen();
void _picking_pass() const;
void _rectangular_selection_picking_pass() const;
void _render_background() const;
void _render_bed(float theta) const;
void _render_axes() const;
@ -690,6 +715,9 @@ private:
void _resize_toolbars() const;
#endif // !ENABLE_SVG_ICONS
// updates the selection from the content of m_hover_volume_idxs
void _update_selection_from_hover();
static std::vector<float> _parse_colors(const std::vector<std::string>& colors);
public:

View File

@ -0,0 +1,117 @@
#include "GLSelectionRectangle.hpp"
#include "Camera.hpp"
#include "3DScene.hpp"
#include "GLCanvas3D.hpp"
#include <GL/glew.h>
namespace Slic3r {
namespace GUI {
void GLSelectionRectangle::start_dragging(const Vec2d& mouse_position, EState state)
{
if (is_dragging() || (state == Off))
return;
m_state = state;
m_start_corner = mouse_position;
m_end_corner = mouse_position;
}
void GLSelectionRectangle::dragging(const Vec2d& mouse_position)
{
if (!is_dragging())
return;
m_end_corner = mouse_position;
}
std::vector<unsigned int> GLSelectionRectangle::stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points)
{
std::vector<unsigned int> out;
if (!is_dragging())
return out;
m_state = Off;
const Camera& camera = canvas.get_camera();
const std::array<int, 4>& viewport = camera.get_viewport();
const Transform3d& modelview_matrix = camera.get_view_matrix();
const Transform3d& projection_matrix = camera.get_projection_matrix();
// bounding box created from the rectangle corners - will take care of order of the corners
BoundingBox rectangle(Points{ Point(m_start_corner.cast<int>()), Point(m_end_corner.cast<int>()) });
// Iterate over all points and determine whether they're in the rectangle.
for (unsigned int i = 0; i<points.size(); ++i) {
const Vec3d& point = points[i];
GLdouble out_x, out_y, out_z;
::gluProject((GLdouble)point(0), (GLdouble)point(1), (GLdouble)point(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
out_y = canvas.get_canvas_size().get_height() - out_y;
if (rectangle.contains(Point(out_x, out_y)))
out.push_back(i);
}
return out;
}
void GLSelectionRectangle::stop_dragging()
{
if (is_dragging())
m_state = Off;
}
void GLSelectionRectangle::render(const GLCanvas3D& canvas) const
{
if (!is_dragging())
return;
float zoom = canvas.get_camera().zoom;
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
Size cnv_size = canvas.get_canvas_size();
float cnv_half_width = 0.5f * (float)cnv_size.get_width();
float cnv_half_height = 0.5f * (float)cnv_size.get_height();
if ((cnv_half_width == 0.0f) || (cnv_half_height == 0.0f))
return;
Vec2d start(m_start_corner(0) - cnv_half_width, cnv_half_height - m_start_corner(1));
Vec2d end(m_end_corner(0) - cnv_half_width, cnv_half_height - m_end_corner(1));
float left = (float)std::min(start(0), end(0)) * inv_zoom;
float top = (float)std::max(start(1), end(1)) * inv_zoom;
float right = (float)std::max(start(0), end(0)) * inv_zoom;
float bottom = (float)std::min(start(1), end(1)) * inv_zoom;
glsafe(::glLineWidth(1.5f));
float color[3];
color[0] = (m_state == Select) ? 0.3f : 1.0f;
color[1] = (m_state == Select) ? 1.0f : 0.3f;
color[2] = 0.3f;
glsafe(::glColor3fv(color));
glsafe(::glDisable(GL_DEPTH_TEST));
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
glsafe(::glPushAttrib(GL_ENABLE_BIT));
glsafe(::glLineStipple(4, 0xAAAA));
glsafe(::glEnable(GL_LINE_STIPPLE));
::glBegin(GL_LINE_LOOP);
::glVertex2f((GLfloat)left, (GLfloat)bottom);
::glVertex2f((GLfloat)right, (GLfloat)bottom);
::glVertex2f((GLfloat)right, (GLfloat)top);
::glVertex2f((GLfloat)left, (GLfloat)top);
glsafe(::glEnd());
glsafe(::glPopAttrib());
glsafe(::glPopMatrix());
}
} // namespace GUI
} // namespace Slic3r

View File

@ -0,0 +1,56 @@
#ifndef slic3r_GLSelectionRectangle_hpp_
#define slic3r_GLSelectionRectangle_hpp_
#include "libslic3r/Point.hpp"
namespace Slic3r {
namespace GUI {
struct Camera;
class GLCanvas3D;
class GLSelectionRectangle {
public:
enum EState {
Off,
Select,
Deselect
};
// Initiates the rectangle.
void start_dragging(const Vec2d& mouse_position, EState state);
// To be called on mouse move.
void dragging(const Vec2d& mouse_position);
// Given a vector of points in world coordinates, the function returns indices of those
// that are in the rectangle. It then disables the rectangle.
std::vector<unsigned int> stop_dragging(const GLCanvas3D& canvas, const std::vector<Vec3d>& points);
// Disables the rectangle.
void stop_dragging();
void render(const GLCanvas3D& canvas) const;
bool is_dragging() const { return m_state != Off; }
EState get_state() const { return m_state; }
float get_width() const { return std::abs(m_start_corner(0) - m_end_corner(0)); }
float get_height() const { return std::abs(m_start_corner(1) - m_end_corner(1)); }
float get_left() const { return std::min(m_start_corner(0), m_end_corner(0)); }
float get_right() const { return std::max(m_start_corner(0), m_end_corner(0)); }
float get_top() const { return std::max(m_start_corner(1), m_end_corner(1)); }
float get_bottom() const { return std::min(m_start_corner(1), m_end_corner(1)); }
private:
EState m_state = Off;
Vec2d m_start_corner;
Vec2d m_end_corner;
};
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_GLGizmoSlaSupports_hpp_

View File

@ -44,6 +44,10 @@
#include "SysInfoDialog.hpp"
#include "KBShortcutsDialog.hpp"
#ifdef __WXMSW__
#include <Shlobj.h>
#endif // __WXMSW__
namespace Slic3r {
namespace GUI {
@ -182,6 +186,10 @@ bool GUI_App::on_init_inner()
app_config->set("version", SLIC3R_VERSION);
app_config->save();
#ifdef __WXMSW__
associate_3mf_files();
#endif // __WXMSW__
preset_updater = new PresetUpdater();
Bind(EVT_SLIC3R_VERSION_ONLINE, [this](const wxCommandEvent &evt) {
app_config->set("version_online", into_u8(evt.GetString()));
@ -303,13 +311,13 @@ bool GUI_App::dark_mode_menus()
void GUI_App::init_label_colours()
{
if (dark_mode()) {
m_color_label_modified = wxColour(252, 77, 1);
m_color_label_sys = wxColour(26, 132, 57);
}
else {
m_color_label_modified = wxColour(253, 111, 40);
m_color_label_sys = wxColour(115, 220, 103);
}
else {
m_color_label_modified = wxColour(252, 77, 1);
m_color_label_sys = wxColour(26, 132, 57);
}
m_color_label_default = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
}
@ -340,15 +348,19 @@ void GUI_App::init_fonts()
#endif /*__WXMAC__*/
}
void GUI_App::update_fonts()
void GUI_App::update_fonts(const MainFrame *main_frame)
{
/* Only normal and bold fonts are used for an application rescale,
* because of under MSW small and normal fonts are the same.
* To avoid same rescaling twice, just fill this values
* from rescaled MainFrame
*/
m_normal_font = mainframe->normal_font();
m_bold_font = mainframe->normal_font().Bold();
if (main_frame == nullptr)
main_frame = this->mainframe;
m_normal_font = main_frame->normal_font();
m_small_font = m_normal_font;
m_bold_font = main_frame->normal_font().Bold();
m_em_unit = main_frame->em_unit();
}
void GUI_App::set_label_clr_modified(const wxColour& clr) {
@ -773,14 +785,14 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
// to notify the user whether he is aware that some preset changes will be lost.
bool GUI_App::check_unsaved_changes()
{
std::string dirty;
wxString dirty;
PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology();
for (Tab *tab : tabs_list)
if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty())
if (dirty.empty())
dirty = tab->name();
dirty = _(tab->name());
else
dirty += std::string(", ") + tab->name();
dirty += wxString(", ") + _(tab->name());
if (dirty.empty())
// No changes, the application may close or reload presets.
return true;
@ -947,5 +959,64 @@ void GUI_App::window_pos_sanitize(wxTopLevelWindow* window)
// }
#ifdef __WXMSW__
void GUI_App::associate_3mf_files()
{
// see as reference: https://stackoverflow.com/questions/20245262/c-program-needs-an-file-association
auto reg_set = [](HKEY hkeyHive, const wchar_t* pszVar, const wchar_t* pszValue)
{
wchar_t szValueCurrent[1000];
DWORD dwType;
DWORD dwSize = sizeof(szValueCurrent);
int iRC = ::RegGetValueW(hkeyHive, pszVar, nullptr, RRF_RT_ANY, &dwType, szValueCurrent, &dwSize);
bool bDidntExist = iRC == ERROR_FILE_NOT_FOUND;
if ((iRC != ERROR_SUCCESS) && !bDidntExist)
// an error occurred
return;
if (!bDidntExist)
{
if (dwType != REG_SZ)
// invalid type
return;
if (::wcscmp(szValueCurrent, pszValue) == 0)
// value already set
return;
}
DWORD dwDisposition;
HKEY hkey;
iRC = ::RegCreateKeyExW(hkeyHive, pszVar, 0, 0, 0, KEY_ALL_ACCESS, nullptr, &hkey, &dwDisposition);
if (iRC == ERROR_SUCCESS)
iRC = ::RegSetValueExW(hkey, L"", 0, REG_SZ, (BYTE*)pszValue, (::wcslen(pszValue) + 1) * sizeof(wchar_t));
RegCloseKey(hkey);
};
wchar_t app_path[MAX_PATH];
::GetModuleFileNameW(nullptr, app_path, sizeof(app_path));
std::wstring prog_path = L"\"" + std::wstring(app_path) + L"\"";
std::wstring prog_id = L"Prusa.Slicer.1";
std::wstring prog_desc = L"PrusaSlicer";
std::wstring prog_command = prog_path + L" \"%1\"";
std::wstring reg_base = L"Software\\Classes";
std::wstring reg_extension = reg_base + L"\\.3mf";
std::wstring reg_prog_id = reg_base + L"\\" + prog_id;
std::wstring reg_prog_id_command = reg_prog_id + L"\\Shell\\Open\\Command";
reg_set(HKEY_CURRENT_USER, reg_extension.c_str(), prog_id.c_str());
reg_set(HKEY_CURRENT_USER, reg_prog_id.c_str(), prog_desc.c_str());
reg_set(HKEY_CURRENT_USER, reg_prog_id_command.c_str(), prog_command.c_str());
::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr);
}
#endif // __WXMSW__
} // GUI
} //Slic3r

View File

@ -100,7 +100,7 @@ public:
void init_label_colours();
void update_label_colours_from_appconfig();
void init_fonts();
void update_fonts();
void update_fonts(const MainFrame *main_frame = nullptr);
void set_label_clr_modified(const wxColour& clr);
void set_label_clr_sys(const wxColour& clr);
@ -178,6 +178,9 @@ private:
void window_pos_save(wxTopLevelWindow* window, const std::string &name);
void window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
void window_pos_sanitize(wxTopLevelWindow* window);
#ifdef __WXMSW__
void associate_3mf_files();
#endif // __WXMSW__
};
DECLARE_APP(GUI_App)

View File

@ -193,6 +193,76 @@ void ObjectList::create_popup_menus()
create_instance_popupmenu(&m_menu_instance);
}
void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(0)*/)
{
const wxDataViewItem item = input_item == wxDataViewItem(0) ? GetSelection() : input_item;
if (!item)
{
obj_idx = vol_idx = -1;
return;
}
const ItemType type = m_objects_model->GetItemType(item);
obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
type & itVolume ? m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)) : -1;
vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
}
int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= -1*/) const
{
if (obj_idx < 0)
return 0;
return (*m_objects)[obj_idx]->get_mesh_errors_count(vol_idx);
}
wxString ObjectList::get_mesh_errors_list(const int obj_idx, const int vol_idx /*= -1*/) const
{
const int errors = get_mesh_errors_count(obj_idx, vol_idx);
if (errors == 0)
return ""; // hide tooltip
// Create tooltip string, if there are errors
wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors);
const stl_stats& stats = vol_idx == -1 ?
(*m_objects)[obj_idx]->get_object_stl_stats() :
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh.stl.stats;
std::map<std::string, int> error_msg = {
{ L("degenerate facets"), stats.degenerate_facets },
{ L("edges fixed"), stats.edges_fixed },
{ L("facets removed"), stats.facets_removed },
{ L("facets added"), stats.facets_added },
{ L("facets reversed"), stats.facets_reversed },
{ L("backwards edges"), stats.backwards_edges }
};
for (const auto& error : error_msg)
if (error.second > 0)
tooltip += wxString::Format("\t%d %s\n", error.second, _(error.first));
if (is_windows10())
tooltip += _(L("Right button click the icon to fix STL through Netfabb"));
return tooltip;
}
wxString ObjectList::get_mesh_errors_list()
{
if (!GetSelection())
return "";
int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx);
return get_mesh_errors_list(obj_idx, vol_idx);
}
void ObjectList::set_tooltip_for_item(const wxPoint& pt)
{
wxDataViewItem item;
@ -200,40 +270,24 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt)
HitTest(pt, item, col);
if (!item) return;
/* GetMainWindow() return window, associated with wxDataViewCtrl.
* And for this window we should to set tooltips.
* Just this->SetToolTip(tooltip) => has no effect.
*/
if (col->GetTitle() == " " && GetSelectedItemsCount()<2)
GetMainWindow()->SetToolTip(_(L("Right button click the icon to change the object settings")));
else if (col->GetTitle() == _("Name") &&
m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.bmp().GetRefData()) {
int obj_idx = m_objects_model->GetIdByItem(item);
auto& stats = (*m_objects)[obj_idx]->volumes[0]->mesh.stl.stats;
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
wxString tooltip = wxString::Format(_(L("Auto-repaired (%d errors):\n")), errors);
std::map<std::string, int> error_msg;
error_msg[L("degenerate facets")] = stats.degenerate_facets;
error_msg[L("edges fixed")] = stats.edges_fixed;
error_msg[L("facets removed")] = stats.facets_removed;
error_msg[L("facets added")] = stats.facets_added;
error_msg[L("facets reversed")] = stats.facets_reversed;
error_msg[L("backwards edges")] = stats.backwards_edges;
for (auto error : error_msg)
{
if (error.second > 0)
tooltip += wxString::Format(_("\t%d %s\n"), error.second, error.first);
else if (col->GetTitle() == _("Name"))
{
#ifdef __WXMSW__
if (pt.x < 2 * wxGetApp().em_unit() || pt.x > 4 * wxGetApp().em_unit()) {
GetMainWindow()->SetToolTip(""); // hide tooltip
return;
}
// OR
// tooltip += wxString::Format(_(L("%d degenerate facets, %d edges fixed, %d facets removed, "
// "%d facets added, %d facets reversed, %d backwards edges")),
// stats.degenerate_facets, stats.edges_fixed, stats.facets_removed,
// stats.facets_added, stats.facets_reversed, stats.backwards_edges);
if (is_windows10())
tooltip += _(L("Right button click the icon to fix STL through Netfabb"));
GetMainWindow()->SetToolTip(tooltip);
#endif //__WXMSW__
int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx, item);
GetMainWindow()->SetToolTip(get_mesh_errors_list(obj_idx, vol_idx));
}
else
GetMainWindow()->SetToolTip(""); // hide tooltip
@ -395,8 +449,8 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
void ObjectList::init_icons()
{
m_bmp_modifiermesh = ScalableBitmap(nullptr, "add_modifier"); // Add part
m_bmp_solidmesh = ScalableBitmap(nullptr, "add_part"); // Add modifier
m_bmp_solidmesh = ScalableBitmap(nullptr, "add_part"); // Add part
m_bmp_modifiermesh = ScalableBitmap(nullptr, "add_modifier"); // Add modifier
m_bmp_support_enforcer = ScalableBitmap(nullptr, "support_enforcer");// Add support enforcer
m_bmp_support_blocker = ScalableBitmap(nullptr, "support_blocker"); // Add support blocker
@ -412,6 +466,8 @@ void ObjectList::init_icons()
// init icon for manifold warning
m_bmp_manifold_warning = ScalableBitmap(nullptr, "exclamation");
// Set warning bitmap for the model
m_objects_model->SetWarningBitmap(&m_bmp_manifold_warning.bmp());
// init bitmap for "Split to sub-objects" context menu
m_bmp_split = ScalableBitmap(nullptr, "split_parts_SMALL");
@ -425,8 +481,8 @@ void ObjectList::rescale_icons()
m_bmp_vector.clear();
m_bmp_vector.reserve(4); // bitmaps for different types of parts
for (ScalableBitmap* bitmap : std::vector<ScalableBitmap*> {
&m_bmp_modifiermesh, // Add part
&m_bmp_solidmesh, // Add modifier
&m_bmp_solidmesh, // Add part
&m_bmp_modifiermesh, // Add modifier
&m_bmp_support_enforcer, // Add support enforcer
&m_bmp_support_blocker }) // Add support blocker
{
@ -437,6 +493,9 @@ void ObjectList::rescale_icons()
m_objects_model->SetVolumeBitmaps(m_bmp_vector);
m_bmp_manifold_warning.msw_rescale();
// Set warning bitmap for the model
m_objects_model->SetWarningBitmap(&m_bmp_manifold_warning.bmp());
m_bmp_split.msw_rescale();
m_bmp_cog.msw_rescale();
@ -498,7 +557,8 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol
for (const ModelVolume* volume : volumes)
{
auto vol_item = m_objects_model->AddVolumeChild(object_item, volume->name, volume->type(),
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, volume->name, volume->type(),
volume->get_mesh_errors_count()>0 ,
volume->config.has("extruder") ? volume->config.option<ConfigOptionInt>("extruder")->value : 0);
auto opt_keys = volume->config.keys();
if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder")))
@ -574,10 +634,13 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
if (title == " ")
show_context_menu();
else if (title == _("Name") && pt.x >15 &&
m_objects_model->GetBitmap(item).GetRefData() == m_bmp_manifold_warning.bmp().GetRefData())
else if (title == _("Name"))
{
if (is_windows10())
int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx, item);
if (is_windows10() && get_mesh_errors_count(obj_idx, vol_idx) > 0 &&
pt.x > 2*wxGetApp().em_unit() && pt.x < 4*wxGetApp().em_unit() )
fix_through_netfabb();
}
@ -937,6 +1000,7 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
{
const std::vector<std::string>& options = get_options_for_bundle(bundle_name);
assert(m_config);
auto opt_keys = m_config->keys();
const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
@ -966,6 +1030,10 @@ void ObjectList::update_settings_item()
const auto settings_item = m_objects_model->IsSettingsItem(item) ? item : m_objects_model->GetSettingsItem(item);
select_item(settings_item ? settings_item :
m_objects_model->AddSettingsChild(item));
// update object selection on Plater
if (!m_prevent_canvas_selection_update)
update_selections_on_canvas();
}
else {
auto panel = wxGetApp().sidebar().scrolled_panel();
@ -1135,19 +1203,21 @@ void ObjectList::append_menu_items_osx(wxMenu* menu)
menu->AppendSeparator();
}
void ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu)
wxMenuItem* ObjectList::append_menu_item_fix_through_netfabb(wxMenu* menu)
{
if (!is_windows10())
return;
append_menu_item(menu, wxID_ANY, _(L("Fix through the Netfabb")), "",
return nullptr;
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _(L("Fix through the Netfabb")), "",
[this](wxCommandEvent&) { fix_through_netfabb(); }, "", menu);
menu->AppendSeparator();
return menu_item;
}
void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
{
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, "",
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(true); }, "", menu);
[](wxCommandEvent&) { wxGetApp().plater()->export_stl(false, true); }, "", menu);
menu->AppendSeparator();
}
@ -1327,21 +1397,23 @@ void ObjectList::load_subobject(ModelVolumeType type)
int obj_idx = m_objects_model->GetIdByItem(item);
if (obj_idx < 0) return;
wxArrayString part_names;
load_part((*m_objects)[obj_idx], part_names, type);
std::vector<std::pair<wxString, bool>> volumes_info;
load_part((*m_objects)[obj_idx], volumes_info, type);
changed_object(obj_idx);
for (int i = 0; i < part_names.size(); ++i) {
const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), type);
if (i == part_names.size() - 1)
select_item(sel_item);
}
wxDataViewItem sel_item;
for (const auto& volume : volumes_info )
sel_item = m_objects_model->AddVolumeChild(item, volume.first, type, volume.second);
if (sel_item)
select_item(sel_item);
}
void ObjectList::load_part( ModelObject* model_object,
wxArrayString& part_names,
std::vector<std::pair<wxString, bool>> &volumes_info,
ModelVolumeType type)
{
wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
@ -1377,7 +1449,7 @@ void ObjectList::load_part( ModelObject* model_object,
new_volume->set_type(type);
new_volume->name = boost::filesystem::path(input_file).filename().string();
part_names.Add(from_u8(new_volume->name));
volumes_info.push_back(std::make_pair(from_u8(new_volume->name), new_volume->get_mesh_errors_count()>0));
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
@ -1533,7 +1605,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
changed_object(obj_idx);
const auto object_item = m_objects_model->GetTopParent(GetSelection());
select_item(m_objects_model->AddVolumeChild(object_item, name, type));
select_item(m_objects_model->AddVolumeChild(object_item, name, type,
new_volume->get_mesh_errors_count()>0));
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed();
#endif //no __WXOSX__ //__WXMSW__
@ -1565,6 +1638,10 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
else if (!del_subobject_from_object(obj_idx, idx, type))
return;
// If last volume item with warning was deleted, unmark object item
if (type == itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
m_objects_model->Delete(item);
}
@ -1671,18 +1748,18 @@ void ObjectList::split()
else
parent = item;
for (auto id = 0; id < model_object->volumes.size(); id++) {
const auto vol_item = m_objects_model->AddVolumeChild(parent, from_u8(model_object->volumes[id]->name),
model_object->volumes[id]->is_modifier() ?
ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
model_object->volumes[id]->config.has("extruder") ?
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
for (const ModelVolume* volume : model_object->volumes) {
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(parent, from_u8(volume->name),
volume->is_modifier() ? ModelVolumeType::PARAMETER_MODIFIER : ModelVolumeType::MODEL_PART,
volume->get_mesh_errors_count()>0,
volume->config.has("extruder") ?
volume->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
// add settings to the part, if it has those
auto opt_keys = model_object->volumes[id]->config.keys();
auto opt_keys = volume->config.keys();
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
select_item(m_objects_model->AddSettingsChild(vol_item));
/*Collapse*/Expand(vol_item);
Expand(vol_item);
}
}
@ -1755,6 +1832,7 @@ void ObjectList::changed_object(const int obj_idx/* = -1*/) const
void ObjectList::part_selection_changed()
{
int obj_idx = -1;
int volume_id = -1;
m_config = nullptr;
wxString og_name = wxEmptyString;
@ -1801,7 +1879,7 @@ void ObjectList::part_selection_changed()
}
else if (m_objects_model->GetItemType(item) == itVolume) {
og_name = _(L("Part manipulation"));
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
volume_id = m_objects_model->GetVolumeIdByItem(item);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
update_and_show_manipulations = true;
}
@ -1821,7 +1899,11 @@ void ObjectList::part_selection_changed()
if (update_and_show_manipulations) {
wxGetApp().obj_manipul()->get_og()->set_name(" " + og_name + " ");
wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(GetSelection()));
if (item) {
wxGetApp().obj_manipul()->get_og()->set_value("object_name", m_objects_model->GetName(item));
wxGetApp().obj_manipul()->update_warning_icon_state(get_mesh_errors_list(obj_idx, volume_id));
}
}
if (update_and_show_settings)
@ -1841,34 +1923,26 @@ void ObjectList::part_selection_changed()
void ObjectList::add_object_to_list(size_t obj_idx)
{
auto model_object = (*m_objects)[obj_idx];
wxString item_name = from_u8(model_object->name);
const wxString& item_name = from_u8(model_object->name);
const auto item = m_objects_model->Add(item_name,
!model_object->config.has("extruder") ? 0 :
model_object->config.option<ConfigOptionInt>("extruder")->value);
// Add error icon if detected auto-repaire
auto stats = model_object->volumes[0]->mesh.stl.stats;
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
if (errors > 0) {
wxVariant variant;
variant << DataViewBitmapText(item_name, m_bmp_manifold_warning.bmp());
m_objects_model->SetValue(variant, item, 0);
}
model_object->config.option<ConfigOptionInt>("extruder")->value,
get_mesh_errors_count(obj_idx) > 0);
// add volumes to the object
if (model_object->volumes.size() > 1) {
for (auto id = 0; id < model_object->volumes.size(); id++) {
auto vol_item = m_objects_model->AddVolumeChild(item,
from_u8(model_object->volumes[id]->name),
model_object->volumes[id]->type(),
!model_object->volumes[id]->config.has("extruder") ? 0 :
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
for (const ModelVolume* volume : model_object->volumes) {
const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(item,
from_u8(volume->name),
volume->type(),
volume->get_mesh_errors_count()>0,
!volume->config.has("extruder") ? 0 :
volume->config.option<ConfigOptionInt>("extruder")->value,
false);
auto opt_keys = model_object->volumes[id]->config.keys();
auto opt_keys = volume->config.keys();
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
select_item(m_objects_model->AddSettingsChild(vol_item));
/*Collapse*/Expand(vol_item);
Expand(vol_item);
}
}
Expand(item);
@ -1882,7 +1956,7 @@ void ObjectList::add_object_to_list(size_t obj_idx)
auto opt_keys = model_object->config.keys();
if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) {
select_item(m_objects_model->AddSettingsChild(item));
/*Collapse*/Expand(item);
Expand(item);
}
#ifndef __WXOSX__
@ -2076,6 +2150,11 @@ void ObjectList::update_selections()
if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx())
return;
}
// but if there is selected only one of several instances by context menu,
// then select this instance in ObjectList
if (selection.is_single_full_instance())
sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), selection.get_instance_idx()));
}
else if (selection.is_single_full_object() || selection.is_multiple_full_object())
{
@ -2666,18 +2745,10 @@ void ObjectList::rename_item()
update_name_in_model(item);
}
void ObjectList::fix_through_netfabb() const
void ObjectList::fix_through_netfabb()
{
const wxDataViewItem item = GetSelection();
if (!item)
return;
const ItemType type = m_objects_model->GetItemType(item);
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
type & itVolume ? m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)) : -1;
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
int obj_idx, vol_idx;
get_selected_item_indexes(obj_idx, vol_idx);
wxGetApp().plater()->fix_through_netfabb(obj_idx, vol_idx);
@ -2691,28 +2762,27 @@ void ObjectList::update_item_error_icon(const int obj_idx, const int vol_idx) co
if (!item)
return;
auto model_object = (*m_objects)[obj_idx];
const stl_stats& stats = model_object->volumes[vol_idx<0 ? 0 : vol_idx]->mesh.stl.stats;
const int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
if (errors == 0) {
// delete Error_icon if all errors are fixed
wxVariant variant;
variant << DataViewBitmapText(from_u8(model_object->name), wxNullBitmap);
m_objects_model->SetValue(variant, item, 0);
if (get_mesh_errors_count(obj_idx, vol_idx) == 0)
{
// if whole object has no errors more,
if (get_mesh_errors_count(obj_idx) == 0)
// unmark all items in the object
m_objects_model->DeleteWarningIcon(vol_idx >= 0 ? m_objects_model->GetParent(item) : item, true);
else
// unmark fixed item only
m_objects_model->DeleteWarningIcon(item);
}
}
void ObjectList::msw_rescale()
{
const int em = wxGetApp().em_unit();
// update min size !!! A width of control shouldn't be a wxDefaultCoord
SetMinSize(wxSize(1, 15 * wxGetApp().em_unit()));
SetMinSize(wxSize(1, 15 * em));
GetColumn(0)->SetWidth(19 * wxGetApp().em_unit());
GetColumn(1)->SetWidth(8 * wxGetApp().em_unit());
GetColumn(2)->SetWidth(int(2 * wxGetApp().em_unit()));
GetColumn(0)->SetWidth(19 * em);
GetColumn(1)->SetWidth( 8 * em);
GetColumn(2)->SetWidth( 2 * em);
// rescale all icons, used by ObjectList
rescale_icons();

View File

@ -175,6 +175,16 @@ public:
void init_icons();
void rescale_icons();
// Get obj_idx and vol_idx values for the selected (by default) or an adjusted item
void get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& item = wxDataViewItem(0));
// Get count of errors in the mesh
int get_mesh_errors_count(const int obj_idx, const int vol_idx = -1) const;
/* Get list of errors in the mesh. Return value is a string, used for the tooltip
* Function without parameters is for a call from Manipulation panel,
* when we don't know parameters of selected item
*/
wxString get_mesh_errors_list(const int obj_idx, const int vol_idx = -1) const;
wxString get_mesh_errors_list();
void set_tooltip_for_item(const wxPoint& pt);
void selection_changed();
@ -192,7 +202,7 @@ public:
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
void append_menu_items_osx(wxMenu* menu);
void append_menu_item_fix_through_netfabb(wxMenu* menu);
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
void append_menu_item_export_stl(wxMenu* menu) const ;
void append_menu_item_change_extruder(wxMenu* menu) const;
void append_menu_item_delete(wxMenu* menu);
@ -206,7 +216,7 @@ public:
void update_opt_keys(t_config_option_keys& t_optopt_keys);
void load_subobject(ModelVolumeType type);
void load_part(ModelObject* model_object, wxArrayString& part_names, ModelVolumeType type);
void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void del_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item);
@ -280,7 +290,7 @@ public:
void instances_to_separated_objects(const int obj_idx);
void split_instances();
void rename_item();
void fix_through_netfabb() const;
void fix_through_netfabb();
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);

View File

@ -10,6 +10,7 @@
#include "Selection.hpp"
#include <boost/algorithm/string.hpp>
#include "slic3r/Utils/FixModelByWin10.hpp"
namespace Slic3r
{
@ -22,6 +23,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
, m_focused_option("")
#endif // __APPLE__
{
m_manifold_warning_bmp = ScalableBitmap(parent, "exclamation");
m_og->set_name(_(L("Object Manipulation")));
m_og->label_width = 12;//125;
m_og->set_grid_vgap(5);
@ -42,17 +44,50 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) :
ConfigOptionDef def;
// Objects(sub-objects) name
def.label = L("Name");
// def.label = L("Name");
// def.gui_type = "legend";
// def.tooltip = L("Object name");
// def.width = 21 * wxGetApp().em_unit();
// def.default_value = new ConfigOptionString{ " " };
// m_og->append_single_option_line(Option(def, "object_name"));
Line line = Line{ "Name", "Object name" };
auto manifold_warning_icon = [this](wxWindow* parent) {
m_fix_throught_netfab_bitmap = new wxStaticBitmap(parent, wxID_ANY, wxNullBitmap);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(m_fix_throught_netfab_bitmap);
if (is_windows10())
m_fix_throught_netfab_bitmap->Bind(wxEVT_CONTEXT_MENU, [this](wxCommandEvent &e)
{
// if object/sub-object has no errors
if (m_fix_throught_netfab_bitmap->GetBitmap().GetRefData() == wxNullBitmap.GetRefData())
return;
wxGetApp().obj_list()->fix_through_netfabb();
update_warning_icon_state(wxGetApp().obj_list()->get_mesh_errors_list());
});
return sizer;
};
line.append_widget(manifold_warning_icon);
def.label = "";
def.gui_type = "legend";
def.tooltip = L("Object name");
def.width = 21;
#ifdef __APPLE__
def.width = 19;
#endif
def.default_value = new ConfigOptionString{ " " };
m_og->append_single_option_line(Option(def, "object_name"));
line.append_option(Option(def, "object_name"));
m_og->append_line(line);
const int field_width = 5;
// Legend for object modification
auto line = Line{ "", "" };
line = Line{ "", "" };
def.label = "";
def.type = coString;
def.width = field_width/*50*/;
@ -334,6 +369,12 @@ void ObjectManipulation::emulate_kill_focus()
}
#endif // __APPLE__
void ObjectManipulation::update_warning_icon_state(const wxString& tooltip)
{
m_fix_throught_netfab_bitmap->SetBitmap(tooltip.IsEmpty() ? wxNullBitmap : m_manifold_warning_bmp.bmp());
m_fix_throught_netfab_bitmap->SetToolTip(tooltip);
}
void ObjectManipulation::reset_settings_value()
{
m_new_position = Vec3d::Zero();
@ -519,5 +560,13 @@ void ObjectManipulation::on_fill_empty_value(const std::string& opt_key)
m_og->set_value(opt_key, double_to_string(value));
}
void ObjectManipulation::msw_rescale()
{
m_manifold_warning_bmp.msw_rescale();
m_fix_throught_netfab_bitmap->SetBitmap(m_manifold_warning_bmp.bmp());
get_og()->msw_rescale();
}
} //namespace GUI
} //namespace Slic3r

View File

@ -8,6 +8,7 @@
class wxStaticText;
class LockButton;
class wxStaticBitmap;
namespace Slic3r {
namespace GUI {
@ -78,6 +79,9 @@ class ObjectManipulation : public OG_Settings
bool m_uniform_scale {true};
LockButton* m_lock_bnt{ nullptr };
ScalableBitmap m_manifold_warning_bmp;
wxStaticBitmap* m_fix_throught_netfab_bitmap;
#ifndef __APPLE__
// Currently focused option name (empty if none)
std::string m_focused_option;
@ -107,6 +111,9 @@ public:
void emulate_kill_focus();
#endif // __APPLE__
void update_warning_icon_state(const wxString& tooltip);
void msw_rescale();
private:
void reset_settings_value();

View File

@ -121,7 +121,7 @@ void ObjectSettings::update_settings_list()
if (cat.second.size() == 1 && cat.second[0] == "extruder")
continue;
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), cat.first, config, false, extra_column);
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
optgroup->label_width = 15;
optgroup->sidetext_width = 5.5;

View File

@ -64,6 +64,7 @@ template<class F> typename F::FN winapi_get_function(const wchar_t *dll, const c
}
#endif
// If called with nullptr, a DPI for the primary monitor is returned.
int get_dpi_for_window(wxWindow *window)
{
#ifdef _WIN32
@ -82,7 +83,8 @@ int get_dpi_for_window(wxWindow *window)
static auto GetDpiForWindow_fn = winapi_get_function<GetDpiForWindow_t>(L"User32.dll", "GetDpiForWindow");
static auto GetDpiForMonitor_fn = winapi_get_function<GetDpiForMonitor_t>(L"Shcore.dll", "GetDpiForMonitor");
const HWND hwnd = window->GetHandle();
// Desktop Window is the window of the primary monitor.
const HWND hwnd = (window == nullptr) ? ::GetDesktopWindow() : window->GetHandle();
if (GetDpiForWindow_fn != nullptr) {
// We're on Windows 10, we have per-screen DPI settings

View File

@ -59,13 +59,16 @@ public:
: P(parent, id, title, pos, size, style, name)
{
m_scale_factor = (float)get_dpi_for_window(this) / (float)DPI_DEFAULT;
m_normal_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
m_prev_scale_factor = m_scale_factor;
float scale_primary_display = (float)get_dpi_for_window(nullptr) / (float)DPI_DEFAULT;
m_normal_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
if (std::abs(m_scale_factor - scale_primary_display) > 1e-6)
m_normal_font = m_normal_font.Scale(m_scale_factor / scale_primary_display);
// An analog of em_unit value from GUI_App.
m_em_unit = std::max<size_t>(10, 10 * m_scale_factor);
m_prev_scale_factor = m_scale_factor;
recalc_font();
// recalc_font();
this->Bind(EVT_DPI_CHANGED, [this](const DpiChangedEvent &evt) {
m_scale_factor = (float)evt.dpi / (float)DPI_DEFAULT;
@ -107,7 +110,7 @@ public:
float prev_scale_factor() const { return m_prev_scale_factor; }
int em_unit() const { return m_em_unit; }
int font_size() const { return m_font_size; }
// int font_size() const { return m_font_size; }
const wxFont& normal_font() const { return m_normal_font; }
protected:
@ -116,19 +119,19 @@ protected:
private:
float m_scale_factor;
int m_em_unit;
int m_font_size;
// int m_font_size;
wxFont m_normal_font;
float m_prev_scale_factor;
bool m_can_rescale{ true };
void recalc_font()
{
wxClientDC dc(this);
const auto metrics = dc.GetFontMetrics();
m_font_size = metrics.height;
// void recalc_font()
// {
// wxClientDC dc(this);
// const auto metrics = dc.GetFontMetrics();
// m_font_size = metrics.height;
// m_em_unit = metrics.averageWidth;
}
// }
// check if new scale is differ from previous
bool is_new_scale_factor() const { return fabs(m_scale_factor - m_prev_scale_factor) > 0.001; }

View File

@ -100,7 +100,7 @@ void GLGizmoSlaSupports::on_render(const Selection& selection) const
if (m_quadric != nullptr && selection.is_from_single_instance())
render_points(selection, false);
render_selection_rectangle();
m_selection_rectangle.render(m_parent);
render_clipping_plane(selection);
glsafe(::glDisable(GL_BLEND));
@ -240,52 +240,6 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const
}
void GLGizmoSlaSupports::render_selection_rectangle() const
{
if (m_selection_rectangle_status == srOff)
return;
glsafe(::glLineWidth(1.5f));
float render_color[3] = {0.f, 1.f, 0.f};
if (m_selection_rectangle_status == srDeselect) {
render_color[0] = 1.f;
render_color[1] = 0.3f;
render_color[2] = 0.3f;
}
glsafe(::glColor3fv(render_color));
glsafe(::glPushAttrib(GL_TRANSFORM_BIT)); // remember current MatrixMode
glsafe(::glMatrixMode(GL_MODELVIEW)); // cache modelview matrix and set to identity
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
glsafe(::glMatrixMode(GL_PROJECTION)); // cache projection matrix and set to identity
glsafe(::glPushMatrix());
glsafe(::glLoadIdentity());
glsafe(::glOrtho(0.f, m_canvas_width, m_canvas_height, 0.f, -1.f, 1.f)); // set projection matrix so that world coords = window coords
// render the selection rectangle (window coordinates):
glsafe(::glPushAttrib(GL_ENABLE_BIT));
glsafe(::glLineStipple(4, 0xAAAA));
glsafe(::glEnable(GL_LINE_STIPPLE));
::glBegin(GL_LINE_LOOP);
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_start_corner(1), (GLfloat)0.5f);
::glVertex3f((GLfloat)m_selection_rectangle_end_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
::glVertex3f((GLfloat)m_selection_rectangle_start_corner(0), (GLfloat)m_selection_rectangle_end_corner(1), (GLfloat)0.5f);
glsafe(::glEnd());
glsafe(::glPopAttrib());
glsafe(::glPopMatrix()); // restore former projection matrix
glsafe(::glMatrixMode(GL_MODELVIEW));
glsafe(::glPopMatrix()); // restore former modelview matrix
glsafe(::glPopAttrib()); // restore former MatrixMode
}
void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const
{
glsafe(::glEnable(GL_DEPTH_TEST));
@ -513,11 +467,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
if (m_hover_id == -1) {
if (shift_down || alt_down) {
m_selection_rectangle_status = shift_down ? srSelect : srDeselect;
m_selection_rectangle_start_corner = mouse_position;
m_selection_rectangle_end_corner = mouse_position;
m_canvas_width = m_parent.get_canvas_size().get_width();
m_canvas_height = m_parent.get_canvas_size().get_height();
m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect);
}
}
else {
@ -533,7 +483,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
}
// left down without selection rectangle - place point on the mesh:
if (action == SLAGizmoEventType::LeftDown && m_selection_rectangle_status == srOff && !shift_down) {
if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle.is_dragging() && !shift_down) {
// If any point is in hover state, this should initiate its move - return control back to GLCanvas:
if (m_hover_id != -1)
return false;
@ -558,38 +508,36 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
}
// left up with selection rectangle - select points inside the rectangle:
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle_status != srOff) {
const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix();
const Camera& camera = m_parent.get_camera();
const std::array<int, 4>& viewport = camera.get_viewport();
const Transform3d& modelview_matrix = camera.get_view_matrix();
const Transform3d& projection_matrix = camera.get_projection_matrix();
if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp || action == SLAGizmoEventType::AltUp) && m_selection_rectangle.is_dragging()) {
// Is this a selection or deselection rectangle?
GLSelectionRectangle::EState rectangle_status = m_selection_rectangle.get_state();
// First collect positions of all the points in world coordinates.
const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix();
std::vector<Vec3d> points;
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
points.push_back(instance_matrix * support_point.pos.cast<double>());
points.back()(2) += m_z_shift;
}
// Now ask the rectangle which of the points are inside.
const Camera& camera = m_parent.get_camera();
std::vector<unsigned int> selected_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
// we'll recover current look direction (in world coords) and transform it to model coords.
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
// bounding box created from the rectangle corners - will take care of order of the corners
BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast<int>()), Point(m_selection_rectangle_end_corner.cast<int>())});
const Transform3d& instance_matrix_no_translation_no_scaling = volume->get_instance_transformation().get_matrix(true,false,true);
// we'll recover current look direction from the modelview matrix (in world coords)...
Vec3f direction_to_camera = camera.get_dir_forward().cast<float>();
// ...and transform it to model coords.
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
Vec3f scaling = volume->get_instance_scaling_factor().cast<float>();
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
// Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh:
for (unsigned int i=0; i<m_editing_mode_cache.size(); ++i) {
// Iterate over all points in the rectangle and check that they are neither clipped by the
// clipping plane nor obscured by the mesh.
for (const unsigned int i : selected_idxs) {
const sla::SupportPoint &support_point = m_editing_mode_cache[i].support_point;
Vec3f pos = instance_matrix.cast<float>() * support_point.pos;
pos(2) += m_z_shift;
GLdouble out_x, out_y, out_z;
::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), (GLdouble*)modelview_matrix.data(), (GLdouble*)projection_matrix.data(), (GLint*)viewport.data(), &out_x, &out_y, &out_z);
out_y = m_canvas_height - out_y;
if (rectangle.contains(Point(out_x, out_y)) && !is_point_clipped(support_point.pos.cast<double>())) {
if (!is_point_clipped(support_point.pos.cast<double>())) {
bool is_obscured = false;
// Cast a ray in the direction of the camera and look for intersection with the mesh:
std::vector<igl::Hit> hits;
@ -627,14 +575,13 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
}
if (!is_obscured) {
if (m_selection_rectangle_status == srDeselect)
if (rectangle_status == GLSelectionRectangle::Deselect)
unselect_point(i);
else
select_point(i);
}
}
}
m_selection_rectangle_status = srOff;
return true;
}
@ -652,9 +599,8 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
return true; // point has been placed and the button not released yet
// this prevents GLCanvas from starting scene rotation
if (m_selection_rectangle_status != srOff) {
m_selection_rectangle_end_corner = mouse_position;
m_selection_rectangle_status = shift_down ? srSelect : srDeselect;
if (m_selection_rectangle.is_dragging()) {
m_selection_rectangle.dragging(mouse_position);
return true;
}
@ -977,7 +923,7 @@ RENDER_AGAIN:
// Following is rendered in both editing and non-editing mode:
m_imgui->text("");
if (m_clipping_plane_distance == 0.f)
m_imgui->text("Clipping of view: ");
m_imgui->text(_(L("Clipping of view:"))+ " ");
else {
if (m_imgui->button(_(L("Reset direction")))) {
wxGetApp().CallAfter([this](){

View File

@ -3,6 +3,7 @@
#include "GLGizmoBase.hpp"
#include "GLGizmos.hpp"
#include "slic3r/GUI/GLSelectionRectangle.hpp"
// There is an L function in igl that would be overridden by our localization macro - let's undefine it...
#undef L
@ -67,13 +68,16 @@ public:
void delete_selected_points(bool force = false);
ClippingPlane get_sla_clipping_plane() const;
bool is_in_editing_mode() const { return m_editing_mode; }
bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); }
private:
bool on_init();
void on_update(const UpdateData& data, const Selection& selection);
virtual void on_render(const Selection& selection) const;
virtual void on_render_for_picking(const Selection& selection) const;
void render_selection_rectangle() const;
//void render_selection_rectangle() const;
void render_points(const Selection& selection, bool picking = false) const;
void render_clipping_plane(const Selection& selection) const;
bool is_mesh_update_necessary() const;
@ -91,20 +95,12 @@ private:
mutable Vec3d m_old_clipping_plane_normal;
mutable Vec3d m_clipping_plane_normal = Vec3d::Zero();
enum SelectionRectangleStatus {
srOff = 0,
srSelect = 1,
srDeselect = 2
}m_selection_rectangle_status = srOff;
GLSelectionRectangle m_selection_rectangle;
Vec2d m_selection_rectangle_start_corner;
Vec2d m_selection_rectangle_end_corner;
bool m_wait_for_up_event = false;
bool m_unsaved_changes = false; // Are there unsaved changes in manual mode?
bool m_selection_empty = true;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
int m_canvas_width;
int m_canvas_height;
mutable std::unique_ptr<TriangleMeshSlicer> m_tms;
mutable std::unique_ptr<TriangleMeshSlicer> m_supports_tms;

View File

@ -693,7 +693,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas)
gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown());
processed = true;
}
else if (evt.LeftUp() && (m_current == Flatten) && ((canvas.get_hover_volume_id() != -1) || grabber_contains_mouse()))
else if (evt.LeftUp() && (m_current == Flatten) && ((canvas.get_first_hover_volume_idx() != -1) || grabber_contains_mouse()))
{
// to avoid to loose the selection when user clicks an object while the Flatten gizmo is active
processed = true;
@ -843,13 +843,34 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas)
if (evt.GetEventType() == wxEVT_KEY_UP)
{
if ((m_current == SlaSupports) && (keyCode == WXK_SHIFT) && gizmo_event(SLAGizmoEventType::ShiftUp))
// shift has been just released - SLA gizmo might want to close rectangular selection.
processed = true;
if (m_current == SlaSupports)
{
GLGizmoSlaSupports* gizmo = reinterpret_cast<GLGizmoSlaSupports*>(get_current());
if ((m_current == SlaSupports) && (keyCode == WXK_ALT) && gizmo_event(SLAGizmoEventType::AltUp))
// alt has been just released - SLA gizmo might want to close rectangular selection.
if (keyCode == WXK_SHIFT)
{
// shift has been just released - SLA gizmo might want to close rectangular selection.
if (gizmo_event(SLAGizmoEventType::ShiftUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging()))
processed = true;
}
else if (keyCode == WXK_ALT)
{
// alt has been just released - SLA gizmo might want to close rectangular selection.
if (gizmo_event(SLAGizmoEventType::AltUp) || (gizmo->is_in_editing_mode() && gizmo->is_selection_rectangle_dragging()))
processed = true;
}
}
// if (processed)
// canvas.set_cursor(GLCanvas3D::Standard);
}
else if (evt.GetEventType() == wxEVT_KEY_DOWN)
{
if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && reinterpret_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode())
{
// canvas.set_cursor(GLCanvas3D::Cross);
processed = true;
}
}
if (processed)

View File

@ -48,9 +48,9 @@ KBShortcutsDialog::KBShortcutsDialog()
m_head_bitmaps.reserve(m_full_shortcuts.size());
const wxSize topic_size = wxSize(10 * wxGetApp().em_unit(), -1);
for (auto& sc : m_full_shortcuts)
for (auto& shortcut : m_full_shortcuts)
{
auto sizer = sc.second.second == szLeft ? l_sizer : r_sizer;
auto sizer = shortcut.second.second == szLeft ? l_sizer : r_sizer;
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(hsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10);
@ -59,7 +59,7 @@ KBShortcutsDialog::KBShortcutsDialog()
hsizer->Add(m_head_bitmaps.back(), 0, wxEXPAND | wxLEFT | wxRIGHT, 15);
// head
wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, topic_size);
wxStaticText* head = new wxStaticText(panel, wxID_ANY, shortcut.first, wxDefaultPosition, topic_size);
head->SetFont(head_font);
hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL);
@ -68,7 +68,7 @@ KBShortcutsDialog::KBShortcutsDialog()
auto grid_sizer = new wxFlexGridSizer(2, 5, 15);
sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT| wxRIGHT, 15);
for (auto pair : sc.second.first)
for (auto pair : shortcut.second.first)
{
auto shortcut = new wxStaticText(panel, wxID_ANY, _(pair.first));
shortcut->SetFont(bold_font);
@ -124,7 +124,7 @@ void KBShortcutsDialog::fill_shortcuts()
main_shortcuts.push_back(Shortcut("+" ,L("Add Instance to selected object ")));
main_shortcuts.push_back(Shortcut("-" ,L("Remove Instance from selected object")));
main_shortcuts.push_back(Shortcut("?" ,L("Show keyboard shortcuts list")));
main_shortcuts.push_back(Shortcut(ctrl+"LeftMouse" ,L("Select multiple object/Move multiple object")));
main_shortcuts.push_back(Shortcut(ctrl/*+"LeftMouse"*/,L("Press to select multiple object or move multiple object with mouse")));
m_full_shortcuts.push_back(std::make_pair(_(L("Main Shortcuts")), std::make_pair(main_shortcuts, szLeft)));

View File

@ -14,6 +14,7 @@
#include "libslic3r/Print.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "Tab.hpp"
#include "PresetBundle.hpp"
@ -35,6 +36,12 @@ MainFrame::MainFrame() :
DPIFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, "mainframe"),
m_printhost_queue_dlg(new PrintHostQueueDialog(this))
{
// Fonts were created by the DPIFrame constructor for the monitor, on which the window opened.
wxGetApp().update_fonts(this);
this->SetFont(this->normal_font());
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
wxGetApp().set_em_unit(std::max<size_t>(10, GetTextExtent("m").x - 1));
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
@ -53,11 +60,6 @@ DPIFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAU
SLIC3R_VERSION +
_(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")));
// initialize default width_unit according to the width of the one symbol ("x") of the current system font
const wxSize size = GetTextExtent("m");
wxGetApp().set_em_unit(std::max<size_t>(10, size.x - 1));
/* Load default preset bitmaps before a tabpanel initialization,
* but after filling of an em_unit value
*/
@ -140,6 +142,7 @@ void MainFrame::init_tabpanel()
// wxNB_NOPAGETHEME: Disable Windows Vista theme for the Notebook background. The theme performance is terrible on Windows 10
// with multiple high resolution displays connected.
m_tabpanel = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL | wxNB_NOPAGETHEME);
m_tabpanel->SetFont(Slic3r::GUI::wxGetApp().normal_font());
m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) {
auto panel = m_tabpanel->GetCurrentPage();
@ -205,12 +208,30 @@ void MainFrame::add_created_tab(Tab* panel)
bool MainFrame::can_save() const
{
return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false;
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
bool MainFrame::can_export_model() const
{
return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false;
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
bool MainFrame::can_export_supports() const
{
if ((m_plater == nullptr) || (m_plater->printer_technology() != ptSLA) || m_plater->model().objects.empty())
return false;
bool can_export = false;
const PrintObjects& objects = m_plater->sla_print().objects();
for (const SLAPrintObject* object : objects)
{
if (object->has_mesh(slaposBasePool) || object->has_mesh(slaposSupportTree))
{
can_export = true;
break;
}
}
return can_export;
}
bool MainFrame::can_export_gcode() const
@ -243,26 +264,25 @@ bool MainFrame::can_change_view() const
bool MainFrame::can_select() const
{
return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false;
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
bool MainFrame::can_delete() const
{
return (m_plater != nullptr) ? !m_plater->is_selection_empty() : false;
return (m_plater != nullptr) && !m_plater->is_selection_empty();
}
bool MainFrame::can_delete_all() const
{
return (m_plater != nullptr) ? !m_plater->model().objects.empty() : false;
return (m_plater != nullptr) && !m_plater->model().objects.empty();
}
void MainFrame::on_dpi_changed(const wxRect &suggested_rect)
{
wxGetApp().update_fonts();
// _strange_ workaround for correct em_unit calculation
const int new_em_unit = scale_factor() * 10;
wxGetApp().set_em_unit(std::max<size_t>(10, new_em_unit));
this->SetFont(this->normal_font());
// initialize default width_unit according to the width of the one symbol ("m") of the currently active font of this window.
wxGetApp().set_em_unit(std::max<size_t>(10, GetTextExtent("m").x - 1));
/* Load default preset bitmaps before a tabpanel initialization,
* but after filling of an em_unit value
@ -353,6 +373,8 @@ void MainFrame::init_menubar()
export_menu->AppendSeparator();
wxMenuItem* item_export_stl = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"));
wxMenuItem* item_export_stl_sla = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL including supports")) + dots, _(L("Export current plate as STL including supports")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, menu_icon("export_plater"));
wxMenuItem* item_export_amf = append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, menu_icon("export_plater"));
export_menu->AppendSeparator();
@ -422,10 +444,11 @@ void MainFrame::init_menubar()
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_save()); }, item_save->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_save()); }, item_save_as->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_plater != nullptr); }, item_import_model->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_gcode()); }, item_export_gcode->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_stl->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_export_model()); }, item_export_amf->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable((m_plater != nullptr) && can_slice()); }, m_menu_item_reslice_now->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_gcode()); }, item_export_gcode->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_model()); }, item_export_stl->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_supports()); }, item_export_stl_sla->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_export_model()); }, item_export_amf->GetId());
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_slice()); }, m_menu_item_reslice_now->GetId());
}
#ifdef _MSC_VER
@ -451,12 +474,12 @@ void MainFrame::init_menubar()
wxString hotkey_delete = "Del";
#endif
wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "A", _(L("Selects all objects")),
[this](wxCommandEvent&) { m_plater->select_all(); }, "");
[this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->select_all(); }, "");
editMenu->AppendSeparator();
wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete, _(L("Deletes the current selection")),
[this](wxCommandEvent&) { m_plater->remove_selected(); }, menu_icon("remove_menu"));
wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete, _(L("Deletes all objects")),
[this](wxCommandEvent&) { m_plater->reset(); }, menu_icon("delete_all_menu"));
[this](wxCommandEvent&) { m_plater->reset_with_confirm(); }, menu_icon("delete_all_menu"));
editMenu->AppendSeparator();
@ -528,7 +551,9 @@ void MainFrame::init_menubar()
// The camera control accelerators are captured by GLCanvas3D::on_char().
wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("Iso")) + sep + "&0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); });
viewMenu->AppendSeparator();
//TRN To be shown in the main menu View->Top
wxMenuItem* item_top = append_menu_item(viewMenu, wxID_ANY, _(L("Top")) + sep + "&1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); });
//TRN To be shown in the main menu View->Bottom
wxMenuItem* item_bottom = append_menu_item(viewMenu, wxID_ANY, _(L("Bottom")) + sep + "&2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); });
wxMenuItem* item_front = append_menu_item(viewMenu, wxID_ANY, _(L("Front")) + sep + "&3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); });
wxMenuItem* item_rear = append_menu_item(viewMenu, wxID_ANY, _(L("Rear")) + sep + "&4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); });

View File

@ -63,6 +63,7 @@ class MainFrame : public DPIFrame
bool can_save() const;
bool can_export_model() const;
bool can_export_supports() const;
bool can_export_gcode() const;
bool can_slice() const;
bool can_change_view() const;

View File

@ -724,7 +724,7 @@ Sidebar::Sidebar(Plater *parent)
auto init_btn = [this](wxButton **btn, wxString label) {
*btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
wxDefaultSize, wxBU_EXACTFIT | wxNO_BORDER);
wxDefaultSize, wxBU_EXACTFIT);
(*btn)->SetFont(wxGetApp().bold_font());
};
@ -896,8 +896,7 @@ void Sidebar::msw_rescale()
p->frequently_changed_parameters->get_og(false)->msw_rescale();
p->object_list->msw_rescale();
p->object_manipulation->get_og()->msw_rescale();
p->object_manipulation->msw_rescale();
p->object_settings->msw_rescale();
p->object_info->msw_rescale();
@ -970,7 +969,7 @@ void Sidebar::show_info_sizer()
p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0), size(1), size(2)));
p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count())));
auto& stats = model_object->volumes.front()->mesh.stl.stats;
const auto& stats = model_object->get_object_stl_stats();//model_object->volumes.front()->mesh.stl.stats;
p->object_info->info_volume->SetLabel(wxString::Format("%.2f", stats.volume));
p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast<int>(model_object->facets_count()), stats.number_of_parts));
@ -990,7 +989,7 @@ void Sidebar::show_info_sizer()
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
}
else {
p->object_info->info_manifold->SetLabel(L("Yes"));
p->object_info->info_manifold->SetLabel(_(L("Yes")));
p->object_info->showing_manifold_warning_icon = false;
p->object_info->info_manifold->SetToolTip("");
p->object_info->manifold_warning_icon->SetToolTip("");
@ -1328,6 +1327,7 @@ struct Plater::priv
bool can_split_to_volumes() const;
bool can_arrange() const;
bool can_layers_editing() const;
bool can_fix_through_netfabb() const;
void msw_rescale_object_menu();
@ -1379,6 +1379,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, view_toolbar(GLToolbar::Radio)
#endif // ENABLE_SVG_ICONS
{
this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font());
arranging = false;
rotoptimizing = false;
background_process.set_fff_print(&fff_print);
@ -1449,7 +1451,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// 3DScene/Toolbar:
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_COPY, [q](SimpleEvent&) { q->copy_selection_to_clipboard(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_PASTE, [q](SimpleEvent&) { q->paste_from_clipboard(); });
@ -2405,7 +2407,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation)
// Background data is valid.
if ((return_state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ||
(return_state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0 )
this->statusbar()->set_status_text(L("Ready to slice"));
this->statusbar()->set_status_text(_(L("Ready to slice")));
sidebar->set_btn_label(ActionButtonType::abExport, _(label_btn_export));
sidebar->set_btn_label(ActionButtonType::abSendGCode, _(label_btn_send));
@ -2443,7 +2445,7 @@ bool Plater::priv::restart_background_process(unsigned int state)
// The print is valid and it can be started.
if (this->background_process.start()) {
this->statusbar()->set_cancel_callback([this]() {
this->statusbar()->set_status_text(L("Cancelling"));
this->statusbar()->set_status_text(_(L("Cancelling")));
this->background_process.stop();
});
return true;
@ -2667,7 +2669,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
}
this->statusbar()->set_progress(evt.status.percent);
this->statusbar()->set_status_text(_(L(evt.status.text)) + wxString::FromUTF8(""));
this->statusbar()->set_status_text(_(evt.status.text) + wxString::FromUTF8(""));
}
if (evt.status.flags & (PrintBase::SlicingStatus::RELOAD_SCENE || PrintBase::SlicingStatus::RELOAD_SLA_SUPPORT_POINTS)) {
switch (this->printer_technology) {
@ -2726,7 +2728,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
this->statusbar()->set_status_text(message);
}
if (canceled)
this->statusbar()->set_status_text(L("Cancelled"));
this->statusbar()->set_status_text(_(L("Cancelled")));
this->sidebar->show_sliced_info_sizer(success);
@ -2933,12 +2935,12 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
[this](wxCommandEvent&) { reload_from_disk(); });
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")),
[this](wxCommandEvent&) { q->export_stl(true); });
[this](wxCommandEvent&) { q->export_stl(false, true); });
menu->AppendSeparator();
}
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
wxMenuItem* item_fix_through_netfabb = sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
wxMenu* mirror_menu = new wxMenu();
if (mirror_menu == nullptr)
@ -2958,6 +2960,8 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
{
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_mirror()); }, item_mirror->GetId());
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_delete()); }, item_delete->GetId());
if (item_fix_through_netfabb)
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_fix_through_netfabb()); }, item_fix_through_netfabb->GetId());
}
return true;
@ -3127,6 +3131,15 @@ bool Plater::priv::can_delete_all() const
return !model.objects.empty();
}
bool Plater::priv::can_fix_through_netfabb() const
{
int obj_idx = get_selected_object_idx();
if (obj_idx < 0)
return false;
return model.objects[obj_idx]->get_mesh_errors_count() > 0;
}
bool Plater::priv::can_increase_instances() const
{
if (arranging || rotoptimizing) {
@ -3290,6 +3303,11 @@ void Plater::select_all() { p->select_all(); }
void Plater::remove(size_t obj_idx) { p->remove(obj_idx); }
void Plater::reset() { p->reset(); }
void Plater::reset_with_confirm()
{
if (wxMessageDialog((wxWindow*)this, _(L("All objects will be removed, continue ?")), _(L("Delete all")), wxYES_NO | wxYES_DEFAULT | wxCENTRE).ShowModal() == wxID_YES)
reset();
}
void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_model(obj_idx); }
@ -3439,7 +3457,7 @@ void Plater::export_gcode()
p->export_gcode(std::move(output_path), PrintHostJob());
}
void Plater::export_stl(bool selection_only)
void Plater::export_stl(bool extended, bool selection_only)
{
if (p->model.objects.empty()) { return; }
@ -3474,8 +3492,65 @@ void Plater::export_stl(bool selection_only)
}
}
else
{
mesh = p->model.mesh();
if (extended && (p->printer_technology == ptSLA))
{
const PrintObjects& objects = p->sla_print.objects();
for (const SLAPrintObject* object : objects)
{
const ModelObject* model_object = object->model_object();
Transform3d mesh_trafo_inv = object->trafo().inverse();
bool is_left_handed = object->is_left_handed();
TriangleMesh pad_mesh;
bool has_pad_mesh = object->has_mesh(slaposBasePool);
if (has_pad_mesh)
{
pad_mesh = object->get_mesh(slaposBasePool);
pad_mesh.transform(mesh_trafo_inv);
}
TriangleMesh supports_mesh;
bool has_supports_mesh = object->has_mesh(slaposSupportTree);
if (has_supports_mesh)
{
supports_mesh = object->get_mesh(slaposSupportTree);
supports_mesh.transform(mesh_trafo_inv);
}
const std::vector<SLAPrintObject::Instance>& obj_instances = object->instances();
for (const SLAPrintObject::Instance& obj_instance : obj_instances)
{
auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(),
[&obj_instance](const ModelInstance *mi) { return mi->id() == obj_instance.instance_id; });
assert(it != model_object->instances.end());
if (it != model_object->instances.end())
{
int instance_idx = it - model_object->instances.begin();
const Transform3d& inst_transform = object->model_object()->instances[instance_idx]->get_transformation().get_matrix();
if (has_pad_mesh)
{
TriangleMesh inst_pad_mesh = pad_mesh;
inst_pad_mesh.transform(inst_transform, is_left_handed);
mesh.merge(inst_pad_mesh);
}
if (has_supports_mesh)
{
TriangleMesh inst_supports_mesh = supports_mesh;
inst_supports_mesh.transform(inst_transform, is_left_handed);
mesh.merge(inst_supports_mesh);
}
}
}
}
}
}
Slic3r::store_stl(path_u8.c_str(), &mesh, true);
p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path));
}

View File

@ -153,6 +153,7 @@ public:
void select_all();
void remove(size_t obj_idx);
void reset();
void reset_with_confirm();
void delete_object_from_model(size_t obj_idx);
void remove_selected();
void increase_instances(size_t num = 1);
@ -163,7 +164,7 @@ public:
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);
void export_gcode();
void export_stl(bool selection_only = false);
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
void reslice();

View File

@ -32,6 +32,7 @@ ProgressStatusBar::ProgressStatusBar(wxWindow *parent, int id):
m_prog->Hide();
m_cancelbutton->Hide();
self->SetFont(GUI::wxGetApp().normal_font());
self->SetFieldsCount(3);
int w[] = {-1, 150, 155};
self->SetStatusWidths(3, w);

View File

@ -42,6 +42,7 @@ Tab::Tab(wxNotebook* parent, const wxString& title, const char* name) :
m_parent(parent), m_title(title), m_name(name)
{
Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBK_LEFT | wxTAB_TRAVERSAL, name);
this->SetFont(Slic3r::GUI::wxGetApp().normal_font());
set_type();
m_compatible_printers.type = Preset::TYPE_PRINTER;
@ -1678,7 +1679,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup)
}
auto printhost_browse = [=](wxWindow* parent) {
add_scaled_button(parent, &m_printhost_browse_btn, "browse", _(L(" Browse ")) + dots, wxBU_LEFT | wxBU_EXACTFIT);
add_scaled_button(parent, &m_printhost_browse_btn, "browse", _(L("Browse")) + " "+ dots, wxBU_LEFT | wxBU_EXACTFIT);
ScalableButton* btn = m_printhost_browse_btn;
btn->SetFont(Slic3r::GUI::wxGetApp().normal_font());
@ -2057,7 +2058,7 @@ void TabPrinter::build_sla()
optgroup->append_single_option_line("area_fill");
optgroup = page->new_optgroup(_(L("Corrections")));
line = Line{ m_config->def()->get("relative_correction")->full_label, "" };
line = Line{ _(m_config->def()->get("relative_correction")->full_label), "" };
// std::vector<std::string> axes{ "X", "Y", "Z" };
std::vector<std::string> axes{ "XY", "Z" };
int id = 0;
@ -3313,7 +3314,7 @@ void TabSLAMaterial::build()
// std::vector<std::string> axes{ "X", "Y", "Z" };
std::vector<std::string> axes{ "XY", "Z" };
for (auto& opt_key : corrections) {
auto line = Line{ m_config->def()->get(opt_key)->full_label, "" };
auto line = Line{ _(m_config->def()->get(opt_key)->full_label), "" };
int id = 0;
for (auto& axis : axes) {
auto opt = optgroup->get_option(opt_key, id);

View File

@ -370,9 +370,13 @@ int em_unit(wxWindow* win)
{
if (win)
{
Slic3r::GUI::DPIDialog* dlg = dynamic_cast<Slic3r::GUI::DPIDialog*>(Slic3r::GUI::find_toplevel_parent(win));
wxTopLevelWindow *toplevel = Slic3r::GUI::find_toplevel_parent(win);
Slic3r::GUI::DPIDialog* dlg = dynamic_cast<Slic3r::GUI::DPIDialog*>(toplevel);
if (dlg)
return dlg->em_unit();
Slic3r::GUI::DPIFrame* frame = dynamic_cast<Slic3r::GUI::DPIFrame*>(toplevel);
if (frame)
return frame->em_unit();
}
return Slic3r::GUI::wxGetApp().em_unit();
@ -417,6 +421,28 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, con
// ObjectDataViewModelNode
// ----------------------------------------------------------------------------
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) :
m_parent(parent),
m_type(type),
m_extruder(wxEmptyString)
{
if (type == itSettings) {
m_name = "Settings to modified";
}
else if (type == itInstanceRoot) {
m_name = _(L("Instances"));
#ifdef __WXGTK__
m_container = true;
#endif //__WXGTK__
}
else if (type == itInstance) {
m_idx = parent->GetChildCount();
m_name = wxString::Format(_(L("Instance_%d")), m_idx + 1);
set_action_icon();
}
}
void ObjectDataViewModelNode::set_action_icon()
{
m_action_icon_name = m_type == itObject ? "advanced_plus" :
@ -455,7 +481,7 @@ bool ObjectDataViewModelNode::update_settings_digest(const std::vector<std::stri
m_name = wxEmptyString;
for (auto& cat : m_opt_categories)
m_name += cat + "; ";
m_name += _(cat) + "; ";
if (!m_name.IsEmpty())
m_name.erase(m_name.Length()-2, 2); // Delete last "; "
@ -491,14 +517,21 @@ ObjectDataViewModel::~ObjectDataViewModel()
m_bitmap_cache = nullptr;
}
wxDataViewItem ObjectDataViewModel::Add(const wxString &name, const int extruder)
wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
const int extruder,
const bool has_errors/* = false*/)
{
const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
auto root = new ObjectDataViewModelNode(name, extruder_str);
// Add error icon if detected auto-repaire
if (has_errors)
root->m_bmp = *m_warning_bmp;
m_objects.push_back(root);
// notify control
wxDataViewItem child((void*)root);
wxDataViewItem parent((void*)NULL);
ItemAdded(parent, child);
return child;
}
@ -506,6 +539,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name, const int extruder
wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const bool has_errors/* = false*/,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
{
@ -519,9 +553,14 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot)
insert_position = -1;
const bool obj_errors = root->m_bmp.IsOk();
if (create_frst_child && root->m_volumes_cnt == 0)
{
const auto node = new ObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0);
const Slic3r::ModelVolumeType type = Slic3r::ModelVolumeType::MODEL_PART;
const auto node = new ObjectDataViewModelNode(root, root->m_name, GetVolumeIcon(type, obj_errors), extruder_str, 0);
node->m_volume_type = type;
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
const wxDataViewItem child((void*)node);
@ -529,12 +568,15 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
root->m_volumes_cnt++;
if (insert_position > 0) insert_position++;
node->m_volume_type = volume_type;
}
const auto node = new ObjectDataViewModelNode(root, name, *m_volume_bmps[int(volume_type)], extruder_str, root->m_volumes_cnt);
const auto node = new ObjectDataViewModelNode(root, name, GetVolumeIcon(volume_type, has_errors), extruder_str, root->m_volumes_cnt);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// if part with errors is added, but object wasn't marked, then mark it
if (!obj_errors && has_errors)
root->SetBitmap(*m_warning_bmp);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
@ -1299,15 +1341,61 @@ void ObjectDataViewModel::Rescale()
node->msw_rescale();
if (node->m_type & itVolume)
node->m_bmp = *m_volume_bmps[node->volume_type()];
node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight());
if (node->m_type & itObject && node->m_bmp.IsOk())
node->m_bmp = create_scaled_bitmap(nullptr, "exclamation");
node->m_bmp = *m_warning_bmp;
ItemChanged(item);
}
}
wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/)
{
if (!is_marked)
return *m_volume_bmps[static_cast<int>(vol_type)];
std::string scaled_bitmap_name = "warning" + std::to_string(static_cast<int>(vol_type));
scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit());
wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name);
if (bmp == nullptr) {
std::vector<wxBitmap> bmps;
bmps.emplace_back(*m_warning_bmp);
bmps.emplace_back(*m_volume_bmps[static_cast<int>(vol_type)]);
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
}
return *bmp;
}
void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object/* = false*/)
{
if (!item.IsOk())
return;
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
if (!node->GetBitmap().IsOk() || !(node->GetType() & (itVolume | itObject)))
return;
if (node->GetType() & itVolume) {
node->SetBitmap(*m_volume_bmps[static_cast<int>(node->volume_type())]);
return;
}
node->SetBitmap(wxNullBitmap);
if (unmark_object)
{
wxDataViewItemArray children;
GetChildren(item, children);
for (const wxDataViewItem& child : children)
DeleteWarningIcon(child);
}
}
//-----------------------------------------------------------------------------
// PrusaDataViewBitmapText
//-----------------------------------------------------------------------------
@ -2450,11 +2538,7 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 10*/) :
m_mode_btns.reserve(3);
for (const auto& button : buttons) {
// int x, y;
// parent->GetTextExtent(button.first, &x, &y, nullptr, nullptr, &Slic3r::GUI::wxGetApp().bold_font());
// const wxSize size = wxSize(x + button.second.GetWidth() + Slic3r::GUI::wxGetApp().em_unit(),
// y + Slic3r::GUI::wxGetApp().em_unit());
m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first/*, size*/));
m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first));
}
for (auto btn : m_mode_btns)

View File

@ -224,28 +224,7 @@ public:
set_action_icon();
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const ItemType type) :
m_parent(parent),
m_type(type),
m_extruder(wxEmptyString)
{
if (type == itSettings) {
m_name = "Settings to modified";
}
else if (type == itInstanceRoot) {
m_name = "Instances";
#ifdef __WXGTK__
m_container = true;
#endif //__WXGTK__
}
else if (type == itInstance) {
m_idx = parent->GetChildCount();
m_name = wxString::Format("Instance_%d", m_idx+1);
set_action_icon();
}
}
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode()
{
@ -328,14 +307,10 @@ public:
return false;
}
void SetBitmap(const wxBitmap &icon)
{
m_bmp = icon;
}
ItemType GetType() const {
return m_type;
}
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
const wxBitmap& GetBitmap() const { return m_bmp; }
const wxString& GetName() const { return m_name; }
ItemType GetType() const { return m_type; }
void SetIdx(const int& idx) {
m_idx = idx;
@ -344,9 +319,7 @@ public:
m_name = wxString::Format("Instance_%d", m_idx + 1);
}
int GetIdx() const {
return m_idx;
}
int GetIdx() const { return m_idx; }
// use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node)
@ -379,10 +352,10 @@ public:
// Set action icons for node
void set_action_icon();
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
int volume_type() const { return int(m_volume_type); }
void msw_rescale();
void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories);
int volume_type() const { return int(m_volume_type); }
void msw_rescale();
private:
friend class ObjectDataViewModel;
};
@ -398,6 +371,7 @@ class ObjectDataViewModel :public wxDataViewModel
{
std::vector<ObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap*> m_volume_bmps;
wxBitmap* m_warning_bmp;
wxDataViewCtrl* m_ctrl{ nullptr };
@ -405,10 +379,13 @@ public:
ObjectDataViewModel();
~ObjectDataViewModel();
wxDataViewItem Add(const wxString &name, const int extruder);
wxDataViewItem Add( const wxString &name,
const int extruder,
const bool has_errors = false);
wxDataViewItem AddVolumeChild( const wxDataViewItem &parent_item,
const wxString &name,
const Slic3r::ModelVolumeType volume_type,
const bool has_errors = false,
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
@ -480,11 +457,16 @@ public:
const std::vector<std::string>& categories);
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; }
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; }
// Rescale bitmaps for existing Items
void Rescale();
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
};
// ----------------------------------------------------------------------------