Merge branch 'master' into fs_QuadricEdgeCollapse

# Conflicts:
#	src/slic3r/GUI/NotificationManager.hpp
This commit is contained in:
Filip Sykala 2021-08-25 13:16:01 +02:00
commit 790d445420
33 changed files with 771 additions and 378 deletions

View File

@ -481,6 +481,7 @@ add_custom_target(gettext_make_pot
COMMAND xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --debug
-f "${L10N_DIR}/list.txt"
-o "${L10N_DIR}/PrusaSlicer.pot"
COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${L10N_DIR}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
COMMENT "Generate pot file from strings in the source tree"
)
@ -553,6 +554,8 @@ endfunction()
add_subdirectory(src)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT PrusaSlicer_app_console)
add_dependencies(gettext_make_pot hintsToPot)
# Perl bindings, currently only used for the unit / integration tests of libslic3r.
# Also runs the unit / integration tests.
#FIXME Port the tests into C++ to finally get rid of the Perl!

View File

@ -49,7 +49,13 @@
# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;)
# Algorithm shows hint only if not in all disabled tags.
# if there are both disabled and preferred, only preferred that are not in disabled are valid.
#
#
# Notifications shows in random order, already shown notifications are saved at cache/hints.cereal (as binary - human non-readable)
# You can affect random ordering by seting weigh
# weight = 5
# Weight must be larger or equal to 1. Default weight is 1.
# Weight defines probability as weight : sum_of_all_weights.
[hint:Fuzzy skin]
text = Fuzzy skin\nDid you know that you can create rough fibre-like texture on the sides of your models using the<a>Fuzzy skin</a>feature? You can also use modifiers to apply fuzzy-skin only to a portion of your model.

View File

@ -7,6 +7,8 @@ varying vec2 tex_coords;
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0);
gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position.x, v_position.y, v_position.z, 1.0);
// the following line leads to crash on some Intel graphics card
//gl_Position = gl_ModelViewProjectionMatrix * vec4(v_position, 1.0);
tex_coords = v_tex_coords;
}

View File

@ -13,6 +13,7 @@ add_subdirectory(qhull)
add_subdirectory(Shiny)
add_subdirectory(semver)
add_subdirectory(libigl)
add_subdirectory(hints)
# Adding libnest2d project for bin packing...
add_subdirectory(libnest2d)

12
src/hints/CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
cmake_minimum_required(VERSION 3.13)
project(HintsToPot)
add_executable(hintsToPot
HintsToPot.cpp)
target_link_libraries(hintsToPot PRIVATE boost_libs)
#encoding_check(HintsToPot)

84
src/hints/HintsToPot.cpp Normal file
View File

@ -0,0 +1,84 @@
#include <iostream>
#include <vector>
#include <string>
#include <boost/filesystem.hpp>
#include <boost/dll.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/algorithm/string/predicate.hpp>
bool write_to_pot(boost::filesystem::path path, const std::vector<std::pair<std::string, std::string>>& data)
{
boost::filesystem::ofstream file(std::move(path), std::ios_base::app);
for (const auto& element : data)
{
//Example of .pot element
//#: src/slic3r/GUI/GUI_App.cpp:1647 src/slic3r/GUI/wxExtensions.cpp:687
//msgctxt "Mode"
//msgid "Advanced"
//msgstr ""
file << "\n#: resources/data/hints.ini: ["<< element.first << "]\nmsgid \"" << element.second << "\"\nmsgstr \"\"\n";
}
file.close();
return true;
}
bool read_hints_ini(boost::filesystem::path path, std::vector<std::pair<std::string, std::string>>& pot_elements)
{
namespace pt = boost::property_tree;
pt::ptree tree;
boost::nowide::ifstream ifs(path.string());
try {
pt::read_ini(ifs, tree);
}
catch (const boost::property_tree::ini_parser::ini_parser_error& err) {
std::cout << err.what() << std::endl;
return false;
}
for (const auto& section : tree) {
if (boost::starts_with(section.first, "hint:")) {
for (const auto& data : section.second) {
if (data.first == "text")
{
pot_elements.emplace_back(section.first, data.second.data());
break;
}
}
}
}
return true;
}
int main(int argc, char* argv[])
{
std::vector<std::pair<std::string, std::string>> data;
boost::filesystem::path path_to_ini;
boost::filesystem::path path_to_pot;
if (argc != 3)
{
std::cout << "HINTS_TO_POT FAILED: WRONG NUM OF ARGS" << std::endl;
return -1;
}
try {
path_to_ini = boost::filesystem::canonical(boost::filesystem::path(argv[1])).parent_path() / "resources" / "data" / "hints.ini";
path_to_pot = boost::filesystem::canonical(boost::filesystem::path(argv[2])).parent_path() / "localization" /"PrusaSlicer.pot";
} catch (std::exception&) {
std::cout << "HINTS_TO_POT FAILED: BOOST CANNONICAL" << std::endl;
return -1;
}
if (!boost::filesystem::exists(path_to_ini)){
std::cout << "HINTS_TO_POT FAILED: PATH TO INI DOES NOT EXISTS" << std::endl;
std::cout << path_to_ini.string() << std::endl;
return -1;
}
if (!read_hints_ini(std::move(path_to_ini), data)) {
std::cout << "HINTS_TO_POT FAILED TO READ HINTS INI" << std::endl;
return -1;
}
if (!write_to_pot(std::move(path_to_pot), data)) {
std::cout << "HINTS_TO_POT FAILED TO WRITE POT FILE" << std::endl;
return -1;
}
std::cout << "HINTS_TO_POT SUCCESS" << std::endl;
return 0;
}

View File

@ -167,9 +167,6 @@ void AppConfig::set_defaults()
if (get("show_splash_screen").empty())
set("show_splash_screen", "1");
if (get("last_hint").empty())
set("last_hint", "0");
if (get("show_hints").empty())
set("show_hints", "1");

View File

@ -71,12 +71,16 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print,
Polygons islands;
ConstPrintObjectPtrs island_to_object;
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
const PrintObject *object = print.objects()[print_object_idx];
if (! object->has_brim())
continue;
Polygons islands_object;
islands_object.reserve(bottom_layers_expolygons[print_object_idx].size());
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx])
islands_object.emplace_back(ex_poly.contour);
const PrintObject *object = print.objects()[print_object_idx];
islands.reserve(islands.size() + object->instances().size() * islands_object.size());
for (const PrintInstance &instance : object->instances())
for (Polygon &poly : islands_object) {

View File

@ -956,9 +956,22 @@ void ModelObject::center_around_origin(bool include_modifiers)
void ModelObject::ensure_on_bed(bool allow_negative_z)
{
const double min_z = get_min_z();
if (!allow_negative_z || min_z > SINKING_Z_THRESHOLD)
translate_instances({ 0.0, 0.0, -min_z });
double z_offset = 0.0;
if (allow_negative_z) {
if (volumes.size() == 1)
z_offset = -get_min_z();
else {
const double max_z = get_max_z();
if (max_z < SINKING_MIN_Z_THRESHOLD)
z_offset = SINKING_MIN_Z_THRESHOLD - max_z;
}
}
else
z_offset = -get_min_z();
if (z_offset != 0.0)
translate_instances(z_offset * Vec3d::UnitZ());
}
void ModelObject::translate_instances(const Vec3d& vector)
@ -1429,6 +1442,19 @@ double ModelObject::get_min_z() const
}
}
double ModelObject::get_max_z() const
{
if (instances.empty())
return 0.0;
else {
double max_z = -DBL_MAX;
for (size_t i = 0; i < instances.size(); ++i) {
max_z = std::max(max_z, get_instance_max_z(i));
}
return max_z;
}
}
double ModelObject::get_instance_min_z(size_t instance_idx) const
{
double min_z = DBL_MAX;
@ -1450,6 +1476,27 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
return min_z + inst->get_offset(Z);
}
double ModelObject::get_instance_max_z(size_t instance_idx) const
{
double max_z = -DBL_MAX;
const ModelInstance* inst = instances[instance_idx];
const Transform3d& mi = inst->get_matrix(true);
for (const ModelVolume* v : volumes) {
if (!v->is_model_part())
continue;
const Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull();
for (const stl_facet& facet : hull.stl.facet_start)
for (int i = 0; i < 3; ++i)
max_z = std::max(max_z, (mv * facet.vertex[i].cast<double>()).z());
}
return max_z + inst->get_offset(Z);
}
unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
{
unsigned int num_printable = 0;

View File

@ -358,7 +358,9 @@ public:
void bake_xy_rotation_into_meshes(size_t instance_idx);
double get_min_z() const;
double get_max_z() const;
double get_instance_min_z(size_t instance_idx) const;
double get_instance_max_z(size_t instance_idx) const;
// Called by Print::validate() from the UI thread.
unsigned int check_instances_print_volume_state(const BoundingBoxf3& print_volume);
@ -1177,6 +1179,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2);
#endif /* NDEBUG */
static const float SINKING_Z_THRESHOLD = -0.001f;
static const double SINKING_MIN_Z_THRESHOLD = 0.05;
} // namespace Slic3r

View File

@ -413,9 +413,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
}
}
const std::vector<std::string>& Preset::print_options()
{
static std::vector<std::string> s_opts {
static std::vector<std::string> s_Preset_print_options {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "ensure_vertical_shell_thickness", "avoid_crossing_perimeters", "thin_walls", "overhangs",
@ -447,13 +445,9 @@ const std::vector<std::string>& Preset::print_options()
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}
};
const std::vector<std::string>& Preset::filament_options()
{
static std::vector<std::string> s_opts {
static std::vector<std::string> s_Preset_filament_options {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
"extrusion_multiplier", "filament_density", "filament_cost", "filament_spool_weight", "filament_loading_speed", "filament_loading_speed_start", "filament_load_time",
"filament_unloading_speed", "filament_unloading_speed_start", "filament_unload_time", "filament_toolchange_delay", "filament_cooling_moves",
@ -466,30 +460,17 @@ const std::vector<std::string>& Preset::filament_options()
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
};
return s_opts;
}
};
const std::vector<std::string>& Preset::machine_limits_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
static std::vector<std::string> s_Preset_machine_limits_options {
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
"machine_min_extruding_rate", "machine_min_travel_rate",
"machine_max_jerk_x", "machine_max_jerk_y", "machine_max_jerk_z", "machine_max_jerk_e",
};
}
return s_opts;
}
};
const std::vector<std::string>& Preset::printer_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"bed_shape", "bed_custom_texture", "bed_custom_model", "z_offset", "gcode_flavor", "use_relative_e_distances",
"use_firmware_retraction", "use_volumetric_e", "variable_layer_height",
@ -502,25 +483,9 @@ const std::vector<std::string>& Preset::printer_options()
"default_print_profile", "inherits",
"remaining_times", "silent_mode",
"machine_limits_usage", "thumbnails"
};
s_opts.insert(s_opts.end(), Preset::machine_limits_options().begin(), Preset::machine_limits_options().end());
s_opts.insert(s_opts.end(), Preset::nozzle_options().begin(), Preset::nozzle_options().end());
}
return s_opts;
}
};
// The following nozzle options of a printer profile will be adjusted to match the size
// of the nozzle_diameter vector.
const std::vector<std::string>& Preset::nozzle_options()
{
return print_config_def.extruder_option_keys();
}
const std::vector<std::string>& Preset::sla_print_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
static std::vector<std::string> s_Preset_sla_print_options {
"layer_height",
"faded_layers",
"supports_enable",
@ -566,16 +531,9 @@ const std::vector<std::string>& Preset::sla_print_options()
"compatible_printers",
"compatible_printers_condition",
"inherits"
};
}
return s_opts;
}
};
const std::vector<std::string>& Preset::sla_material_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
static std::vector<std::string> s_Preset_sla_material_options {
"material_type",
"initial_layer_height",
"bottle_cost",
@ -590,16 +548,9 @@ const std::vector<std::string>& Preset::sla_material_options()
"default_sla_material_profile",
"compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits"
};
}
return s_opts;
}
};
const std::vector<std::string>& Preset::sla_printer_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
static std::vector<std::string> s_Preset_sla_printer_options {
"printer_technology",
"bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
@ -617,8 +568,26 @@ const std::vector<std::string>& Preset::sla_printer_options()
"print_host", "printhost_apikey", "printhost_cafile",
"printer_notes",
"inherits"
};
}
};
const std::vector<std::string>& Preset::print_options() { return s_Preset_print_options; }
const std::vector<std::string>& Preset::filament_options() { return s_Preset_filament_options; }
const std::vector<std::string>& Preset::machine_limits_options() { return s_Preset_machine_limits_options; }
// The following nozzle options of a printer profile will be adjusted to match the size
// of the nozzle_diameter vector.
const std::vector<std::string>& Preset::nozzle_options() { return print_config_def.extruder_option_keys(); }
const std::vector<std::string>& Preset::sla_print_options() { return s_Preset_sla_print_options; }
const std::vector<std::string>& Preset::sla_material_options() { return s_Preset_sla_material_options; }
const std::vector<std::string>& Preset::sla_printer_options() { return s_Preset_sla_printer_options; }
const std::vector<std::string>& Preset::printer_options()
{
static std::vector<std::string> s_opts = [](){
std::vector<std::string> opts = s_Preset_printer_options;
append(opts, s_Preset_machine_limits_options);
append(opts, Preset::nozzle_options());
return opts;
}();
return s_opts;
}
@ -1426,11 +1395,7 @@ std::string PhysicalPrinter::separator()
return " * ";
}
const std::vector<std::string>& PhysicalPrinter::printer_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
static std::vector<std::string> s_PhysicalPrinter_opts {
"preset_name", // temporary option to compatibility with older Slicer
"preset_names",
"printer_technology",
@ -1442,10 +1407,13 @@ const std::vector<std::string>& PhysicalPrinter::printer_options()
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
"printhost_password"
};
}
return s_opts;
"printhost_password",
"printhost_ssl_ignore_revoke"
};
const std::vector<std::string>& PhysicalPrinter::printer_options()
{
return s_PhysicalPrinter_opts;
}
static constexpr auto legacy_print_host_options = {

View File

@ -371,7 +371,7 @@ public:
const Preset& get_edited_preset() const { return m_edited_preset; }
// Return the last saved preset.
const Preset& get_saved_preset() const { return m_saved_preset; }
// const Preset& get_saved_preset() const { return m_saved_preset; }
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
@ -395,7 +395,7 @@ public:
void discard_current_changes() {
m_presets[m_idx_selected].reset_dirty();
m_edited_preset = m_presets[m_idx_selected];
update_saved_preset_from_current_preset();
// update_saved_preset_from_current_preset();
}
// Return a preset by its name. If the preset is active, a temporary copy is returned.
@ -474,7 +474,7 @@ public:
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
bool saved_is_dirty() const
{ return is_dirty(&this->get_edited_preset(), &this->get_saved_preset()); }
{ return is_dirty(&this->get_edited_preset(), &m_saved_preset); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return the list of keys where they differ.
// std::vector<std::string> saved_dirty_options() const
// { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), /* deep_compare */ false); }

View File

@ -159,7 +159,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "wipe_tower_rotation_angle") {
steps.emplace_back(psSkirtBrim);
} else if (
opt_key == "nozzle_diameter"
opt_key == "first_layer_height"
|| opt_key == "nozzle_diameter"
|| opt_key == "resolution"
// Spiral Vase forces different kind of slicing than the normal model:
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.

View File

@ -271,7 +271,11 @@ public:
// Centering offset of the sliced mesh from the scaled and rotated mesh of the model.
const Point& center_offset() const { return m_center_offset; }
bool has_brim() const { return this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.; }
bool has_brim() const {
return this->config().brim_type != btNoBrim
&& this->config().brim_width.value > 0.
&& ! this->has_raft();
}
// This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id
@ -321,7 +325,7 @@ public:
bool has_raft() const { return m_config.raft_layers > 0; }
bool has_support_material() const { return this->has_support() || this->has_raft(); }
// Checks if the model object is painted using the multi-material painting gizmo.
bool is_mm_painted() const { return this->model_object()->is_mm_painted(); };
bool is_mm_painted() const { return this->model_object()->is_mm_painted(); }
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
std::vector<unsigned int> object_extruders() const;

View File

@ -232,6 +232,16 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("elefant_foot_compensation", coFloat);
def->label = L("Elephant foot compensation");
def->category = L("Advanced");
def->tooltip = L("The first layer will be shrunk in the XY plane by the configured value "
"to compensate for the 1st layer squish aka an Elephant Foot effect.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("thumbnails", coPoints);
def->label = L("G-code thumbnails");
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\"");
@ -264,6 +274,7 @@ void PrintConfigDef::init_common_params()
"Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL "
"in the following format: https://username:password@your-octopi-address/");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_apikey", coString);
@ -271,6 +282,7 @@ void PrintConfigDef::init_common_params()
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
"the API Key or the password required for authentication.");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_port", coString);
@ -278,6 +290,7 @@ void PrintConfigDef::init_common_params()
def->tooltip = L("Name of the printer");
def->gui_type = ConfigOptionDef::GUIType::select_open;
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_cafile", coString);
@ -285,44 +298,40 @@ void PrintConfigDef::init_common_params()
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->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("elefant_foot_compensation", coFloat);
def->label = L("Elephant foot compensation");
def->category = L("Advanced");
def->tooltip = L("The first layer will be shrunk in the XY plane by the configured value "
"to compensate for the 1st layer squish aka an Elephant Foot effect.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
// Options used by physical printers
def = this->add("printhost_user", coString);
def->label = L("User");
// def->tooltip = L("");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_password", coString);
def->label = L("Password");
// def->tooltip = L("");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
// Only available on Windows.
def = this->add("printhost_ssl_ignore_revoke", coBool);
def->label = L("Ignore HTTPS certificate revocation checks");
def->tooltip = L("Ignore HTTPS certificate revocation checks in case of missing or offline distribution points. "
"One may want to enable this option for self signed certificates if connection fails.");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("preset_names", coStrings);
def->label = L("Printer preset names");
def->tooltip = L("Names of presets related to the physical printer");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings());
// temporary workaround for compatibility with older Slicer
{
def = this->add("preset_name", coString);
def->set_default_value(new ConfigOptionString());
}
def = this->add("printhost_authorization_type", coEnum);
def->label = L("Authorization Type");
// def->tooltip = L("");
@ -332,7 +341,14 @@ void PrintConfigDef::init_common_params()
def->enum_labels.push_back(L("API key"));
def->enum_labels.push_back(L("HTTP digest"));
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum<AuthorizationType>(atKeyPassword));
// temporary workaround for compatibility with older Slicer
{
def = this->add("preset_name", coString);
def->set_default_value(new ConfigOptionString());
}
}
void PrintConfigDef::init_fff_params()
@ -465,7 +481,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_width", coFloat);
def->label = L("Brim width");
def->category = L("Skirt and brim");
def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer.");
def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer."
"When raft is used, no brim is generated (use raft_first_layer_expansion).");
def->sidetext = L("mm");
def->min = 0;
def->max = 200;
@ -1809,6 +1826,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back("AstroBox");
def->enum_labels.push_back("Repetier");
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionEnum<PrintHostType>(htOctoPrint));
def = this->add("only_retract_when_crossing_perimeters", coBool);

View File

@ -535,7 +535,6 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posPerimeters);
} else if (
opt_key == "layer_height"
|| opt_key == "first_layer_height"
|| opt_key == "mmu_segmented_region_max_width"
|| opt_key == "raft_layers"
|| opt_key == "raft_contact_distance"

View File

@ -595,7 +595,7 @@ bool GLVolume::is_sinking() const
bool GLVolume::is_below_printbed() const
{
return transformed_convex_hull_bounding_box().max(2) < 0.0;
return transformed_convex_hull_bounding_box().max.z() < 0.0;
}
#if ENABLE_SINKING_CONTOURS

View File

@ -87,6 +87,11 @@
#include <boost/nowide/fstream.hpp>
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
// Needed for forcing menu icons back under gtk2 and gtk3
#if defined(__WXGTK20__) || defined(__WXGTK3__)
#include <gtk/gtk.h>
#endif
namespace Slic3r {
namespace GUI {
@ -799,6 +804,14 @@ bool GUI_App::OnInit()
bool GUI_App::on_init_inner()
{
// Forcing back menu icons under gtk2 and gtk3. Solution is based on:
// https://docs.gtk.org/gtk3/class.Settings.html
// see also https://docs.wxwidgets.org/3.0/classwx_menu_item.html#a2b5d6bcb820b992b1e4709facbf6d4fb
// TODO: Find workaround for GTK4
#if defined(__WXGTK20__) || defined(__WXGTK3__)
g_object_set (gtk_settings_get_default (), "gtk-menu-images", TRUE, NULL);
#endif
// Verify resources path
const wxString resources_dir = from_u8(Slic3r::resources_dir());
wxCHECK_MSG(wxDirExists(resources_dir), false,

View File

@ -1049,7 +1049,7 @@ void ObjectList::key_event(wxKeyEvent& event)
|| event.GetKeyCode() == WXK_BACK
#endif //__WXOSX__
) {
remove();
wxGetApp().plater()->remove_selected();
}
else if (event.GetKeyCode() == WXK_F5)
wxGetApp().plater()->reload_all_from_disk();
@ -1920,16 +1920,15 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
if (vol->is_model_part())
++solid_cnt;
if (volume->is_model_part() && solid_cnt == 1) {
Slic3r::GUI::show_error(nullptr, _(L("From Object List You can't delete the last solid part from object.")));
Slic3r::GUI::show_error(nullptr, _L("From Object List You can't delete the last solid part from object."));
return false;
}
take_snapshot(_(L("Delete Subobject")));
take_snapshot(_L("Delete Subobject"));
object->delete_volume(idx);
if (object->volumes.size() == 1)
{
if (object->volumes.size() == 1) {
const auto last_volume = object->volumes[0];
if (!last_volume->config.empty()) {
object->config.apply(last_volume->config);
@ -1948,11 +1947,11 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
}
else if (type == itInstance) {
if (object->instances.size() == 1) {
Slic3r::GUI::show_error(nullptr, _(L("Last instance of an object cannot be deleted.")));
Slic3r::GUI::show_error(nullptr, _L("Last instance of an object cannot be deleted."));
return false;
}
take_snapshot(_(L("Delete Instance")));
take_snapshot(_L("Delete Instance"));
object->delete_instance(idx);
}
else

View File

@ -14,6 +14,31 @@
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <map>
#include <cereal/archives/binary.hpp>
#include <cereal/types/string.hpp>
#include <cereal/types/vector.hpp>
#define HINTS_CEREAL_VERSION 1
// structure for writing used hints into binary file with version
struct HintsCerealData
{
std::vector<std::string> my_data;
// cereal will supply the version automatically when loading or saving
// The version number comes from the CEREAL_CLASS_VERSION macro
template<class Archive>
void serialize(Archive& ar, std::uint32_t const version)
{
// You can choose different behaviors depending on the version
// This is useful if you need to support older variants of your codebase
// interacting with newer ones
if (version > HINTS_CEREAL_VERSION)
throw Slic3r::IOError("Version of hints.cereal is higher than current version.");
else
ar(my_data);
}
};
// version of used hints binary file
CEREAL_CLASS_VERSION(HintsCerealData, HINTS_CEREAL_VERSION);
namespace Slic3r {
namespace GUI {
@ -31,6 +56,41 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f
else
ImGui::PushStyleColor(idx, col);
}
void write_used_binary(const std::vector<std::string>& ids)
{
boost::filesystem::ofstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"), std::ios::binary);
cereal::BinaryOutputArchive archive(file);
HintsCerealData cd { ids };
try
{
archive(cd);
}
catch (const std::exception& ex)
{
BOOST_LOG_TRIVIAL(error) << "Failed to write to hints.cereal. " << ex.what();
}
}
void read_used_binary(std::vector<std::string>& ids)
{
boost::filesystem::ifstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"));
cereal::BinaryInputArchive archive(file);
HintsCerealData cd;
try
{
archive(cd);
}
catch (const std::exception& ex)
{
BOOST_LOG_TRIVIAL(error) << "Failed to load to hints.cereal. " << ex.what();
return;
}
ids = cd.my_data;
}
enum TagCheckResult
{
TagCheckAffirmative,
@ -180,16 +240,16 @@ void launch_browser_if_allowed(const std::string& url)
wxLaunchDefaultBrowser(url);
}
} //namespace
HintDatabase::~HintDatabase()
{
if (m_initialized) {
write_used_binary(m_used_ids);
}
}
void HintDatabase::init()
{
load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini"));
const AppConfig* app_config = wxGetApp().app_config;
m_hint_id = std::atoi(app_config->get("last_hint").c_str());
m_initialized = true;
}
void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
{
@ -210,15 +270,22 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
for (const auto& data : section.second) {
dict.emplace(data.first, data.second.data());
}
//unescaping and translating all texts and saving all data common for all hint types
// unique id string [hint:id] (trim "hint:")
std::string id_string = section.first.substr(5);
id_string = std::to_string(std::hash<std::string>{}(id_string));
// unescaping and translating all texts and saving all data common for all hint types
std::string fulltext;
std::string text1;
std::string hypertext_text;
std::string follow_text;
// tags
std::string disabled_tags;
std::string enabled_tags;
// optional link to documentation (accessed from button)
std::string documentation_link;
// randomized weighted order variables
size_t weight = 1;
bool was_displayed = is_used(id_string);
//unescape text1
unescape_string_cstyle(_utf8(dict["text"]), fulltext);
// replace <b> and </b> for imgui markers
@ -276,37 +343,41 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
documentation_link = dict["documentation_link"];
}
if (dict.find("weight") != dict.end()) {
weight = (size_t)std::max(1, std::atoi(dict["weight"].c_str()));
}
// create HintData
if (dict.find("hypertext_type") != dict.end()) {
//link to internet
if(dict["hypertext_type"] == "link") {
std::string hypertext_link = dict["hypertext_link"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(hypertext_link); } };
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [hypertext_link]() { launch_browser_if_allowed(hypertext_link); } };
m_loaded_hints.emplace_back(hint_data);
// highlight settings
} else if (dict["hypertext_type"] == "settings") {
std::string opt = dict["hypertext_settings_opt"];
Preset::Type type = static_cast<Preset::Type>(std::atoi(dict["hypertext_settings_type"].c_str()));
std::wstring category = boost::nowide::widen(dict["hypertext_settings_category"]);
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } };
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [opt, type, category]() { GUI::wxGetApp().sidebar().jump_to_option(opt, type, category); } };
m_loaded_hints.emplace_back(hint_data);
// open preferences
} else if(dict["hypertext_type"] == "preferences") {
int page = static_cast<Preset::Type>(std::atoi(dict["hypertext_preferences_page"].c_str()));
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } };
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, [page]() { wxGetApp().open_preferences(page); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "plater") {
std::string item = dict["hypertext_plater_item"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } };
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_toolbar_item(item); } };
m_loaded_hints.emplace_back(hint_data);
} else if (dict["hypertext_type"] == "gizmo") {
std::string item = dict["hypertext_gizmo_item"];
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } };
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [item]() { wxGetApp().plater()->canvas3D()->highlight_gizmo(item); } };
m_loaded_hints.emplace_back(hint_data);
}
else if (dict["hypertext_type"] == "gallery") {
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() {
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link, []() {
// Deselect all objects, otherwise gallery wont show.
wxGetApp().plater()->canvas3D()->deselect_all();
wxGetApp().obj_list()->load_shape_object_from_gallery(); } };
@ -314,17 +385,17 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
}
} else {
// plain text without hypertext
HintData hint_data{ text1, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link };
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, false, documentation_link };
m_loaded_hints.emplace_back(hint_data);
}
}
}
}
HintData* HintDatabase::get_hint(bool up)
HintData* HintDatabase::get_hint(bool new_hint/* = true*/)
{
if (! m_initialized) {
init();
//return false;
new_hint = true;
}
if (m_loaded_hints.empty())
{
@ -332,23 +403,97 @@ HintData* HintDatabase::get_hint(bool up)
return nullptr;
}
// shift id
m_hint_id = (up ? m_hint_id + 1 : m_hint_id );
m_hint_id %= m_loaded_hints.size();
try
{
if (new_hint)
m_hint_id = get_next();
}
catch (const std::exception&)
{
return nullptr;
}
AppConfig* app_config = wxGetApp().app_config;
app_config->set("last_hint", std::to_string(m_hint_id));
//data = &m_loaded_hints[m_hint_id];
/*
data.text = m_loaded_hints[m_hint_id].text;
data.hypertext = m_loaded_hints[m_hint_id].hypertext;
data.follow_text = m_loaded_hints[m_hint_id].follow_text;
data.callback = m_loaded_hints[m_hint_id].callback;
*/
return &m_loaded_hints[m_hint_id];
}
size_t HintDatabase::get_next()
{
if (!m_sorted_hints)
{
auto compare_wieght = [](const HintData& a, const HintData& b){ return a.weight < b.weight; };
std::sort(m_loaded_hints.begin(), m_loaded_hints.end(), compare_wieght);
m_sorted_hints = true;
srand(time(NULL));
}
std::vector<size_t> candidates; // index in m_loaded_hints
// total weight
size_t total_weight = 0;
for (size_t i = 0; i < m_loaded_hints.size(); i++) {
if (!m_loaded_hints[i].was_displayed && tags_check(m_loaded_hints[i].disabled_tags, m_loaded_hints[i].enabled_tags)) {
candidates.emplace_back(i);
total_weight += m_loaded_hints[i].weight;
}
}
// all were shown
if (total_weight == 0) {
clear_used();
for (size_t i = 0; i < m_loaded_hints.size(); i++) {
m_loaded_hints[i].was_displayed = false;
if (tags_check(m_loaded_hints[i].disabled_tags, m_loaded_hints[i].enabled_tags)) {
candidates.emplace_back(i);
total_weight += m_loaded_hints[i].weight;
}
}
}
if (total_weight == 0) {
BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed. No suitable hint was found.";
throw std::exception();
}
size_t random_number = rand() % total_weight + 1;
size_t current_weight = 0;
for (size_t i = 0; i < candidates.size(); i++) {
current_weight += m_loaded_hints[candidates[i]].weight;
if (random_number <= current_weight) {
set_used(m_loaded_hints[candidates[i]].id_string);
m_loaded_hints[candidates[i]].was_displayed = true;
return candidates[i];
}
}
BOOST_LOG_TRIVIAL(error) << "Hint notification random number generator failed.";
throw std::exception();
}
bool HintDatabase::is_used(const std::string& id)
{
// load used ids from file
if (!m_used_ids_loaded) {
read_used_binary(m_used_ids);
m_used_ids_loaded = true;
}
// check if id is in used
for (const std::string& used_id : m_used_ids) {
if (used_id == id)
{
return true;
}
}
return false;
}
void HintDatabase::set_used(const std::string& id)
{
// check needed?
if (!is_used(id))
{
m_used_ids.emplace_back(id);
}
}
void HintDatabase::clear_used()
{
m_used_ids.clear();
}
void NotificationManager::HintNotification::count_spaces()
{
//determine line width
@ -844,23 +989,12 @@ void NotificationManager::HintNotification::open_documentation()
launch_browser_if_allowed(m_documentation_link);
}
}
void NotificationManager::HintNotification::retrieve_data(int recursion_counter)
void NotificationManager::HintNotification::retrieve_data(bool new_hint/* = true*/)
{
HintData* hint_data = HintDatabase::get_instance().get_hint(recursion_counter >= 0 ? true : false);
HintData* hint_data = HintDatabase::get_instance().get_hint(new_hint);
if (hint_data == nullptr)
close();
if (hint_data != nullptr && !tags_check(hint_data->disabled_tags, hint_data->enabled_tags))
{
// Content for different user - retrieve another
size_t count = HintDatabase::get_instance().get_count();
if ((int)count < recursion_counter) {
BOOST_LOG_TRIVIAL(error) << "Hint notification failed to load data due to recursion counter.";
} else {
retrieve_data(recursion_counter + 1);
}
return;
}
if(hint_data != nullptr)
{
NotificationData nd { NotificationType::DidYouKnowHint,

View File

@ -9,7 +9,10 @@ namespace GUI {
// Database of hints updatable
struct HintData
{
std::string id_string;
std::string text;
size_t weight;
bool was_displayed;
std::string hypertext;
std::string follow_text;
std::string disabled_tags;
@ -33,11 +36,12 @@ private:
: m_hint_id(0)
{}
public:
~HintDatabase();
HintDatabase(HintDatabase const&) = delete;
void operator=(HintDatabase const&) = delete;
// return true if HintData filled;
HintData* get_hint(bool up = true);
HintData* get_hint(bool new_hint = true);
size_t get_count() {
if (!m_initialized)
return 0;
@ -46,10 +50,17 @@ public:
private:
void init();
void load_hints_from_file(const boost::filesystem::path& path);
bool is_used(const std::string& id);
void set_used(const std::string& id);
void clear_used();
// Returns position in m_loaded_hints with next hint chosed randomly with weights
size_t get_next();
size_t m_hint_id;
bool m_initialized { false };
std::vector<HintData> m_loaded_hints;
bool m_sorted_hints { false };
std::vector<std::string> m_used_ids;
bool m_used_ids_loaded { false };
};
// Notification class - shows current Hint ("Did you know")
class NotificationManager::HintNotification : public NotificationManager::PopNotification
@ -58,10 +69,10 @@ public:
HintNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool new_hint)
: PopNotification(n, id_provider, evt_handler)
{
retrieve_data(new_hint ? 0 : -1);
retrieve_data(new_hint);
}
virtual void init() override;
void open_next() { retrieve_data(0); }
void open_next() { retrieve_data(); }
protected:
virtual void set_next_window_size(ImGuiWrapper& imgui) override;
virtual void count_spaces() override;
@ -87,7 +98,7 @@ protected:
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
// recursion counter -1 tells to retrieve same hint as last time
void retrieve_data(int recursion_counter = 0);
void retrieve_data(bool new_hint = true);
void open_documentation();
bool m_has_hint_data { false };

View File

@ -147,6 +147,11 @@ void FillBedJob::finalize()
size_t inst_cnt = model_object->instances.size();
int added_cnt = std::accumulate(m_selected.begin(), m_selected.end(), 0, [](int s, auto &ap) {
return s + int(ap.priority == 0 && ap.bed_idx == 0);
});
if (added_cnt > 0) {
for (ArrangePolygon &ap : m_selected) {
if (ap.bed_idx != arrangement::UNARRANGED && (ap.priority != 0 || ap.bed_idx == 0))
ap.apply();
@ -156,17 +161,14 @@ void FillBedJob::finalize()
m_plater->update();
int added_cnt = std::accumulate(m_selected.begin(), m_selected.end(), 0,
[](int s, auto &ap) {
return s + int(ap.priority == 0 && ap.bed_idx == 0);
});
// FIXME: somebody explain why this is needed for increase_object_instances
if (inst_cnt == 1) added_cnt++;
if (added_cnt > 0)
m_plater->sidebar()
.obj_list()->increase_object_instances(m_object_idx, size_t(added_cnt));
}
Job::finalize();
}
}} // namespace Slic3r::GUI

View File

@ -545,7 +545,6 @@ private:
NotificationType::PlaterWarning,
NotificationType::ProgressBar,
NotificationType::PrintHostUpload,
NotificationType::UpdatedItemsInfo,
NotificationType::SimplifySuggestion
};
//prepared (basic) notifications

View File

@ -396,6 +396,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
m_optgroup->append_line(cafile_hint);
}
else {
Line line{ "", "" };
line.full_width = 1;
@ -411,7 +412,6 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
sizer->Add(txt, 1, wxEXPAND);
return sizer;
};
m_optgroup->append_line(line);
}
@ -421,6 +421,12 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
m_optgroup->append_single_option_line(option);
}
#ifdef WIN32
option = m_optgroup->get_option("printhost_ssl_ignore_revoke");
option.opt.width = Field::def_width_wider();
m_optgroup->append_single_option_line(option);
#endif
m_optgroup->activate();
Field* printhost_field = m_optgroup->get_field("print_host");

View File

@ -1643,7 +1643,7 @@ struct Plater::priv
BoundingBox scaled_bed_shape_bb() const;
std::vector<size_t> load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config, bool used_inches = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false, bool force_center_on_bed = false);
std::vector<size_t> load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z = false);
wxString get_export_file(GUI::FileType file_type);
@ -1658,6 +1658,7 @@ struct Plater::priv
void deselect_all();
void remove(size_t obj_idx);
void delete_object_from_model(size_t obj_idx);
void delete_all_objects_from_model();
void reset();
void mirror(Axis axis);
void split_object();
@ -1944,7 +1945,8 @@ 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, [q](SimpleEvent&) { q->reset_with_confirm(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { delete_all_objects_from_model(); });
// view3D_canvas->Bind(EVT_GLTOOLBAR_DELETE_ALL, [q](SimpleEvent&) { q->reset_with_confirm(); });
view3D_canvas->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { this->q->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(); });
@ -2426,7 +2428,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
if (one_by_one) {
auto loaded_idxs = load_model_objects(model.objects, is_project_file, !is_project_file);
if (type_3mf && !is_project_file)
model.center_instances_around_point(bed_shape_bb().center());
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
} else {
// This must be an .stl or .obj file, which may contain a maximum of one volume.
@ -2481,7 +2485,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// #define AUTOPLACEMENT_ON_LOAD
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z, bool force_center_on_bed)
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& model_objects, bool allow_negative_z)
{
const BoundingBoxf bed_shape = bed_shape_bb();
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0) - 2.0 * Vec3d::Ones();
@ -2541,9 +2545,6 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
object->ensure_on_bed(allow_negative_z);
}
if (force_center_on_bed)
model.center_instances_around_point(bed_shape.center());
#ifdef AUTOPLACEMENT_ON_LOAD
// FIXME distance should be a config value /////////////////////////////////
auto min_obj_distance = static_cast<coord_t>(6/SCALING_FACTOR);
@ -2756,6 +2757,32 @@ void Plater::priv::delete_object_from_model(size_t obj_idx)
object_list_changed();
}
void Plater::priv::delete_all_objects_from_model()
{
Plater::TakeSnapshot snapshot(q, _L("Delete All Objects"));
if (view3D->is_layers_editing_enabled())
view3D->enable_layers_editing(false);
reset_gcode_toolpaths();
gcode_result.reset();
view3D->get_canvas3d()->reset_sequential_print_clearance();
// Stop and reset the Print content.
background_process.reset();
model.clear_objects();
update();
// Delete object from Sidebar list. Do it after update, so that the GLScene selection is updated with the modified model.
sidebar->obj_list()->delete_all_objects_from_list();
object_list_changed();
// The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here
sidebar->show_sliced_info_sizer(false);
model.custom_gcode_per_print_z.gcodes.clear();
}
void Plater::priv::reset()
{
Plater::TakeSnapshot snapshot(q, _L("Reset Project"));
@ -2947,6 +2974,9 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
if (view3D->is_layers_editing_enabled())
view3D->get_wxglcanvas()->Refresh();
if (background_process.empty())
view3D->get_canvas3d()->reset_sequential_print_clearance();
if (invalidated == Print::APPLY_STATUS_INVALIDATED) {
// Some previously calculated data on the Print was invalidated.
// Hide the slicing results, as the current slicing status is no more valid.
@ -4759,10 +4789,8 @@ void Plater::load_project(const wxString& filename)
std::vector<fs::path> input_paths;
input_paths.push_back(into_path(filename));
std::vector<size_t> res = load_files(input_paths);
// if res is empty no data has been loaded
if (!res.empty()) {
if (! load_files(input_paths).empty()) {
// At least one file was loaded.
p->set_project_filename(filename);
reset_project_dirty_initial_presets();
update_project_dirty_from_presets();
@ -4797,8 +4825,7 @@ void Plater::add_model(bool imperial_units/* = false*/)
}
Plater::TakeSnapshot snapshot(this, snapshot_label);
std::vector<size_t> res = load_files(paths, true, false, imperial_units);
if (!res.empty())
if (! load_files(paths, true, false, imperial_units).empty())
wxGetApp().mainframe->update_title();
}
@ -4933,7 +4960,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string& filename)
_L("Select an action to apply to the file") + ": " + from_u8(filename)), 0, wxEXPAND | wxALL, 10);
m_action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
static_cast<int>(LoadType::OpenProject) - 1, static_cast<int>(LoadType::LoadConfig)) - 1;
static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)) - 1;
wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action"));
if (!wxOSX) action_stb->SetBackgroundStyle(wxBG_STYLE_PAINT);
@ -6228,8 +6255,7 @@ void Plater::changed_object(int obj_idx)
if (obj_idx < 0)
return;
// recenter and re - align to Z = 0
auto model_object = p->model.objects[obj_idx];
model_object->ensure_on_bed(this->p->printer_technology != ptSLA);
p->model.objects[obj_idx]->ensure_on_bed(p->printer_technology != ptSLA);
if (this->p->printer_technology == ptSLA) {
// Update the SLAPrint from the current Model, so that the reload_scene()
// pulls the correct data, update the 3D scene.

View File

@ -28,7 +28,7 @@ static const UndoRedo::Snapshot* get_active_snapshot(const UndoRedo::Stack& stac
const size_t active_snapshot_time = stack.active_snapshot_time();
const auto it = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(active_snapshot_time));
const int idx = it - snapshots.begin() - 1;
const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && (size_t)idx < snapshots.size() - 1) ?
const Slic3r::UndoRedo::Snapshot* ret = (0 <= idx && idx < int(snapshots.size()) - 1) ?
&snapshots[idx] : nullptr;
assert(ret != nullptr);
@ -195,8 +195,7 @@ void ProjectDirtyStateManager::update_from_undo_redo_stack(UpdateType type)
void ProjectDirtyStateManager::update_from_presets()
{
m_state.presets = false;
std::vector<std::pair<unsigned int, std::string>> selected_presets = wxGetApp().get_selected_presets();
for (const auto& [type, name] : selected_presets) {
for (const auto& [type, name] : wxGetApp().get_selected_presets()) {
m_state.presets |= !m_initial_presets[type].empty() && m_initial_presets[type] != name;
}
m_state.presets |= wxGetApp().has_unsaved_preset_changes();
@ -214,6 +213,7 @@ void ProjectDirtyStateManager::reset_after_save()
m_last_save.main = (saveable_snapshot != nullptr) ? saveable_snapshot->timestamp : 0;
}
else {
// Gizmo is active with its own Undo / Redo stack (for example the SLA support point editing gizmo).
const UndoRedo::Snapshot* main_active_snapshot = get_active_snapshot(main_stack);
if (boost::starts_with(main_active_snapshot->name, _utf8("Entering"))) {
if (m_state.gizmos.current)
@ -231,8 +231,7 @@ void ProjectDirtyStateManager::reset_after_save()
void ProjectDirtyStateManager::reset_initial_presets()
{
m_initial_presets = std::array<std::string, Preset::TYPE_COUNT>();
std::vector<std::pair<unsigned int, std::string>> selected_presets = wxGetApp().get_selected_presets();
for (const auto& [type, name] : selected_presets) {
for (const auto& [type, name] : wxGetApp().get_selected_presets()) {
m_initial_presets[type] = name;
}
}

View File

@ -683,7 +683,8 @@ void Selection::translate(const Vec3d& displacement, bool local)
synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH
this->set_bounding_boxes_dirty();
ensure_not_below_bed();
set_bounding_boxes_dirty();
}
// Rotate an object around one of the axes. Only one rotation component is expected to be changing.
@ -1148,6 +1149,7 @@ void Selection::erase()
}
wxGetApp().obj_list()->delete_from_model_and_list(items);
ensure_not_below_bed();
}
}
@ -1712,7 +1714,7 @@ void Selection::calc_unscaled_instance_bounding_box() const
if (volume.is_modifier)
continue;
Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, true, false) * volume.get_volume_transformation().get_matrix();
trafo.translation()(2) += volume.get_sla_shift_z();
trafo.translation().z() += volume.get_sla_shift_z();
unscaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
@ -1729,7 +1731,7 @@ void Selection::calc_scaled_instance_bounding_box() const
if (volume.is_modifier)
continue;
Transform3d trafo = volume.get_instance_transformation().get_matrix(false, false, false, false) * volume.get_volume_transformation().get_matrix();
trafo.translation()(2) += volume.get_sla_shift_z();
trafo.translation().z() += volume.get_sla_shift_z();
scaled_instance_bounding_box->merge(volume.transformed_convex_hull_bounding_box(trafo));
}
}
@ -2134,6 +2136,32 @@ void Selection::ensure_on_bed()
}
}
void Selection::ensure_not_below_bed()
{
typedef std::map<std::pair<int, int>, double> InstancesToZMap;
InstancesToZMap instances_max_z;
for (size_t i = 0; i < m_volumes->size(); ++i) {
GLVolume* volume = (*m_volumes)[i];
if (!volume->is_wipe_tower && !volume->is_modifier) {
const double max_z = volume->transformed_convex_hull_bounding_box().max.z();
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_max_z.find(instance);
if (it == instances_max_z.end())
it = instances_max_z.insert(InstancesToZMap::value_type(instance, -DBL_MAX)).first;
it->second = std::max(it->second, max_z);
}
}
for (GLVolume* volume : *m_volumes) {
std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx());
InstancesToZMap::iterator it = instances_max_z.find(instance);
if (it != instances_max_z.end() && it->second < SINKING_MIN_Z_THRESHOLD)
volume->set_instance_offset(Z, volume->get_instance_offset(Z) + SINKING_MIN_Z_THRESHOLD - it->second);
}
}
bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
{
struct SameInstance

View File

@ -385,6 +385,7 @@ public:
private:
void ensure_on_bed();
void ensure_not_below_bed();
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
void paste_volumes_from_clipboard();

View File

@ -491,6 +491,18 @@ Http& Http::form_add_file(const std::string &name, const fs::path &path, const s
return *this;
}
#ifdef WIN32
// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present.
// This option is only supported for Schannel (the native Windows SSL library).
Http& Http::ssl_revoke_best_effort(bool set)
{
if(p && set){
::curl_easy_setopt(p->curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
}
return *this;
}
#endif // WIN32
Http& Http::set_post_body(const fs::path &path)
{
if (p) { p->set_post_body(path);}

View File

@ -80,6 +80,12 @@ public:
// Same as above except also override the file's filename with a custom one
Http& form_add_file(const std::string &name, const boost::filesystem::path &path, const std::string &filename);
#ifdef WIN32
// Tells libcurl to ignore certificate revocation checks in case of missing or offline distribution points for those SSL backends where such behavior is present.
// This option is only supported for Schannel (the native Windows SSL library).
Http& ssl_revoke_best_effort(bool set);
#endif // WIN32
// Set the file contents as a POST request body.
// The data is used verbatim, it is not additionally encoded in any way.
// This can be used for hosts which do not support multipart requests.

View File

@ -23,9 +23,10 @@ namespace pt = boost::property_tree;
namespace Slic3r {
OctoPrint::OctoPrint(DynamicPrintConfig *config) :
host(config->opt_string("print_host")),
apikey(config->opt_string("printhost_apikey")),
cafile(config->opt_string("printhost_cafile"))
m_host(config->opt_string("print_host")),
m_apikey(config->opt_string("printhost_apikey")),
m_cafile(config->opt_string("printhost_cafile")),
m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke"))
{}
const char* OctoPrint::get_name() const { return "OctoPrint"; }
@ -73,6 +74,9 @@ bool OctoPrint::test(wxString &msg) const
msg = "Could not parse server response";
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
#endif
.perform_sync();
return res;
@ -137,6 +141,9 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
res = false;
}
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
#endif
.perform_sync();
return res;
@ -149,31 +156,31 @@ bool OctoPrint::validate_version_text(const boost::optional<std::string> &versio
void OctoPrint::set_auth(Http &http) const
{
http.header("X-Api-Key", apikey);
http.header("X-Api-Key", m_apikey);
if (! cafile.empty()) {
http.ca_file(cafile);
if (!m_cafile.empty()) {
http.ca_file(m_cafile);
}
}
std::string OctoPrint::make_url(const std::string &path) const
{
if (host.find("http://") == 0 || host.find("https://") == 0) {
if (host.back() == '/') {
return (boost::format("%1%%2%") % host % path).str();
if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
if (m_host.back() == '/') {
return (boost::format("%1%%2%") % m_host % path).str();
} else {
return (boost::format("%1%/%2%") % host % path).str();
return (boost::format("%1%/%2%") % m_host % path).str();
}
} else {
return (boost::format("http://%1%/%2%") % host % path).str();
return (boost::format("http://%1%/%2%") % m_host % path).str();
}
}
SL1Host::SL1Host(DynamicPrintConfig *config) :
OctoPrint(config),
authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
username(config->opt_string("printhost_user")),
password(config->opt_string("printhost_password"))
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
m_username(config->opt_string("printhost_user")),
m_password(config->opt_string("printhost_password"))
{
}
@ -199,12 +206,12 @@ bool SL1Host::validate_version_text(const boost::optional<std::string> &version_
void SL1Host::set_auth(Http &http) const
{
switch (authorization_type) {
switch (m_authorization_type) {
case atKeyPassword:
http.header("X-Api-Key", get_apikey());
break;
case atUserPassword:
http.auth_digest(username, password);
http.auth_digest(m_username, m_password);
break;
}
@ -216,9 +223,9 @@ void SL1Host::set_auth(Http &http) const
// PrusaLink
PrusaLink::PrusaLink(DynamicPrintConfig* config) :
OctoPrint(config),
authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
username(config->opt_string("printhost_user")),
password(config->opt_string("printhost_password"))
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
m_username(config->opt_string("printhost_user")),
m_password(config->opt_string("printhost_password"))
{
}
@ -243,12 +250,12 @@ bool PrusaLink::validate_version_text(const boost::optional<std::string>& versio
void PrusaLink::set_auth(Http& http) const
{
switch (authorization_type) {
switch (m_authorization_type) {
case atKeyPassword:
http.header("X-Api-Key", get_apikey());
break;
case atUserPassword:
http.auth_digest(username, password);
http.auth_digest(m_username, m_password);
break;
}

View File

@ -29,17 +29,18 @@ public:
bool has_auto_discovery() const override { return true; }
bool can_test() const override { return true; }
bool can_start_print() const override { return true; }
std::string get_host() const override { return host; }
const std::string& get_apikey() const { return apikey; }
const std::string& get_cafile() const { return cafile; }
std::string get_host() const override { return m_host; }
const std::string& get_apikey() const { return m_apikey; }
const std::string& get_cafile() const { return m_cafile; }
protected:
virtual bool validate_version_text(const boost::optional<std::string> &version_text) const;
private:
std::string host;
std::string apikey;
std::string cafile;
std::string m_host;
std::string m_apikey;
std::string m_cafile;
bool m_ssl_revoke_best_effort;
virtual void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
@ -64,10 +65,10 @@ private:
void set_auth(Http &http) const override;
// Host authorization type.
AuthorizationType authorization_type;
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string username;
std::string password;
std::string m_username;
std::string m_password;
};
class PrusaLink : public OctoPrint
@ -89,10 +90,10 @@ private:
void set_auth(Http& http) const override;
// Host authorization type.
AuthorizationType authorization_type;
AuthorizationType m_authorization_type;
// username and password for HTTP Digest Authentization (RFC RFC2617)
std::string username;
std::string password;
std::string m_username;
std::string m_password;
};
}