Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-08-26 12:47:32 +02:00
commit 526700db76
89 changed files with 2002 additions and 840 deletions

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)

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(Shiny)
cmake_minimum_required(VERSION 2.6)
add_library(Shiny STATIC
Shiny.h

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(admesh)
cmake_minimum_required(VERSION 2.6)
add_library(admesh STATIC
connect.cpp

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(nowide)
cmake_minimum_required(VERSION 2.6)
add_library(nowide STATIC
nowide/args.hpp

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(clipper)
cmake_minimum_required(VERSION 2.6)
add_library(clipper STATIC
# We are using ClipperLib compiled as part of the libslic3r project using Slic3r::Point as its base type.

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(glu-libtess)
cmake_minimum_required(VERSION 2.6)
add_library(glu-libtess STATIC
src/dict-list.h

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

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(imgui)
cmake_minimum_required(VERSION 2.6)
add_library(imgui STATIC
imconfig.h

View file

@ -1,5 +1,5 @@
project(libigl)
cmake_minimum_required(VERSION 3.0)
project(libigl)
add_library(libigl INTERFACE)

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

@ -426,7 +426,19 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
}
}
// this will *ignore* options not present in both configs
// Are the two configs equal? Ignoring options not present in both configs.
bool ConfigBase::equals(const ConfigBase &other) const
{
for (const t_config_option_key &opt_key : this->keys()) {
const ConfigOption *this_opt = this->option(opt_key);
const ConfigOption *other_opt = other.option(opt_key);
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
return false;
}
return true;
}
// Returns options differing in the two configs, ignoring options not present in both configs.
t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
{
t_config_option_keys diff;
@ -439,6 +451,7 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
return diff;
}
// Returns options being equal in the two configs, ignoring options not present in both configs.
t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
{
t_config_option_keys equal;
@ -1190,6 +1203,65 @@ t_config_option_keys StaticConfig::keys() const
return keys;
}
// Iterate over the pairs of options with equal keys, call the fn.
// Returns true on early exit by fn().
template<typename Fn>
static inline bool dynamic_config_iterate(const DynamicConfig &lhs, const DynamicConfig &rhs, Fn fn)
{
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator i = lhs.cbegin();
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator j = rhs.cbegin();
while (i != lhs.cend() && j != rhs.cend())
if (i->first < j->first)
++ i;
else if (i->first > j->first)
++ j;
else {
assert(i->first == j->first);
if (fn(i->first, i->second.get(), j->second.get()))
// Early exit by fn.
return true;
++ i;
++ j;
}
// Finished to the end.
return false;
}
// Are the two configs equal? Ignoring options not present in both configs.
bool DynamicConfig::equals(const DynamicConfig &other) const
{
return ! dynamic_config_iterate(*this, other,
[](const t_config_option_key & /* key */, const ConfigOption *l, const ConfigOption *r) { return *l != *r; });
}
// Returns options differing in the two configs, ignoring options not present in both configs.
t_config_option_keys DynamicConfig::diff(const DynamicConfig &other) const
{
t_config_option_keys diff;
dynamic_config_iterate(*this, other,
[&diff](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
if (*l != *r)
diff.emplace_back(key);
// Continue iterating.
return false;
});
return diff;
}
// Returns options being equal in the two configs, ignoring options not present in both configs.
t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const
{
t_config_option_keys equal;
dynamic_config_iterate(*this, other,
[&equal](const t_config_option_key &key, const ConfigOption *l, const ConfigOption *r) {
if (*l == *r)
equal.emplace_back(key);
// Continue iterating.
return false;
});
return equal;
}
}
#include <cereal/types/polymorphic.hpp>

View file

@ -1893,8 +1893,8 @@ public:
// The configuration definition is static: It does not carry the actual configuration values,
// but it carries the defaults of the configuration values.
ConfigBase() {}
~ConfigBase() override {}
ConfigBase() = default;
~ConfigBase() override = default;
// Virtual overridables:
public:
@ -1953,8 +1953,11 @@ public:
// An UnknownOptionException is thrown in case some option keys are not defined by this->def(),
// or this ConfigBase is of a StaticConfig type and it does not support some of the keys, and ignore_nonexistent is not set.
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
// Are the two configs equal? Ignoring options not present in both configs.
bool equals(const ConfigBase &other) const;
// Returns options differing in the two configs, ignoring options not present in both configs.
t_config_option_keys diff(const ConfigBase &other) const;
// Returns options being equal in the two configs, ignoring options not present in both configs.
t_config_option_keys equal(const ConfigBase &other) const;
std::string opt_serialize(const t_config_option_key &opt_key) const;
@ -2022,12 +2025,12 @@ private:
class DynamicConfig : public virtual ConfigBase
{
public:
DynamicConfig() {}
DynamicConfig() = default;
DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); }
explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
virtual ~DynamicConfig() override { clear(); }
virtual ~DynamicConfig() override = default;
// Copy a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def().
@ -2144,6 +2147,13 @@ public:
}
}
// Are the two configs equal? Ignoring options not present in both configs.
bool equals(const DynamicConfig &other) const;
// Returns options differing in the two configs, ignoring options not present in both configs.
t_config_option_keys diff(const DynamicConfig &other) const;
// Returns options being equal in the two configs, ignoring options not present in both configs.
t_config_option_keys equal(const DynamicConfig &other) const;
std::string& opt_string(const t_config_option_key &opt_key, bool create = false) { return this->option<ConfigOptionString>(opt_key, create)->value; }
const std::string& opt_string(const t_config_option_key &opt_key) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key); }
std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionStrings>(opt_key)->get_at(idx); }

View file

@ -595,7 +595,7 @@ namespace Slic3r {
mz_zip_archive_file_stat stat;
m_name = boost::filesystem::path(filename).filename().stem().string();
m_name = boost::filesystem::path(filename).stem().string();
// we first loop the entries to read from the archive the .model file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i) {
@ -1408,6 +1408,13 @@ namespace Slic3r {
m_model->delete_object(model_object);
}
if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one object,
// set the object name to match the filename
if (m_model->objects.size() == 1)
m_model->objects.front()->name = m_name;
}
// applies instances' matrices
for (Instance& instance : m_instances) {
if (instance.instance != nullptr && instance.instance->get_object() != nullptr)

View file

@ -24,6 +24,9 @@
static const float INCHES_TO_MM = 25.4f;
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
#if ENABLE_RETRACT_ACCELERATION
static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
#endif // ENABLE_RETRACT_ACCELERATION
static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
static const size_t MIN_EXTRUDERS_COUNT = 5;
@ -178,6 +181,10 @@ void GCodeProcessor::TimeMachine::reset()
enabled = false;
acceleration = 0.0f;
max_acceleration = 0.0f;
#if ENABLE_RETRACT_ACCELERATION
retract_acceleration = 0.0f;
max_retract_acceleration = 0.0f;
#endif // ENABLE_RETRACT_ACCELERATION
travel_acceleration = 0.0f;
max_travel_acceleration = 0.0f;
extrude_factor_override_percentage = 1.0f;
@ -834,6 +841,11 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
#if ENABLE_RETRACT_ACCELERATION
float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
#endif // ENABLE_RETRACT_ACCELERATION
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@ -1052,6 +1064,11 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
m_time_processor.machines[i].max_acceleration = max_acceleration;
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
#if ENABLE_RETRACT_ACCELERATION
float max_retract_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i);
m_time_processor.machines[i].max_retract_acceleration = max_retract_acceleration;
m_time_processor.machines[i].retract_acceleration = (max_retract_acceleration > 0.0f) ? max_retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
#endif // ENABLE_RETRACT_ACCELERATION
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
@ -2713,14 +2730,22 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('T', value))
#if ENABLE_RETRACT_ACCELERATION
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
#else
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
#endif // ENABLE_RETRACT_ACCELERATION
}
else {
// New acceleration format, compatible with the upstream Marlin.
if (line.has_value('P', value))
set_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
if (line.has_value('R', value))
#if ENABLE_RETRACT_ACCELERATION
set_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
#else
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
#endif // ENABLE_RETRACT_ACCELERATION
if (line.has_value('T', value))
// Interpret the T value as the travel acceleration in the new Marlin format.
set_travel_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), value);
@ -2979,10 +3004,30 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
}
}
#if ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
size_t id = static_cast<size_t>(mode);
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].retract_acceleration : DEFAULT_RETRACT_ACCELERATION;
}
#else
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
return get_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, static_cast<size_t>(mode));
}
#endif // ENABLE_RETRACT_ACCELERATION
#if ENABLE_RETRACT_ACCELERATION
void GCodeProcessor::set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value)
{
size_t id = static_cast<size_t>(mode);
if (id < m_time_processor.machines.size()) {
m_time_processor.machines[id].retract_acceleration = (m_time_processor.machines[id].max_retract_acceleration == 0.0f) ? value :
// Clamp the acceleration with the maximum.
std::min(value, m_time_processor.machines[id].max_retract_acceleration);
}
}
#endif // ENABLE_RETRACT_ACCELERATION
float GCodeProcessor::get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{

View file

@ -242,6 +242,11 @@ namespace Slic3r {
float acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_acceleration; // mm/s^2
#if ENABLE_RETRACT_ACCELERATION
float retract_acceleration; // mm/s^2
// hard limit for the acceleration, to which the firmware will clamp.
float max_retract_acceleration; // mm/s^2
#endif // ENABLE_RETRACT_ACCELERATION
float travel_acceleration; // mm/s^2
// hard limit for the travel acceleration, to which the firmware will clamp.
float max_travel_acceleration; // mm/s^2
@ -669,6 +674,9 @@ namespace Slic3r {
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
#if ENABLE_RETRACT_ACCELERATION
void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
#endif // ENABLE_RETRACT_ACCELERATION
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_travel_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;

View file

@ -118,6 +118,11 @@ void triangle_mesh_to_cgal(const std::vector<stl_vertex> & V,
{
if (F.empty()) return;
size_t vertices_count = V.size();
size_t edges_count = (F.size()* 3) / 2;
size_t faces_count = F.size();
out.reserve(vertices_count, edges_count, faces_count);
for (auto &v : V)
out.add_vertex(typename _Mesh::Point{v.x(), v.y(), v.z()});

View file

@ -424,7 +424,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
object->name = this->objects.front()->name;
object->name = boost::filesystem::path(this->objects.front()->input_file).stem().string();
//FIXME copy the config etc?
unsigned int extruder_counter = 0;
@ -439,7 +439,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
int counter = 1;
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
assert(new_v != nullptr);
new_v->name = o->name + "_" + std::to_string(counter++);
new_v->name = (counter > 1) ? o->name + "_" + std::to_string(counter++) : o->name;
new_v->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
return new_v;
};
@ -956,9 +956,26 @@ 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 (parts_count() == 1) {
const double min_z = get_min_z();
const double max_z = get_max_z();
if (min_z >= SINKING_Z_THRESHOLD || max_z < 0.0)
z_offset = -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)
@ -1114,6 +1131,15 @@ size_t ModelObject::facets_count() const
return num;
}
size_t ModelObject::parts_count() const
{
size_t num = 0;
for (const ModelVolume* v : this->volumes)
if (v->is_model_part())
++num;
return num;
}
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
@ -1429,6 +1455,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 +1489,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

@ -347,6 +347,7 @@ public:
size_t materials_count() const;
size_t facets_count() const;
size_t parts_count() const;
bool needed_repair() const;
ModelObjectPtrs cut(size_t instance, coordf_t z, ModelObjectCutAttributes attributes);
void split(ModelObjectPtrs* new_objects);
@ -358,7 +359,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 +1180,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

@ -24,6 +24,7 @@ public:
PlaceholderParser(const DynamicConfig *external_config = nullptr);
void clear_config() { m_config.clear(); }
// Return a list of keys, which should be changed in m_config from rhs.
// This contains keys, which are found in rhs, but not in m_config.
std::vector<std::string> config_diff(const DynamicPrintConfig &rhs);

View file

@ -413,212 +413,181 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
}
}
const std::vector<std::string>& Preset::print_options()
{
static std::vector<std::string> s_opts {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "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",
"seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
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",
"seam_position", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",
"ironing", "ironing_type", "ironing_flowrate", "ironing_speed", "ironing_spacing",
"max_print_speed", "max_volumetric_speed", "avoid_crossing_perimeters_max_detour",
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_dist",
#ifdef HAS_PRESSURE_EQUALIZER
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
"max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative",
#endif /* HAS_PRESSURE_EQUALIZER */
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_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;
}
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "infill_speed", "solid_infill_speed",
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
"support_material_contact_distance", "support_material_bottom_contact_distance",
"support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder",
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio", "clip_multipart_objects",
"elefant_foot_compensation", "xy_size_compensation", "threads", "resolution", "wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_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"
};
const std::vector<std::string>& Preset::filament_options()
{
static std::vector<std::string> s_opts {
"filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed",
"extrusion_multiplier", "filament_density", "filament_cost", "filament_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",
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
"temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
"max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
"start_filament_gcode", "end_filament_gcode",
// Retract overrides
"filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
};
return 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",
"filament_cooling_initial_speed", "filament_cooling_final_speed", "filament_ramming_parameters", "filament_minimal_purge_on_wipe_tower",
"temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", "fan_always_on", "cooling", "min_fan_speed",
"max_fan_speed", "bridge_fan_speed", "disable_fan_first_layers", "full_fan_speed_layer", "fan_below_layer_time", "slowdown_below_layer_time", "min_print_speed",
"start_filament_gcode", "end_filament_gcode",
// Retract overrides
"filament_retract_length", "filament_retract_lift", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_speed", "filament_deretract_speed", "filament_retract_restart_extra", "filament_retract_before_travel",
"filament_retract_layer_change", "filament_wipe", "filament_retract_before_wipe",
// Profile compatibility
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits"
};
const std::vector<std::string>& Preset::machine_limits_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"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;
}
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",
};
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",
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"color_change_gcode", "pause_print_gcode", "template_custom_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height",
"default_print_profile", "inherits",
"remaining_times", "silent_mode",
"machine_limits_usage", "thumbnails"
};
static std::vector<std::string> s_Preset_sla_print_options {
"layer_height",
"faded_layers",
"supports_enable",
"support_head_front_diameter",
"support_head_penetration",
"support_head_width",
"support_pillar_diameter",
"support_small_pillar_diameter_percent",
"support_max_bridges_on_pillar",
"support_pillar_connection_mode",
"support_buildplate_only",
"support_pillar_widening_factor",
"support_base_diameter",
"support_base_height",
"support_base_safety_distance",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
"support_object_elevation",
"support_points_density_relative",
"support_points_minimal_distance",
"slice_closing_radius",
"slicing_mode",
"pad_enable",
"pad_wall_thickness",
"pad_wall_height",
"pad_brim_size",
"pad_max_merge_distance",
// "pad_edge_radius",
"pad_wall_slope",
"pad_object_gap",
"pad_around_object",
"pad_around_object_everywhere",
"pad_object_connector_stride",
"pad_object_connector_width",
"pad_object_connector_penetration",
"hollowing_enable",
"hollowing_min_thickness",
"hollowing_quality",
"hollowing_closing_distance",
"output_filename_format",
"default_sla_print_profile",
"compatible_printers",
"compatible_printers_condition",
"inherits"
};
static std::vector<std::string> s_Preset_sla_material_options {
"material_type",
"initial_layer_height",
"bottle_cost",
"bottle_volume",
"bottle_weight",
"material_density",
"exposure_time",
"initial_exposure_time",
"material_correction",
"material_notes",
"material_vendor",
"default_sla_material_profile",
"compatible_prints", "compatible_prints_condition",
"compatible_printers", "compatible_printers_condition", "inherits"
};
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",
"display_mirror_x", "display_mirror_y",
"display_orientation",
"fast_tilt_time", "slow_tilt_time", "area_fill",
"relative_correction",
"absolute_correction",
"elefant_foot_compensation",
"elefant_foot_min_width",
"gamma_correction",
"min_exposure_time", "max_exposure_time",
"min_initial_exposure_time", "max_initial_exposure_time",
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
"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;
if (s_opts.empty()) {
s_opts = {
"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",
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
"host_type", "print_host", "printhost_apikey", "printhost_cafile",
"single_extruder_multi_material", "start_gcode", "end_gcode", "before_layer_gcode", "layer_gcode", "toolchange_gcode",
"color_change_gcode", "pause_print_gcode", "template_custom_gcode",
"between_objects_gcode", "printer_vendor", "printer_model", "printer_variant", "printer_notes", "cooling_tube_retraction",
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "max_print_height",
"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 = {
"layer_height",
"faded_layers",
"supports_enable",
"support_head_front_diameter",
"support_head_penetration",
"support_head_width",
"support_pillar_diameter",
"support_small_pillar_diameter_percent",
"support_max_bridges_on_pillar",
"support_pillar_connection_mode",
"support_buildplate_only",
"support_pillar_widening_factor",
"support_base_diameter",
"support_base_height",
"support_base_safety_distance",
"support_critical_angle",
"support_max_bridge_length",
"support_max_pillar_link_distance",
"support_object_elevation",
"support_points_density_relative",
"support_points_minimal_distance",
"slice_closing_radius",
"slicing_mode",
"pad_enable",
"pad_wall_thickness",
"pad_wall_height",
"pad_brim_size",
"pad_max_merge_distance",
// "pad_edge_radius",
"pad_wall_slope",
"pad_object_gap",
"pad_around_object",
"pad_around_object_everywhere",
"pad_object_connector_stride",
"pad_object_connector_width",
"pad_object_connector_penetration",
"hollowing_enable",
"hollowing_min_thickness",
"hollowing_quality",
"hollowing_closing_distance",
"output_filename_format",
"default_sla_print_profile",
"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 = {
"material_type",
"initial_layer_height",
"bottle_cost",
"bottle_volume",
"bottle_weight",
"material_density",
"exposure_time",
"initial_exposure_time",
"material_correction",
"material_notes",
"material_vendor",
"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 = {
"printer_technology",
"bed_shape", "bed_custom_texture", "bed_custom_model", "max_print_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
"display_mirror_x", "display_mirror_y",
"display_orientation",
"fast_tilt_time", "slow_tilt_time", "area_fill",
"relative_correction",
"absolute_correction",
"elefant_foot_compensation",
"elefant_foot_min_width",
"gamma_correction",
"min_exposure_time", "max_exposure_time",
"min_initial_exposure_time", "max_initial_exposure_time",
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
"print_host", "printhost_apikey", "printhost_cafile",
"printer_notes",
"inherits"
};
}
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;
}
@ -1194,21 +1163,38 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
return diff;
}
static constexpr const std::initializer_list<const char*> optional_keys { "compatible_prints", "compatible_printers" };
bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference)
{
if (edited != nullptr && reference != nullptr) {
// Only compares options existing in both configs.
if (! reference->config.equals(edited->config))
return true;
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
for (auto &opt_key : optional_keys)
if (reference->config.has(opt_key) != edited->config.has(opt_key))
return true;
}
return false;
}
std::vector<std::string> PresetCollection::dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare /*= false*/)
{
std::vector<std::string> changed;
if (edited != nullptr && reference != nullptr) {
// Only compares options existing in both configs.
changed = deep_compare ?
deep_diff(edited->config, reference->config) :
reference->config.diff(edited->config);
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
std::initializer_list<const char*> optional_keys { "compatible_prints", "compatible_printers" };
for (auto &opt_key : optional_keys) {
for (auto &opt_key : optional_keys)
if (reference->config.has(opt_key) != edited->config.has(opt_key))
changed.emplace_back(opt_key);
}
}
return changed;
}
@ -1409,26 +1395,25 @@ std::string PhysicalPrinter::separator()
return " * ";
}
static std::vector<std::string> s_PhysicalPrinter_opts {
"preset_name", // temporary option to compatibility with older Slicer
"preset_names",
"printer_technology",
"host_type",
"print_host",
"printhost_apikey",
"printhost_cafile",
"printhost_port",
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
"printhost_password",
"printhost_ssl_ignore_revoke"
};
const std::vector<std::string>& PhysicalPrinter::printer_options()
{
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"preset_name", // temporary option to compatibility with older Slicer
"preset_names",
"printer_technology",
"host_type",
"print_host",
"printhost_apikey",
"printhost_cafile",
"printhost_port",
"printhost_authorization_type",
// HTTP digest authentization (RFC 2617)
"printhost_user",
"printhost_password"
};
}
return s_opts;
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.
@ -463,7 +463,8 @@ public:
size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return true if they differ.
bool current_is_dirty() const { return ! this->current_dirty_options().empty(); }
bool current_is_dirty() const
{ return is_dirty(&this->get_edited_preset(), &this->get_selected_preset()); }
// Compare the content of get_selected_preset() with get_edited_preset() configs, return the list of keys where they differ.
std::vector<std::string> current_dirty_options(const bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_selected_preset(), deep_compare); }
@ -472,10 +473,11 @@ public:
{ return dirty_options(&this->get_edited_preset(), this->get_selected_preset_parent(), deep_compare); }
// Compare the content of get_saved_preset() with get_edited_preset() configs, return true if they differ.
bool saved_is_dirty() const { return !this->saved_dirty_options().empty(); }
bool saved_is_dirty() const
{ 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 bool deep_compare = false) const
{ return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), deep_compare); }
// std::vector<std::string> saved_dirty_options() const
// { return dirty_options(&this->get_edited_preset(), &this->get_saved_preset(), /* deep_compare */ false); }
// Copy edited preset into saved preset.
void update_saved_preset_from_current_preset() { m_saved_preset = m_edited_preset; }
@ -552,7 +554,8 @@ private:
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, PresetSelectCompatibleType unselect_if_incompatible);
public:
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
static bool is_dirty(const Preset *edited, const Preset *reference);
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool deep_compare = false);
private:
// Type of this PresetCollection: TYPE_PRINT, TYPE_FILAMENT or TYPE_PRINTER.
Preset::Type m_type;

View file

@ -1477,7 +1477,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
if (! active_print.empty())
prints.select_preset_by_name(active_print, true);
if (! active_sla_print.empty())
sla_materials.select_preset_by_name(active_sla_print, true);
sla_prints.select_preset_by_name(active_sla_print, true);
if (! active_sla_material.empty())
sla_materials.select_preset_by_name(active_sla_material, true);
if (! active_printer.empty())

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

@ -216,22 +216,25 @@ static t_config_option_keys print_config_diffs(
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
// An extruder retract override is available at some of the filament presets.
if (*opt_old != *opt_new || opt_new->overriden_by(opt_new_filament)) {
bool overriden = opt_new->overriden_by(opt_new_filament);
if (overriden || *opt_old != *opt_new) {
auto opt_copy = opt_new->clone();
opt_copy->apply_override(opt_new_filament);
if (*opt_old == *opt_copy)
delete opt_copy;
else {
filament_overrides.set_key_value(opt_key, opt_copy);
bool changed = *opt_old != *opt_copy;
if (changed)
print_diff.emplace_back(opt_key);
}
if (changed || overriden) {
// filament_overrides will be applied to the placeholder parser, which layers these parameters over full_print_config.
filament_overrides.set_key_value(opt_key, opt_copy);
} else
delete opt_copy;
}
} else if (*opt_new != *opt_old)
print_diff.emplace_back(opt_key);
}
return print_diff;
}
}
// Prepare for storing of the full print config into new_full_config to be exported into the G-code and to be used by the PlaceholderParser.
static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig &current_full_config, const DynamicPrintConfig &new_full_config)
@ -812,7 +815,7 @@ static PrintObjectRegions* generate_print_object_regions(
layer_ranges_regions.push_back({ range.layer_height_range, range.config });
}
const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, is_mm_painted ? 0.f : std::max(0.f, xy_size_compensation));
std::vector<PrintRegion*> region_set;
@ -928,6 +931,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
bool num_extruders_changed = false;
if (! full_config_diff.empty()) {
update_apply_status(this->invalidate_step(psGCodeExport));
m_placeholder_parser.clear_config();
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
@ -939,6 +943,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, print_diff, true);
//FIXME use move semantics once ConfigBase supports it.
// Some filament_overrides may contain values different from new_full_config, but equal to m_config.
// As long as these config options don't reallocate memory when copying, we are safe overriding a value, which is in use by a worker thread.
m_config.apply(filament_overrides);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, object_diff, true);
@ -946,8 +952,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
if (num_extruders != m_config.nozzle_diameter.size()) {
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
num_extruders = m_config.nozzle_diameter.size();
num_extruders_changed = true;
}
}
@ -1065,7 +1071,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked.
bool solid_or_modifier_differ = model_volume_list_changed(model_object, model_object_new, solid_or_modifier_types) ||
model_mmu_segmentation_data_changed(model_object, model_object_new);
model_mmu_segmentation_data_changed(model_object, model_object_new) ||
(model_object_new.is_mm_painted() && num_extruders_changed);
bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) ||
model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty());
@ -1267,7 +1274,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
print_object_regions->ref_cnt_inc();
}
std::vector<unsigned int> painting_extruders;
if (const auto &volumes = print_object.model_object()->volumes;
if (const auto &volumes = print_object.model_object()->volumes;
num_extruders > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume *v) { return ! v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
//FIXME be more specific! Don't enumerate extruders that are not used for painting!
painting_extruders.assign(num_extruders, 0);

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,31 +298,33 @@ 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");
@ -317,12 +332,6 @@ void PrintConfigDef::init_common_params()
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

@ -167,8 +167,9 @@ static std::vector<VolumeSlices> slice_volumes_inner(
params_base.mode_below = params_base.mode;
const bool is_mm_painted = std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
const size_t num_extruders = print_config.nozzle_diameter.size();
const bool is_mm_painted = num_extruders > 1 && std::any_of(model_volumes.cbegin(), model_volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
const auto extra_offset = is_mm_painted ? 0.f : std::max(0.f, float(print_object_config.xy_size_compensation.value));
for (const ModelVolume *model_volume : model_volumes)
if (model_volume_needs_slicing(*model_volume)) {
@ -723,6 +724,7 @@ void PrintObject::slice_volumes()
// Is any ModelVolume MMU painted?
if (const auto& volumes = this->model_object()->volumes;
m_print->config().nozzle_diameter.size() > 1 &&
std::find_if(volumes.begin(), volumes.end(), [](const ModelVolume* v) { return !v->mmu_segmentation_facets.empty(); }) != volumes.end()) {
// If XY Size compensation is also enabled, notify the user that XY Size compensation
@ -743,8 +745,9 @@ void PrintObject::slice_volumes()
BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - make_slices in parallel - begin";
{
// Compensation value, scaled. Only applying the negative scaling here, as the positive scaling has already been applied during slicing.
const auto xy_compensation_scaled = this->is_mm_painted() ? scaled<float>(0.f) : scaled<float>(std::min(m_config.xy_size_compensation.value, 0.));
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
const size_t num_extruders = print->config().nozzle_diameter.size();
const auto xy_compensation_scaled = (num_extruders > 1 && this->is_mm_painted()) ? scaled<float>(0.f) : scaled<float>(std::min(m_config.xy_size_compensation.value, 0.));
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
// Only enable Elephant foot compensation if printing directly on the print bed.
float(scale_(m_config.elefant_foot_compensation.value)) :
0.f;

View file

@ -7,13 +7,14 @@
using namespace Slic3r;
// only private namespace not neccessary be in hpp
// only private namespace not neccessary be in .hpp
namespace QuadricEdgeCollapse {
using Vertices = std::vector<stl_vertex>;
using Triangle = stl_triangle_vertex_indices;
using Indices = std::vector<stl_triangle_vertex_indices>;
using SymMat = SimplifyMesh::implementation::SymetricMatrix<double>;
using ThrowOnCancel = std::function<void(void)>;
using StatusFn = std::function<void(int)>;
// smallest error caused by edges, identify smallest edge in triangle
struct Error
{
@ -74,12 +75,14 @@ namespace QuadricEdgeCollapse {
// calculate error for vertex and quadrics, triangle quadrics and triangle vertex give zero, only pozitive number
double vertex_error(const SymMat &q, const Vec3d &vertex);
SymMat create_quadric(const Triangle &t, const Vec3d& n, const Vertices &vertices);
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors> init(const indexed_triangle_set &its);
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors>
init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn);
std::optional<uint32_t> find_triangle_index1(uint32_t vi, const VertexInfo& v_info,
uint32_t ti, const EdgeInfos& e_infos, const Indices& indices);
bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info,
const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its);
bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info,
const EdgeInfos &e_infos, const Indices &indices);
// find edge with smallest error in triangle
Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos);
Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index);
@ -88,6 +91,14 @@ namespace QuadricEdgeCollapse {
uint32_t vi0, uint32_t vi1, uint32_t vi_top0,
const Triangle &t1, CopyEdgeInfos& infos, EdgeInfos &e_infos1);
void compact(const VertexInfos &v_infos, const TriangleInfos &t_infos, const EdgeInfos &e_infos, indexed_triangle_set &its);
#ifndef NDEBUG
void store_surround(const char *obj_filename, size_t triangle_index, int depth, const indexed_triangle_set &its,
const VertexInfos &v_infos, const EdgeInfos &e_infos);
bool check_neighbors(const indexed_triangle_set &its, const TriangleInfos &t_infos,
const VertexInfos &v_infos, const EdgeInfos &e_infos);
#endif /* NDEBUG */
} // namespace QuadricEdgeCollapse
using namespace QuadricEdgeCollapse;
@ -97,7 +108,7 @@ void Slic3r::its_quadric_edge_collapse(
uint32_t triangle_count,
float * max_error,
std::function<void(void)> throw_on_cancel,
std::function<void(int)> statusfn)
std::function<void(int)> status_fn)
{
// constants --> may be move to config
const int status_init_size = 10; // in percents
@ -108,15 +119,22 @@ void Slic3r::its_quadric_edge_collapse(
float maximal_error = (max_error == nullptr)? std::numeric_limits<float>::max() : *max_error;
if (maximal_error <= 0.f) return;
if (throw_on_cancel == nullptr) throw_on_cancel = []() {};
if (statusfn == nullptr) statusfn = [](int) {};
if (status_fn == nullptr) status_fn = [](int) {};
StatusFn init_status_fn = [&](int percent) {
status_fn(std::round((percent * status_init_size) / 100.));
};
TriangleInfos t_infos; // only normals with information about deleted triangle
VertexInfos v_infos;
EdgeInfos e_infos;
Errors errors;
std::tie(t_infos, v_infos, e_infos, errors) = init(its);
std::tie(t_infos, v_infos, e_infos, errors) = init(its, throw_on_cancel, init_status_fn);
throw_on_cancel();
statusfn(status_init_size);
status_fn(status_init_size);
//its_store_triangle(its, "triangle.obj", 1182);
//store_surround("triangle_surround1.obj", 1182, 1, its, v_infos, e_infos);
// convert from triangle index to mutable priority queue index
std::vector<size_t> ti_2_mpqi(its.indices.size(), {0});
@ -142,7 +160,7 @@ void Slic3r::its_quadric_edge_collapse(
(double) count_triangle_to_reduce;
double status = status_init_size + (100 - status_init_size) *
(1. - reduced);
statusfn(static_cast<int>(std::round(status)));
status_fn(static_cast<int>(std::round(status)));
};
// modulo for update status
uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100);
@ -181,6 +199,8 @@ void Slic3r::its_quadric_edge_collapse(
find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) :
find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ;
if (!ti1_opt.has_value() || // edge has only one triangle
degenerate(vi0, ti0, *ti1_opt, v_info1, e_infos, its.indices) ||
degenerate(vi1, ti0, *ti1_opt, v_info0, e_infos, its.indices) ||
is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) ||
is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) {
// try other triangle's edge
@ -236,8 +256,7 @@ void Slic3r::its_quadric_edge_collapse(
}
v_info0.q = q;
// fix neighbors
// fix neighbors
// vertex index of triangle 0 which is not vi0 nor vi1
uint32_t vi_top0 = t0[(t_info0.min_index + 2) % 3];
const Triangle &t1 = its.indices[ti1];
@ -263,6 +282,7 @@ void Slic3r::its_quadric_edge_collapse(
t_info1.set_deleted();
// triangle counter decrementation
actual_triangle_count-=2;
assert(check_neighbors(its, t_infos, v_infos, e_infos));
}
// compact triangle
@ -362,8 +382,16 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t,
}
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors>
QuadricEdgeCollapse::init(const indexed_triangle_set &its)
QuadricEdgeCollapse::init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn)
{
// change speed of progress bargraph
const int status_normal_size = 25;
const int status_sum_quadric = 25;
const int status_set_offsets = 10;
const int status_calc_errors = 30;
const int status_create_refs = 10;
int status_offset = 0;
TriangleInfos t_infos(its.indices.size());
VertexInfos v_infos(its.vertices.size());
{
@ -377,8 +405,13 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
Vec3d normal = create_normal(t, its.vertices);
t_info.n = normal.cast<float>();
triangle_quadrics[i] = create_quadric(t, normal, its.vertices);
if (i % 1000000 == 0) {
throw_on_cancel();
status_fn(status_offset + (i * status_normal_size) / its.indices.size());
}
}
}); // END parallel for
status_offset += status_normal_size;
// sum quadrics
for (size_t i = 0; i < its.indices.size(); i++) {
@ -389,7 +422,12 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
v_info.q += q;
++v_info.count; // triangle count
}
if (i % 1000000 == 0) {
throw_on_cancel();
status_fn(status_offset + (i * status_sum_quadric) / its.indices.size());
}
}
status_offset += status_sum_quadric;
} // remove triangle quadrics
// set offseted starts
@ -402,6 +440,10 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
}
assert(its.indices.size() * 3 == triangle_start);
status_offset += status_set_offsets;
throw_on_cancel();
status_fn(status_offset);
// calc error
Errors errors(its.indices.size());
tbb::parallel_for(tbb::blocked_range<size_t>(0, its.indices.size()),
@ -410,8 +452,15 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
const Triangle &t = its.indices[i];
TriangleInfo & t_info = t_infos[i];
errors[i] = calculate_error(i, t, its.vertices, v_infos, t_info.min_index);
if (i % 1000000 == 0) {
throw_on_cancel();
status_fn(status_offset + (i * status_calc_errors) / its.indices.size());
}
if (i % 1000000 == 0) throw_on_cancel();
}
}); // END parallel for
status_offset += status_calc_errors;
// create reference
EdgeInfos e_infos(its.indices.size() * 3);
@ -426,7 +475,14 @@ QuadricEdgeCollapse::init(const indexed_triangle_set &its)
e_info.edge = j;
++v_info.count;
}
if (i % 1000000 == 0) {
throw_on_cancel();
status_fn(status_offset + (i * status_create_refs) / its.indices.size());
}
}
throw_on_cancel();
status_fn(100);
return {t_infos, v_infos, e_infos, errors};
}
@ -489,6 +545,28 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
return false;
}
bool QuadricEdgeCollapse::degenerate(uint32_t vi,
uint32_t ti0,
uint32_t ti1,
const VertexInfo &v_info,
const EdgeInfos & e_infos,
const Indices & indices)
{
// check surround triangle do not contain vertex index
// protect from creation of triangle with two same vertices inside
size_t v_info_end = v_info.start + v_info.count;
for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
assert(ei < e_infos.size());
const EdgeInfo &e_info = e_infos[ei];
if (e_info.t_index == ti0) continue; // ti0 will be deleted
if (e_info.t_index == ti1) continue; // ti1 will be deleted
const Triangle &t = indices[e_info.t_index];
for (size_t i = 0; i < 3; ++i)
if (static_cast<uint32_t>(t[i]) == vi) return true;
}
return false;
}
Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t,
const Vertices & vertices,
const VertexInfos &v_infos)
@ -653,3 +731,115 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos,
}
its.indices.erase(its.indices.begin() + ti_new, its.indices.end());
}
#ifndef NDEBUG
// store triangle surrounding to file
void QuadricEdgeCollapse::store_surround(const char *obj_filename,
size_t triangle_index,
int depth,
const indexed_triangle_set &its,
const VertexInfos & v_infos,
const EdgeInfos & e_infos)
{
std::set<size_t> triangles;
// triangle index, depth
using Item = std::pair<size_t, int>;
std::queue<Item> process;
process.push({triangle_index, depth});
while (!process.empty()) {
Item item = process.front();
process.pop();
size_t ti = item.first;
auto it = triangles.find(ti);
if (it != triangles.end()) continue;
triangles.insert(ti);
if (item.second == 0) continue;
const Vec3i &t = its.indices[ti];
for (size_t i = 0; i < 3; ++i) {
const auto &v_info = v_infos[t[i]];
for (size_t d = 0; d < v_info.count; ++d) {
size_t ei = v_info.start + d;
const auto &e_info = e_infos[ei];
auto it = triangles.find(e_info.t_index);
if (it != triangles.end()) continue;
process.push({e_info.t_index, item.second - 1});
}
}
}
std::vector<size_t> trs;
trs.reserve(triangles.size());
for (size_t ti : triangles) trs.push_back(ti);
its_store_triangles(its, obj_filename, trs);
// its_write_obj(its,"original.obj");
}
bool QuadricEdgeCollapse::check_neighbors(const indexed_triangle_set &its,
const TriangleInfos & t_infos,
const VertexInfos & v_infos,
const EdgeInfos & e_infos)
{
VertexInfos v_infos2(v_infos.size());
size_t count_indices = 0;
for (size_t ti = 0; ti < its.indices.size(); ti++) {
if (t_infos[ti].is_deleted()) continue;
++count_indices;
const Triangle &t = its.indices[ti];
for (size_t e = 0; e < 3; e++) {
VertexInfo &v_info = v_infos2[t[e]];
++v_info.count; // triangle count
}
}
uint32_t triangle_start = 0;
for (VertexInfo &v_info : v_infos2) {
v_info.start = triangle_start;
triangle_start += v_info.count;
// set filled vertex to zero
v_info.count = 0;
}
// create reference
EdgeInfos e_infos2(count_indices * 3);
for (size_t ti = 0; ti < its.indices.size(); ti++) {
if (t_infos[ti].is_deleted()) continue;
const Triangle &t = its.indices[ti];
for (size_t j = 0; j < 3; ++j) {
VertexInfo &v_info = v_infos2[t[j]];
size_t ei = v_info.start + v_info.count;
assert(ei < e_infos2.size());
EdgeInfo &e_info = e_infos2[ei];
e_info.t_index = ti;
e_info.edge = j;
++v_info.count;
}
}
for (size_t vi = 0; vi < its.vertices.size(); vi++) {
const VertexInfo &v_info = v_infos[vi];
if (v_info.is_deleted()) continue;
const VertexInfo &v_info2 = v_infos2[vi];
if (v_info.count != v_info2.count) { return false; }
EdgeInfos eis;
eis.reserve(v_info.count);
std::copy(e_infos.begin() + v_info.start,
e_infos.begin() + v_info.start + v_info.count,
std::back_inserter(eis));
auto compare = [](const EdgeInfo &ei1, const EdgeInfo &ei2) {
return ei1.t_index < ei2.t_index;
};
std::sort(eis.begin(), eis.end(), compare);
std::sort(e_infos2.begin() + v_info2.start,
e_infos2.begin() + v_info2.start + v_info2.count, compare);
for (size_t ei = 0; ei < v_info.count; ++ei) {
if (eis[ei].t_index != e_infos2[ei + v_info2.start].t_index) {
return false;
}
}
}
return true;
}
#endif /* NDEBUG */

View file

@ -47,6 +47,8 @@
#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0)
// Enable drawing contours, at cut level, for sinking volumes
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0)
// Enable implementation of retract acceleration in gcode processor
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0)
// Enable the fix for exporting and importing to/from 3mf file of mirrored volumes
#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA0)

View file

@ -957,6 +957,48 @@ int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit)
return removed;
}
bool its_store_triangle(const indexed_triangle_set &its,
const char * obj_filename,
size_t triangle_index)
{
if (its.indices.size() <= triangle_index) return false;
Vec3i t = its.indices[triangle_index];
indexed_triangle_set its2;
its2.indices = {{0, 1, 2}};
its2.vertices = {its.vertices[t[0]], its.vertices[t[1]],
its.vertices[t[2]]};
return its_write_obj(its2, obj_filename);
}
bool its_store_triangles(const indexed_triangle_set &its,
const char * obj_filename,
const std::vector<size_t> & triangles)
{
indexed_triangle_set its2;
its2.vertices.reserve(triangles.size() * 3);
its2.indices.reserve(triangles.size());
std::map<size_t, size_t> vertex_map;
for (auto ti : triangles) {
if (its.indices.size() <= ti) return false;
Vec3i t = its.indices[ti];
Vec3i new_t;
for (size_t i = 0; i < 3; ++i) {
size_t vi = t[i];
auto it = vertex_map.find(vi);
if (it != vertex_map.end()) {
new_t[i] = it->second;
continue;
}
size_t new_vi = its2.vertices.size();
its2.vertices.push_back(its.vertices[vi]);
vertex_map[vi] = new_vi;
new_t[i] = new_vi;
}
its2.indices.push_back(new_t);
}
return its_write_obj(its2, obj_filename);
}
void its_shrink_to_fit(indexed_triangle_set &its)
{
its.indices.shrink_to_fit();

View file

@ -140,6 +140,10 @@ int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit =
// Remove vertices, which none of the faces references. Return number of freed vertices.
int its_compactify_vertices(indexed_triangle_set &its, bool shrink_to_fit = true);
// store part of index triangle set
bool its_store_triangle(const indexed_triangle_set &its, const char *obj_filename, size_t triangle_index);
bool its_store_triangles(const indexed_triangle_set &its, const char *obj_filename, const std::vector<size_t>& triangles);
std::vector<indexed_triangle_set> its_split(const indexed_triangle_set &its);
bool its_is_splittable(const indexed_triangle_set &its);

View file

@ -4,7 +4,7 @@
#include <boost/container/small_vector.hpp>
#ifndef NDEBUG
#define EXPENSIVE_DEBUG_CHECKS
// #define EXPENSIVE_DEBUG_CHECKS
#endif // NDEBUG
namespace Slic3r {

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(miniz)
cmake_minimum_required(VERSION 2.6)
add_library(miniz INTERFACE)

View file

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.12)
project(semver)
cmake_minimum_required(VERSION 2.6)
add_library(semver STATIC
semver.c

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

@ -195,7 +195,7 @@ void CopyrightsDialog::on_dpi_changed(const wxRect &suggested_rect)
void CopyrightsDialog::onLinkClicked(wxHtmlLinkEvent &event)
{
wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
event.Skip(false);
}
@ -344,7 +344,7 @@ void AboutDialog::on_dpi_changed(const wxRect &suggested_rect)
void AboutDialog::onLinkClicked(wxHtmlLinkEvent &event)
{
wxLaunchDefaultBrowser(event.GetLinkInfo().GetHref());
wxGetApp().open_browser_with_warning_dialog(event.GetLinkInfo().GetHref());
event.Skip(false);
}

View file

@ -268,7 +268,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("gap_fill_speed", have_perimeters);
for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" })
toggle_field(el, has_top_solid_infill);
toggle_field(el, has_top_solid_infill || (has_spiral_vase && has_bottom_solid_infill));
bool have_default_acceleration = config->opt_float("default_acceleration") > 0;
for (auto el : { "perimeter_acceleration", "infill_acceleration",

View file

@ -2028,7 +2028,7 @@ void Control::show_cog_icon_context_menu()
append_menu_item(&menu, wxID_ANY, _L("Set extruder sequence for the entire print"), "",
[this](wxCommandEvent&) { edit_extruder_sequence(); }, "", &menu);
if (m_mode != MultiExtruder && m_draw_mode == dmRegular)
if (GUI::wxGetApp().is_editor() && m_mode != MultiExtruder && m_draw_mode == dmRegular)
append_menu_item(&menu, wxID_ANY, _L("Set auto color changes"), "",
[this](wxCommandEvent&) { auto_color_change(); }, "", &menu);

View file

@ -2239,6 +2239,8 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
m_dirty |= mouse3d_controller_applied;
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
auto gizmo = wxGetApp().plater()->canvas3D()->get_gizmos_manager().get_current();
if (gizmo != nullptr) m_dirty |= gizmo->update_items_state();
if (!m_dirty)
return;
@ -2780,11 +2782,10 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
{
// no need to do anything here
// right after this event is recieved, idle event is fired
//m_dirty = true;
//wxWakeUpIdle();
// no need to wake up idle
// right after this event, idle event is fired
// m_dirty = true;
// wxWakeUpIdle();
}
@ -2802,21 +2803,15 @@ void GLCanvas3D::schedule_extra_frame(int miliseconds)
return;
}
}
// Start timer
int64_t now = timestamp_now();
int remaining_time = m_render_timer.GetInterval();
// Timer is not running
if (! m_render_timer.IsRunning()) {
m_extra_frame_requested_delayed = miliseconds;
if (!m_render_timer.IsRunning()) {
m_render_timer.StartOnce(miliseconds);
m_render_timer_start = now;
// Timer is running - restart only if new period is shorter than remaning period
} else {
const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now;
if (miliseconds + 20 < remaining_time) {
m_render_timer.Stop();
m_extra_frame_requested_delayed = miliseconds;
m_render_timer.StartOnce(miliseconds);
m_render_timer_start = now;
}
}
}
@ -4438,13 +4433,7 @@ bool GLCanvas3D::_init_main_toolbar()
}
// init arrow
BackgroundTexture::Metadata arrow_data;
arrow_data.filename = "toolbar_arrow.png";
// arrow_data.filename = "toolbar_arrow.svg";
//arrow_data.left = 16;
//arrow_data.top = 16;
//arrow_data.right = 16;
//arrow_data.bottom = 16;
arrow_data.filename = "toolbar_arrow.svg";
arrow_data.left = 0;
arrow_data.top = 0;
arrow_data.right = 0;

View file

@ -463,15 +463,13 @@ private:
std::string m_sidebar_field;
// when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame()
bool m_extra_frame_requested;
int m_extra_frame_requested_delayed { std::numeric_limits<int>::max() };
bool m_extra_frame_requested;
bool m_event_handlers_bound{ false };
GLVolumeCollection m_volumes;
GCodeViewer m_gcode_viewer;
RenderTimer m_render_timer;
int64_t m_render_timer_start;
Selection m_selection;
const DynamicPrintConfig* m_config;

View file

@ -193,10 +193,9 @@ bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture)
std::string path = resources_dir() + "/icons/";
bool res = false;
if (!arrow_texture.filename.empty())
res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
if (!arrow_texture.filename.empty()) {
res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
}
if (res)
m_arrow_texture.metadata = arrow_texture;
@ -1176,19 +1175,22 @@ void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighte
float right = left + scaled_icons_size;
unsigned int tex_id = m_arrow_texture.texture.get_id();
// width and height of icon arrow is pointing to
float tex_width = (float)m_icons_texture.get_width();
float tex_height = (float)m_icons_texture.get_height();
// arrow width and height
float arr_tex_width = (float)m_arrow_texture.texture.get_width();
float arr_tex_height = (float)m_arrow_texture.texture.get_height();
if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) {
float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f;
float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f;
if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) {
float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f;
float internal_left = left + border - scaled_icons_size / 2; // add half scaled_icons_size for huge arrow
float internal_right = right - border + scaled_icons_size / 2;
float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow
float internal_right = right - border + scaled_icons_size * 1.5f;
float internal_top = top - border;
// bottom is not moving and should be calculated from arrow texture sides ratio
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio;
float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ;
float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width;
float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width;

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,
@ -2314,7 +2327,7 @@ wxString GUI_App::current_language_code_safe() const
void GUI_App::open_web_page_localized(const std::string &http_address)
{
wxLaunchDefaultBrowser(http_address + "&lng=" + this->current_language_code_safe());
open_browser_with_warning_dialog(http_address + "&lng=" + this->current_language_code_safe());
}
bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page)
@ -2512,6 +2525,23 @@ void GUI_App::check_updates(const bool verbose)
}
}
bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* = 0*/)
{
bool launch = true;
if (get_app_config()->get("suppress_hyperlinks").empty()) {
wxRichMessageDialog dialog(nullptr, _L("Should we open this hyperlink in your default browser?"), _L("PrusaSlicer: Open hyperlink"), wxICON_QUESTION | wxYES_NO);
dialog.ShowCheckBox(_L("Remember my choice"));
int answer = dialog.ShowModal();
launch = answer == wxID_YES;
get_app_config()->set("suppress_hyperlinks", dialog.IsCheckBoxChecked() ? (answer == wxID_NO ? "1" : "0") : "");
}
if (launch)
launch = get_app_config()->get("suppress_hyperlinks") != "1";
return launch && wxLaunchDefaultBrowser(url, flags);
}
// static method accepting a wxWindow object as first parameter
// void warning_catcher{
// my($self, $message_dialog) = @_;

View file

@ -261,7 +261,8 @@ public:
void open_preferences(size_t open_on_tab = 0);
virtual bool OnExceptionInMainLoop() override;
// Calls wxLaunchDefaultBrowser if user confirms in dialog.
bool open_browser_with_warning_dialog(const wxString& url, int flags = 0);
#ifdef __APPLE__
void OSXStoreOpenFiles(const wxArrayString &files) override;
// wxWidgets override to get an event on open files.

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
@ -2731,8 +2730,9 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
return;
m_prevent_list_events = true;
for (std::vector<ItemForDelete>::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item)
{
std::set<size_t> modified_objects_ids;
for (std::vector<ItemForDelete>::const_reverse_iterator item = items_for_delete.rbegin(); item != items_for_delete.rend(); ++item) {
if (!(item->type&(itObject | itVolume | itInstance)))
continue;
if (item->type&itObject) {
@ -2742,8 +2742,7 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
else {
if (!del_subobject_from_object(item->obj_idx, item->sub_obj_idx, item->type))
continue;
if (item->type&itVolume)
{
if (item->type&itVolume) {
m_objects_model->Delete(m_objects_model->GetItemByVolumeId(item->obj_idx, item->sub_obj_idx));
ModelObject* obj = object(item->obj_idx);
if (obj->volumes.size() == 1) {
@ -2761,7 +2760,14 @@ void ObjectList::delete_from_model_and_list(const std::vector<ItemForDelete>& it
else
m_objects_model->Delete(m_objects_model->GetItemByInstanceId(item->obj_idx, item->sub_obj_idx));
}
modified_objects_ids.insert(static_cast<size_t>(item->obj_idx));
}
for (size_t id : modified_objects_ids) {
update_info_items(id);
}
m_prevent_list_events = true;
part_selection_changed();
}
@ -4017,7 +4023,23 @@ void ObjectList::fix_through_netfabb()
void ObjectList::simplify()
{
GLGizmosManager& gizmos_mgr = wxGetApp().plater()->canvas3D()->get_gizmos_manager();
auto plater = wxGetApp().plater();
GLGizmosManager& gizmos_mgr = plater->canvas3D()->get_gizmos_manager();
// Do not simplify when a gizmo is open. There might be issues with updates
// and what is worse, the snapshot time would refer to the internal stack.
auto current_type = gizmos_mgr.get_current_type();
if (current_type == GLGizmosManager::Simplify) {
// close first
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}else if (current_type != GLGizmosManager::Undefined) {
plater->get_notification_manager()->push_notification(
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
NotificationManager::NotificationLevel::RegularNotification,
_u8L("ERROR: Please close all manipulators available from "
"the left toolbar before start simplify the mesh."));
return;
}
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
}

View file

@ -85,6 +85,7 @@ GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, u
, m_dragging(false)
, m_imgui(wxGetApp().imgui())
, m_first_input_window_render(true)
, m_dirty(false)
{
m_base_color = DEFAULT_BASE_COLOR;
m_drag_color = DEFAULT_DRAG_COLOR;
@ -154,6 +155,13 @@ void GLGizmoBase::update(const UpdateData& data)
on_update(data);
}
bool GLGizmoBase::update_items_state()
{
bool res = m_dirty;
m_dirty = false;
return res;
};
std::array<float, 4> GLGizmoBase::picking_color_component(unsigned int id) const
{
static const float INV_255 = 1.0f / 255.0f;
@ -209,6 +217,10 @@ std::string GLGizmoBase::format(float value, unsigned int decimals) const
return Slic3r::string_printf("%.*f", decimals, value);
}
void GLGizmoBase::set_dirty() {
m_dirty = true;
}
void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
{
on_render_input_window(x, y, bottom_limit);

View file

@ -154,6 +154,9 @@ public:
void update(const UpdateData& data);
// returns True when Gizmo changed its state
bool update_items_state();
void render() { m_tooltip.clear(); on_render(); }
void render_for_picking() { on_render_for_picking(); }
void render_input_window(float x, float y, float bottom_limit);
@ -187,6 +190,13 @@ protected:
void render_grabbers_for_picking(const BoundingBoxf3& box) const;
std::string format(float value, unsigned int decimals) const;
// Mark gizmo as dirty to Re-Render when idle()
void set_dirty();
private:
// Flag for dirty visible state of Gizmo
// When True then need new rendering
bool m_dirty;
};
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components

View file

@ -630,7 +630,11 @@ void TriangleSelectorMmGui::render(ImGuiWrapper *imgui)
auto *contour_shader = wxGetApp().get_shader("mm_contour");
contour_shader->start_using();
glsafe(::glDepthFunc(GL_LEQUAL));
m_gizmo_scene.render_contour();
glsafe(::glDepthFunc(GL_LESS));
contour_shader->stop_using();
}
@ -725,10 +729,6 @@ void GLMmSegmentationGizmo3DScene::render(size_t triangle_indices_idx) const
assert(this->vertices_VBO_id != 0);
assert(this->triangle_indices_VBO_ids[triangle_indices_idx] != 0);
ScopeGuard offset_fill_guard([]() { glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); });
glsafe(::glEnable(GL_POLYGON_OFFSET_FILL));
glsafe(::glPolygonOffset(5.0, 5.0));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 3 * sizeof(float), (const void*)(0 * sizeof(float))));

View file

@ -1,34 +1,38 @@
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro.
#include "GLGizmoSimplify.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_ObjectManipulation.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/QuadricEdgeCollapse.hpp"
#include <chrono>
#include <thread>
namespace Slic3r::GUI {
GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent,
const std::string &icon_filename,
unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, -1)
, state(State::settings)
, is_valid_result(false)
, progress(0)
, volume(nullptr)
, obj_index(0)
, need_reload(false)
, m_state(State::settings)
, m_is_valid_result(false)
, m_exist_preview(false)
, m_progress(0)
, m_volume(nullptr)
, m_obj_index(0)
, m_need_reload(false)
, tr_mesh_name(_u8L("Mesh name"))
, tr_triangles(_u8L("Triangles"))
, tr_preview(_u8L("Preview"))
, tr_detail_level(_u8L("Detail level"))
, tr_decimate_ratio(_u8L("Decimate ratio"))
{}
GLGizmoSimplify::~GLGizmoSimplify() {
state = State::canceling;
if (worker.joinable()) worker.join();
m_state = State::canceling;
if (m_worker.joinable()) m_worker.join();
}
bool GLGizmoSimplify::on_init()
@ -38,7 +42,6 @@ bool GLGizmoSimplify::on_init()
return true;
}
std::string GLGizmoSimplify::on_get_name() const
{
return (_L("Simplify")).ToUTF8().data();
@ -49,14 +52,7 @@ void GLGizmoSimplify::on_render_for_picking() {}
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
{
const int min_triangle_count = 4; // tetrahedron
const int max_char_in_name = 25;
create_gui_cfg();
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse;
m_imgui->begin(on_get_name(), flag);
const Selection &selection = m_parent.get_selection();
int object_idx = selection.get_object_idx();
ModelObject *obj = wxGetApp().plater()->model().objects[object_idx];
@ -64,159 +60,196 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
// Check selection of new volume
// Do not reselect object when processing
if (act_volume != volume && state == State::settings) {
obj_index = object_idx; // to remember correct object
volume = act_volume;
original_its = {};
const TriangleMesh &tm = volume->mesh();
c.wanted_percent = 50.; // default value
c.update_percent(tm.its.indices.size());
is_valid_result = false;
// set window position
ImVec2 pos = ImGui::GetMousePos();
pos.x -= gui_cfg->window_offset;
pos.y -= gui_cfg->window_offset;
ImGui::SetWindowPos(pos, ImGuiCond_Always);
if (act_volume != m_volume && m_state == State::settings) {
bool change_window_position = (m_volume == nullptr);
// select different model
if (m_volume != nullptr && m_original_its.has_value()) {
set_its(*m_original_its);
}
m_obj_index = object_idx; // to remember correct object
m_volume = act_volume;
m_original_its = {};
m_configuration.decimate_ratio = 50.; // default value
m_configuration.fix_count_by_ratio(m_volume->mesh().its.indices.size());
m_is_valid_result = false;
m_exist_preview = false;
if (change_window_position) {
ImVec2 pos = ImGui::GetMousePos();
pos.x -= m_gui_cfg->window_offset_x;
pos.y -= m_gui_cfg->window_offset_y;
// minimal top left value
ImVec2 tl(m_gui_cfg->window_padding, m_gui_cfg->window_padding);
if (pos.x < tl.x) pos.x = tl.x;
if (pos.y < tl.y) pos.y = tl.y;
// maximal bottom right value
auto parent_size = m_parent.get_canvas_size();
ImVec2 br(
parent_size.get_width() - (2 * m_gui_cfg->window_offset_x + m_gui_cfg->window_padding),
parent_size.get_height() - (2 * m_gui_cfg->window_offset_y + m_gui_cfg->window_padding));
if (pos.x > br.x) pos.x = br.x;
if (pos.y > br.y) pos.y = br.y;
ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
}
}
size_t triangle_count = volume->mesh().its.indices.size();
// already reduced mesh
if (original_its.has_value())
triangle_count = original_its->indices.size();
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse;
m_imgui->begin(on_get_name(), flag);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Mesh name") + ":");
ImGui::SameLine(gui_cfg->top_left_width);
std::string name = volume->name;
if (name.length() > max_char_in_name)
name = name.substr(0, max_char_in_name-3) + "...";
size_t triangle_count = m_volume->mesh().its.indices.size();
// already reduced mesh
if (m_original_its.has_value())
triangle_count = m_original_its->indices.size();
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_mesh_name + ":");
ImGui::SameLine(m_gui_cfg->top_left_width);
std::string name = m_volume->name;
if (name.length() > m_gui_cfg->max_char_in_name)
name = name.substr(0, m_gui_cfg->max_char_in_name - 3) + "...";
m_imgui->text(name);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Triangles") + ":");
ImGui::SameLine(gui_cfg->top_left_width);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_triangles + ":");
ImGui::SameLine(m_gui_cfg->top_left_width);
m_imgui->text(std::to_string(triangle_count));
/*
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr_preview + ":");
ImGui::SameLine(m_gui_cfg->top_left_width);
if (m_exist_preview) {
m_imgui->text(std::to_string(m_volume->mesh().its.indices.size()));
} else {
m_imgui->text("---");
}*/
ImGui::Separator();
ImGui::Text(_L("Limit by triangles").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
// First initialization + fix triangle count
if (m_imgui->checkbox("##UseCount", c.use_count)) {
if (!c.use_count) c.use_error = true;
is_valid_result = false;
if(ImGui::RadioButton("##use_error", !m_configuration.use_count)) {
m_is_valid_result = false;
m_configuration.use_count = !m_configuration.use_count;
}
ImGui::SameLine();
m_imgui->disabled_begin(m_configuration.use_count);
ImGui::Text("%s", tr_detail_level.c_str());
std::vector<std::string> reduce_captions = {
static_cast<std::string>(_u8L("Extra high")),
static_cast<std::string>(_u8L("High")),
static_cast<std::string>(_u8L("Medium")),
static_cast<std::string>(_u8L("Low")),
static_cast<std::string>(_u8L("Extra low"))
};
ImGui::SameLine(m_gui_cfg->bottom_left_width);
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
static int reduction = 2;
if(ImGui::SliderInt("##ReductionLevel", &reduction, 0, 4, reduce_captions[reduction].c_str())) {
m_is_valid_result = false;
if (reduction < 0) reduction = 0;
if (reduction > 4) reduction = 4;
switch (reduction) {
case 0: m_configuration.max_error = 1e-3f; break;
case 1: m_configuration.max_error = 1e-2f; break;
case 2: m_configuration.max_error = 0.1f; break;
case 3: m_configuration.max_error = 0.5f; break;
case 4: m_configuration.max_error = 1.f; break;
}
}
m_imgui->disabled_end(); // !use_count
if (ImGui::RadioButton("##use_count", m_configuration.use_count)) {
m_is_valid_result = false;
m_configuration.use_count = !m_configuration.use_count;
}
ImGui::SameLine();
// show preview result triangle count (percent)
if (m_need_reload && !m_configuration.use_count) {
m_configuration.wanted_count = static_cast<uint32_t>(m_volume->mesh().its.indices.size());
m_configuration.decimate_ratio =
(1.0f - (m_configuration.wanted_count / (float) triangle_count)) * 100.f;
}
m_imgui->disabled_begin(!c.use_count);
ImGui::Text(_L("Triangle count").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
int wanted_count = c.wanted_count;
ImGui::SetNextItemWidth(gui_cfg->input_width);
if (ImGui::SliderInt("##triangle_count", &wanted_count, min_triangle_count, triangle_count, "%d")) {
c.wanted_count = static_cast<uint32_t>(wanted_count);
if (c.wanted_count < min_triangle_count)
c.wanted_count = min_triangle_count;
if (c.wanted_count > triangle_count)
c.wanted_count = triangle_count;
c.update_count(triangle_count);
is_valid_result = false;
m_imgui->disabled_begin(!m_configuration.use_count);
ImGui::Text("%s", tr_decimate_ratio.c_str());
ImGui::SameLine(m_gui_cfg->bottom_left_width);
ImGui::SetNextItemWidth(m_gui_cfg->input_width);
const char * format = (m_configuration.decimate_ratio > 10)? "%.0f %%":
((m_configuration.decimate_ratio > 1)? "%.1f %%":"%.2f %%");
if (ImGui::SliderFloat("##decimate_ratio", &m_configuration.decimate_ratio, 0.f, 100.f, format)) {
m_is_valid_result = false;
if (m_configuration.decimate_ratio < 0.f)
m_configuration.decimate_ratio = 0.01f;
if (m_configuration.decimate_ratio > 100.f)
m_configuration.decimate_ratio = 100.f;
m_configuration.fix_count_by_ratio(triangle_count);
}
ImGui::Text(_L("Ratio").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
ImGui::SetNextItemWidth(gui_cfg->input_small_width);
const char * precision = (c.wanted_percent > 10)? "%.0f": ((c.wanted_percent > 1)? "%.1f":"%.2f");
float step = (c.wanted_percent > 10)? 1.f: ((c.wanted_percent > 1)? 0.1f : 0.01f);
if (ImGui::InputFloat("%", &c.wanted_percent, step, 10*step, precision)) {
if (c.wanted_percent > 100.f) c.wanted_percent = 100.f;
c.update_percent(triangle_count);
if (c.wanted_count < min_triangle_count) {
c.wanted_count = min_triangle_count;
c.update_count(triangle_count);
}
is_valid_result = false;
}
m_imgui->disabled_end(); // use_count
ImGui::NewLine();
ImGui::Text(_L("Limit by error").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
if (m_imgui->checkbox("##UseError", c.use_error)) {
if (!c.use_error) c.use_count = true;
is_valid_result = false;
}
ImGui::SameLine(m_gui_cfg->bottom_left_width);
ImGui::Text(_L("%d triangles").c_str(), m_configuration.wanted_count);
m_imgui->disabled_end(); // use_count
m_imgui->disabled_begin(!c.use_error);
ImGui::Text(_L("Max. error").c_str());
ImGui::SameLine(gui_cfg->bottom_left_width);
ImGui::SetNextItemWidth(gui_cfg->input_small_width);
if (ImGui::InputFloat("##maxError", &c.max_error, 0.01f, .1f, "%.2f")) {
if (c.max_error < 0.f) c.max_error = 0.f;
is_valid_result = false;
}
m_imgui->disabled_end(); // use_error
if (state == State::settings) {
if (m_state == State::settings) {
if (m_imgui->button(_L("Cancel"))) {
if (original_its.has_value()) {
set_its(*original_its);
state = State::close_on_end;
if (m_original_its.has_value()) {
set_its(*m_original_its);
m_state = State::close_on_end;
} else {
close();
}
}
ImGui::SameLine(gui_cfg->bottom_left_width);
ImGui::SameLine(m_gui_cfg->bottom_left_width);
if (m_imgui->button(_L("Preview"))) {
state = State::simplifying;
m_state = State::preview;
// simplify but not aply on mesh
process();
}
ImGui::SameLine();
if (m_imgui->button(_L("Apply"))) {
if (!is_valid_result) {
state = State::close_on_end;
if (!m_is_valid_result) {
m_state = State::close_on_end;
process();
} else {
// use preview and close
if (m_exist_preview) {
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
plater->changed_mesh(m_obj_index);
}
close();
}
}
} else {
m_imgui->disabled_begin(state == State::canceling);
if (m_imgui->button(_L("Cancel"))) state = State::canceling;
m_imgui->disabled_begin(m_state == State::canceling);
if (m_imgui->button(_L("Cancel"))) m_state = State::canceling;
m_imgui->disabled_end();
ImGui::SameLine(gui_cfg->bottom_left_width);
ImGui::SameLine(m_gui_cfg->bottom_left_width);
// draw progress bar
char buf[32];
sprintf(buf, L("Process %d / 100"), progress);
ImGui::ProgressBar(progress / 100., ImVec2(gui_cfg->input_width, 0.f), buf);
sprintf(buf, L("Process %d / 100"), m_progress);
ImGui::ProgressBar(m_progress / 100., ImVec2(m_gui_cfg->input_width, 0.f), buf);
}
m_imgui->end();
if (need_reload) {
need_reload = false;
if (m_need_reload) {
m_need_reload = false;
bool close_on_end = (m_state == State::close_on_end);
// Reload visualization of mesh - change VBO, FBO on GPU
m_parent.reload_scene(true); // deactivate gizmo??
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
if (state == State::close_on_end) {
m_parent.reload_scene(true);
// set m_state must be before close() !!!
m_state = State::settings;
if (close_on_end) {
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
plater->changed_mesh(obj_index); // deactivate gizmo??
// changed_mesh cause close();
//close();
plater->changed_mesh(m_obj_index);
close();
}
// change from simplifying | aply
state = State::settings;
// Fix warning icon in object list
wxGetApp().obj_list()->update_item_error_icon(obj_index, -1);
wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1);
}
}
void GLGizmoSimplify::close() {
volume = nullptr;
// close gizmo == open it again
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
gizmos_mgr.open_gizmo(GLGizmosManager::EType::Simplify);
@ -230,66 +263,60 @@ void GLGizmoSimplify::process()
const char* what() const throw() { return L("Model simplification has been canceled"); }
};
if (!original_its.has_value())
original_its = volume->mesh().its; // copy
if (!m_original_its.has_value())
m_original_its = m_volume->mesh().its; // copy
auto plater = wxGetApp().plater();
plater->take_snapshot(_L("Simplify ") + volume->name);
plater->clear_before_change_mesh(obj_index);
progress = 0;
if (worker.joinable()) worker.join();
worker = std::thread([&]() {
plater->take_snapshot(_L("Simplify ") + m_volume->name);
plater->clear_before_change_mesh(m_obj_index);
m_progress = 0;
if (m_worker.joinable()) m_worker.join();
m_worker = std::thread([this]() {
// store original triangles
uint32_t triangle_count = (c.use_count) ? c.wanted_count : 0;
float max_error = (c.use_error) ? c.max_error : std::numeric_limits<float>::max();
uint32_t triangle_count = (m_configuration.use_count) ? m_configuration.wanted_count : 0;
float max_error = (!m_configuration.use_count) ? m_configuration.max_error : std::numeric_limits<float>::max();
std::function<void(void)> throw_on_cancel = [&]() {
if (state == State::canceling) {
if (m_state == State::canceling) {
throw SimplifyCanceledException();
}
};
std::function<void(int)> statusfn = [&](int percent) {
progress = percent;
m_parent.schedule_extra_frame(0);
};
indexed_triangle_set collapsed;
if (last_error.has_value()) {
// is chance to continue with last reduction
const indexed_triangle_set &its = volume->mesh().its;
uint32_t last_triangle_count = static_cast<uint32_t>(its.indices.size());
if ((!c.use_count || triangle_count <= last_triangle_count) &&
(!c.use_error || c.max_error <= *last_error)) {
collapsed = its; // small copy
} else {
collapsed = *original_its; // copy
}
} else {
collapsed = *original_its; // copy
}
std::function<void(int)> statusfn = [this](int percent) {
m_progress = percent;
// check max 4fps
static int64_t last = 0;
int64_t now = m_parent.timestamp_now();
if ((now - last) < 250) return;
last = now;
request_rerender();
};
indexed_triangle_set collapsed = *m_original_its; // copy
try {
its_quadric_edge_collapse(collapsed, triangle_count, &max_error, throw_on_cancel, statusfn);
set_its(collapsed);
is_valid_result = true;
last_error = max_error;
m_is_valid_result = true;
m_exist_preview = true;
} catch (SimplifyCanceledException &) {
state = State::settings;
// set state out of main thread
m_state = State::settings;
}
// need to render last status fn
// without sleep it freezes until mouse move
std::this_thread::sleep_for(std::chrono::milliseconds(50));
m_parent.schedule_extra_frame(0);
// need to render last status fn to change bar graph to buttons
request_rerender();
});
}
void GLGizmoSimplify::set_its(indexed_triangle_set &its) {
auto tm = std::make_unique<TriangleMesh>(its);
tm->repair();
volume->set_mesh(std::move(tm));
volume->set_new_unique_id();
volume->get_object()->invalidate_bounding_box();
need_reload = true;
m_volume->set_mesh(std::move(tm));
m_volume->set_new_unique_id();
m_volume->get_object()->invalidate_bounding_box();
m_need_reload = true;
}
bool GLGizmoSimplify::on_is_activable() const
@ -297,26 +324,62 @@ bool GLGizmoSimplify::on_is_activable() const
return !m_parent.get_selection().is_empty();
}
void GLGizmoSimplify::create_gui_cfg() {
if (gui_cfg.has_value()) return;
void GLGizmoSimplify::on_set_state()
{
// Closing gizmo. e.g. selecting another one
if (GLGizmoBase::m_state == GLGizmoBase::Off) {
// refuse outgoing during simlification
if (m_state != State::settings) {
GLGizmoBase::m_state = GLGizmoBase::On;
auto notification_manager = wxGetApp().plater()->get_notification_manager();
notification_manager->push_notification(
NotificationType::CustomNotification,
NotificationManager::NotificationLevel::RegularNotification,
_u8L("ERROR: Wait until Simplification ends or Cancel process."));
return;
}
// revert preview
if (m_exist_preview) {
set_its(*m_original_its);
m_parent.reload_scene(true);
m_need_reload = false;
}
// invalidate selected model
m_volume = nullptr;
} else if (GLGizmoBase::m_state == GLGizmoBase::On) {
// when open by hyperlink it needs to show up
request_rerender();
}
}
void GLGizmoSimplify::create_gui_cfg() {
if (m_gui_cfg.has_value()) return;
int space_size = m_imgui->calc_text_size(":MM").x;
GuiCfg cfg;
cfg.top_left_width = std::max(m_imgui->calc_text_size(_L("Mesh name")).x,
m_imgui->calc_text_size(_L("Triangles")).x)
cfg.top_left_width = std::max(m_imgui->calc_text_size(tr_mesh_name).x,
m_imgui->calc_text_size(tr_triangles).x)
+ space_size;
const float radio_size = ImGui::GetFrameHeight();
cfg.bottom_left_width =
std::max(
std::max(m_imgui->calc_text_size(_L("Limit by triangles")).x,
std::max(m_imgui->calc_text_size(_L("Triangle count")).x,
m_imgui->calc_text_size(_L("Ratio")).x)),
std::max(m_imgui->calc_text_size(_L("Limit by error")).x,
m_imgui->calc_text_size(_L("Max. error")).x)) + space_size;
cfg.input_width = cfg.bottom_left_width;
cfg.input_small_width = cfg.input_width * 0.8;
cfg.window_offset = cfg.input_width;
gui_cfg = cfg;
std::max(m_imgui->calc_text_size(tr_detail_level).x,
m_imgui->calc_text_size(tr_decimate_ratio).x) +
space_size + radio_size;
cfg.input_width = cfg.bottom_left_width * 1.5;
cfg.window_offset_x = (cfg.bottom_left_width + cfg.input_width)/2;
cfg.window_offset_y = ImGui::GetTextLineHeightWithSpacing() * 5;
m_gui_cfg = cfg;
}
void GLGizmoSimplify::request_rerender() {
wxGetApp().plater()->CallAfter([this]() {
set_dirty();
m_parent.schedule_extra_frame(0);
});
}
} // namespace Slic3r::GUI

View file

@ -1,35 +1,23 @@
#ifndef slic3r_GLGizmoSimplify_hpp_
#define slic3r_GLGizmoSimplify_hpp_
// Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code,
// which overrides our localization "L" macro.
#include "GLGizmoBase.hpp"
#include "libslic3r/Model.hpp"
#include "admesh/stl.h" // indexed_triangle_set
#include <thread>
#include <optional>
#include <atomic>
namespace Slic3r {
class ModelVolume;
namespace GUI {
class GLGizmoSimplify : public GLGizmoBase
{
enum class State {
settings,
simplifying, // start processing
canceling, // canceled
successfull, // successful simplified
close_on_end
} state;
bool is_valid_result; // differ what to do in apply
int progress;
ModelVolume *volume;
size_t obj_index;
std::optional<indexed_triangle_set> original_its;
std::optional<float> last_error; // for use previous reduction
bool need_reload; // after simplify, glReload must be on main thread
std::thread worker;
{
public:
GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
virtual ~GLGizmoSimplify();
@ -41,32 +29,51 @@ protected:
virtual void on_render_input_window(float x, float y, float bottom_limit) override;
virtual bool on_is_activable() const override;
virtual bool on_is_selectable() const override { return false; }
virtual void on_set_state() override;
private:
void close();
void process();
void set_its(indexed_triangle_set &its);
void create_gui_cfg();
void request_rerender();
std::atomic_bool m_is_valid_result; // differ what to do in apply
std::atomic_bool m_exist_preview; // set when process end
volatile int m_progress; // percent of done work
ModelVolume *m_volume; //
size_t m_obj_index;
std::optional<indexed_triangle_set> m_original_its;
volatile bool m_need_reload; // after simplify, glReload must be on main thread
std::thread m_worker;
enum class State {
settings,
preview, // simplify to show preview
close_on_end, // simplify with close on end
canceling // after button click, before canceled
};
volatile State m_state;
struct Configuration
{
bool use_count = true;
bool use_count = false;
// minimal triangle count
float wanted_percent = 50.f;
float decimate_ratio = 50.f; // in percent
uint32_t wanted_count = 0; // initialize by percents
bool use_error = false;
// maximal quadric error
float max_error = 1.;
void update_count(size_t triangle_count)
{
wanted_percent = (float) wanted_count / triangle_count * 100.f;
}
void update_percent(size_t triangle_count)
void fix_count_by_ratio(size_t triangle_count)
{
wanted_count = static_cast<uint32_t>(
std::round(triangle_count * wanted_percent / 100.f));
std::round(triangle_count * (100.f-decimate_ratio) / 100.f));
}
} c;
} m_configuration;
// This configs holds GUI layout size given by translated texts.
// etc. When language changes, GUI is recreated and this class constructed again,
@ -76,11 +83,21 @@ private:
int top_left_width = 100;
int bottom_left_width = 100;
int input_width = 100;
int input_small_width = 80;
int window_offset = 100;
int window_offset_x = 100;
int window_offset_y = 100;
int window_padding = 0;
// trunc model name when longer
size_t max_char_in_name = 30;
};
std::optional<GuiCfg> gui_cfg;
void create_gui_cfg();
std::optional<GuiCfg> m_gui_cfg;
// translations used for calc window size
const std::string tr_mesh_name;
const std::string tr_triangles;
const std::string tr_preview;
const std::string tr_detail_level;
const std::string tr_decimate_ratio;
};
} // namespace GUI

View file

@ -132,8 +132,7 @@ bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_textur
bool res = false;
if (!arrow_texture.filename.empty())
res = m_arrow_texture.texture.load_from_file(path + arrow_texture.filename, false, GLTexture::SingleThreaded, false);
// res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, true, false, 100);
res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000);
if (res)
m_arrow_texture.metadata = arrow_texture;
@ -1019,7 +1018,7 @@ void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_t
float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width();
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } });
GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } });
break;
}
zoomed_top_y -= zoomed_stride_y;

View file

@ -3,6 +3,7 @@
#include "format.hpp"
#include "I18N.hpp"
#include "GUI_ObjectList.hpp"
#include "GLCanvas3D.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Config.hpp"
@ -13,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 {
@ -30,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,
@ -175,20 +236,19 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag
}
void launch_browser_if_allowed(const std::string& url)
{
if (wxGetApp().app_config->get("suppress_hyperlinks") != "1")
wxLaunchDefaultBrowser(url);
wxGetApp().open_browser_with_warning_dialog(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)
{
@ -209,15 +269,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
@ -275,52 +342,59 @@ 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, []() { wxGetApp().obj_list()->load_shape_object_from_gallery(); } };
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(); } };
m_loaded_hints.emplace_back(hint_data);
}
} 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())
{
@ -328,23 +402,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
@ -840,23 +988,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

@ -35,7 +35,9 @@ protected:
void prepare() override;
void on_exception(const std::exception_ptr &) override;
void process() override;
public:
ArrangeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
: PlaterJob{std::move(pri), plater}
@ -46,8 +48,6 @@ public:
return int(m_selected.size() + m_unprintable.size());
}
void process() override;
void finalize() override;
};

View file

@ -147,26 +147,28 @@ void FillBedJob::finalize()
size_t inst_cnt = model_object->instances.size();
for (ArrangePolygon &ap : m_selected) {
if (ap.bed_idx != arrangement::UNARRANGED && (ap.priority != 0 || ap.bed_idx == 0))
ap.apply();
}
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);
});
model_object->ensure_on_bed();
if (added_cnt > 0) {
for (ArrangePolygon &ap : m_selected) {
if (ap.bed_idx != arrangement::UNARRANGED && (ap.priority != 0 || ap.bed_idx == 0))
ap.apply();
}
m_plater->update();
model_object->ensure_on_bed();
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);
});
m_plater->update();
// FIXME: somebody explain why this is needed for increase_object_instances
if (inst_cnt == 1) added_cnt++;
// 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

@ -24,6 +24,7 @@ class FillBedJob : public PlaterJob
protected:
void prepare() override;
void process() override;
public:
FillBedJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
@ -35,8 +36,6 @@ public:
return m_status_range;
}
void process() override;
void finalize() override;
};

View file

@ -49,11 +49,20 @@ protected:
// Launched just before start(), a job can use it to prepare internals
virtual void prepare() {}
// The method where the actual work of the job should be defined.
virtual void process() = 0;
// Launched when the job is finished. It refreshes the 3Dscene by def.
virtual void finalize() { m_finalized = true; }
virtual void on_exception(const std::exception_ptr &) {}
// Exceptions occuring in process() are redirected from the worker thread
// into the main (UI) thread. This method is called from the main thread and
// can be overriden to handle these exceptions.
virtual void on_exception(const std::exception_ptr &eptr)
{
if (eptr) std::rethrow_exception(eptr);
}
public:
Job(std::shared_ptr<ProgressIndicator> pri);
@ -65,8 +74,6 @@ public:
Job &operator=(const Job &) = delete;
Job &operator=(Job &&) = delete;
virtual void process() = 0;
void start();
// To wait for the running job and join the threads. False is

View file

@ -48,14 +48,14 @@ class RotoptimizeJob : public PlaterJob
protected:
void prepare() override;
void process() override;
public:
RotoptimizeJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater)
: PlaterJob{std::move(pri), plater}
{}
void process() override;
void finalize() override;
static constexpr size_t get_methods_count() { return std::size(Methods); }

View file

@ -10,18 +10,16 @@ class SLAImportJob : public PlaterJob {
std::unique_ptr<priv> p;
protected:
void prepare() override;
void process() override;
void finalize() override;
public:
SLAImportJob(std::shared_ptr<ProgressIndicator> pri, Plater *plater);
~SLAImportJob();
void process() override;
void reset();
protected:
void prepare() override;
void finalize() override;
};
}} // namespace Slic3r::GUI

View file

@ -618,8 +618,11 @@ void MainFrame::update_title()
// Don't try to remove the extension, it would remove part of the file name after the last dot!
wxString project = from_path(into_path(m_plater->get_project_filename()).filename());
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
if (!dirty_marker.empty() || !project.empty())
if (!dirty_marker.empty() || !project.empty()) {
if (!dirty_marker.empty() && project.empty())
project = _("Untitled");
title = dirty_marker + project + " - ";
}
}
std::string build_id = wxGetApp().is_editor() ? SLIC3R_BUILD_ID : GCODEVIEWER_BUILD_ID;
@ -1054,7 +1057,7 @@ static wxMenu* generate_help_menu()
append_menu_item(helpMenu, wxID_ANY, _L("Prusa 3D &Drivers"), _L("Open the Prusa3D drivers download page in your browser"),
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/downloads"); });
append_menu_item(helpMenu, wxID_ANY, _L("Software &Releases"), _L("Open the software releases page in your browser"),
[](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); });
[](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); });
//# my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", "Check for new Slic3r versions", sub{
//# wxTheApp->check_version(1);
//# });
@ -1064,14 +1067,14 @@ static wxMenu* generate_help_menu()
[](wxCommandEvent&) { wxGetApp().open_web_page_localized("https://www.prusa3d.com/slicerweb"); });
// append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("%s &Manual"), SLIC3R_APP_NAME),
// wxString::Format(_L("Open the %s manual in your browser"), SLIC3R_APP_NAME),
// [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://manual.slic3r.org/"); });
// [this](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("http://manual.slic3r.org/"); });
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _L("System &Info"), _L("Show system information"),
[](wxCommandEvent&) { wxGetApp().system_info(); });
append_menu_item(helpMenu, wxID_ANY, _L("Show &Configuration Folder"), _L("Show user configuration folder (datadir)"),
[](wxCommandEvent&) { Slic3r::GUI::desktop_open_datadir_folder(); });
append_menu_item(helpMenu, wxID_ANY, _L("Report an I&ssue"), wxString::Format(_L("Report an issue on %s"), SLIC3R_APP_NAME),
[](wxCommandEvent&) { wxLaunchDefaultBrowser("https://github.com/prusa3d/slic3r/issues/new"); });
[](wxCommandEvent&) { wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/slic3r/issues/new"); });
if (wxGetApp().is_editor())
append_menu_item(helpMenu, wxID_ANY, wxString::Format(_L("&About %s"), SLIC3R_APP_NAME), _L("Show about dialog"),
[](wxCommandEvent&) { Slic3r::GUI::about(); });

View file

@ -4,6 +4,7 @@
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleMeshSlicer.hpp"
#include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Model.hpp"
#include "slic3r/GUI/Camera.hpp"
@ -225,7 +226,7 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
// Also, remove anything below the bed (sinking objects).
for (i=0; i<hits.size(); ++i) {
Vec3d transformed_hit = trafo * hits[i].position();
if (transformed_hit.z() >= 0. &&
if (transformed_hit.z() >= SINKING_Z_THRESHOLD &&
(! clipping_plane || ! clipping_plane->is_point_clipped(transformed_hit)))
break;
}

View file

@ -42,12 +42,12 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat
}
},
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
wxLaunchDefaultBrowser("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
_u8L("You have just added a G-code for color change, but its value is empty.\n"
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotification, 10,
_u8L("This model doesn't allow to automatically add the color changes") },
_u8L("No color change event was added to the print. The print does not look like a sign.") },
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotification, 10,
_u8L("Desktop integration was successful.") },
{NotificationType::DesktopIntegrationFail, NotificationLevel::WarningNotification, 10,

View file

@ -96,7 +96,9 @@ enum class NotificationType
DidYouKnowHint,
// Shows when ObjectList::update_info_items finds information that should be stressed to the user
// Might contain logo taken from gizmos
UpdatedItemsInfo
UpdatedItemsInfo,
// Give user advice to simplify object with big amount of triangles
SimplifySuggestion
};
class NotificationManager
@ -538,7 +540,13 @@ private:
// Timestamp of last rendering
int64_t m_last_render { 0LL };
// Notification types that can be shown multiple types at once (compared by text)
const std::vector<NotificationType> m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload, NotificationType::UpdatedItemsInfo };
const std::vector<NotificationType> m_multiple_types = {
NotificationType::CustomNotification,
NotificationType::PlaterWarning,
NotificationType::ProgressBar,
NotificationType::PrintHostUpload,
NotificationType::SimplifySuggestion
};
//prepared (basic) notifications
static const NotificationData 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

@ -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();
@ -1720,7 +1721,7 @@ struct Plater::priv
void replace_with_stl();
void reload_all_from_disk();
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void create_simplify_notification(const std::vector<size_t>& obj_ids);
void set_current_panel(wxPanel* panel);
void on_select_preset(wxCommandEvent&);
@ -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(); });
@ -2362,25 +2364,23 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// Convert even if the object is big.
convert_from_imperial_units(model, false);
else if (model.looks_like_saved_in_meters()) {
//wxMessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
MessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
"The object in file %s looks like saved in meters.\n"
"Should I consider it as a saved in meters and convert it?",
"Some objects in file %s look like saved in meters.\n"
"Should I consider them as a saved in meters and convert them?", model.objects.size()), from_path(filename)) + "\n",
_L("The object appears to be saved in meters"), wxICON_WARNING | wxYES | wxNO);
"The dimensions of the object from file %s seem to be defined in meters.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
"The dimensions of some objects from file %s seem to be defined in meters.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES)
//FIXME up-scale only the small parts?
model.convert_from_meters(true);
}
else if (model.looks_like_imperial_units()) {
//wxMessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
MessageDialog msg_dlg(q, format_wxstr(_L_PLURAL(
"The object in file %s looks like saved in inches.\n"
"Should I consider it as a saved in inches and convert it?",
"Some objects in file %s look like saved in inches.\n"
"Should I consider them as a saved in inches and convert them?", model.objects.size()), from_path(filename)) + "\n",
_L("The object appears to be saved in inches"), wxICON_WARNING | wxYES | wxNO);
"The dimensions of the object from file %s seem to be defined in inches.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of the object?",
"The dimensions of some objects from file %s seem to be defined in inches.\n"
"The internal unit of PrusaSlicer are millimeters. Do you want to recalculate the dimensions of these objects?", model.objects.size()), from_path(filename)) + "\n",
_L("The object is too small"), wxICON_WARNING | wxYES | wxNO);
if (msg_dlg.ShowModal() == wxID_YES)
//FIXME up-scale only the small parts?
convert_from_imperial_units(model, true);
@ -2428,6 +2428,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
if (one_by_one) {
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 {
@ -2476,6 +2478,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
view3D->get_canvas3d()->update_gizmos_on_off_state();
}
create_simplify_notification(obj_idxs);
return obj_idxs;
}
@ -2753,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"));
@ -2944,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.
@ -3509,6 +3542,53 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
q->SetFocus();
}
void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_ids) {
const uint32_t triangles_to_suggest_simplify = 1000000;
std::vector<size_t> big_ids;
big_ids.reserve(obj_ids.size());
std::copy_if(obj_ids.begin(), obj_ids.end(), std::back_inserter(big_ids),
[this, triangles_to_suggest_simplify](size_t object_id) {
if (object_id >= model.objects.size()) return false; // out of object index
ModelVolumePtrs& volumes = model.objects[object_id]->volumes;
if (volumes.size() != 1) return false; // not only one volume
size_t triangle_count = volumes.front()->mesh().its.indices.size();
if (triangle_count < triangles_to_suggest_simplify) return false; // small volume
return true;
});
if (big_ids.empty()) return;
for (size_t object_id : big_ids) {
std::string t = _u8L(
"Processing model '@object_name' with more than 1M triangles "
"could be slow. It is highly recommend to reduce "
"amount of triangles.");
t.replace(t.find("@object_name"), sizeof("@object_name") - 1,
model.objects[object_id]->name);
std::stringstream text;
text << _u8L("WARNING:") << "\n" << t << "\n";
std::string hypertext = _u8L("Simplify model");
std::function<bool(wxEvtHandler *)> open_simplify = [object_id](wxEvtHandler *) {
auto plater = wxGetApp().plater();
if (object_id >= plater->model().objects.size()) return true;
Selection &selection = plater->canvas3D()->get_selection();
selection.clear();
selection.add_object((unsigned int) object_id);
auto &manager = plater->canvas3D()->get_gizmos_manager();
manager.open_gizmo(GLGizmosManager::EType::Simplify);
return true;
};
notification_manager->push_notification(
NotificationType::SimplifySuggestion,
NotificationManager::NotificationLevel::WarningNotification,
text.str(), hypertext, open_simplify);
}
}
void Plater::priv::set_current_panel(wxPanel* panel)
{
if (std::find(panels.begin(), panels.end(), panel) == panels.end())
@ -4285,6 +4365,12 @@ bool Plater::priv::can_fix_through_netfabb() const
bool Plater::priv::can_simplify() const
{
// is object for simplification selected
if (get_selected_object_idx() < 0) return false;
// is already opened?
if (q->canvas3D()->get_gizmos_manager().get_current_type() ==
GLGizmosManager::EType::Simplify)
return false;
return true;
}
@ -4703,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();
@ -4741,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();
}
@ -4876,7 +4959,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string& filename)
main_sizer->Add(new wxStaticText(this, wxID_ANY,
_L("Select an action to apply to the file") + ": " + from_u8(filename)), 0, wxEXPAND | wxALL, 10);
int action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
m_action = std::clamp(std::stoi(wxGetApp().app_config->get("drop_project_action")),
static_cast<int>(LoadType::OpenProject), static_cast<int>(LoadType::LoadConfig)) - 1;
wxStaticBox* action_stb = new wxStaticBox(this, wxID_ANY, _L("Action"));
@ -4887,7 +4970,7 @@ ProjectDropDialog::ProjectDropDialog(const std::string& filename)
int id = 0;
for (const wxString& label : choices) {
wxRadioButton* btn = new wxRadioButton(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, id == 0 ? wxRB_GROUP : 0);
btn->SetValue(id == action);
btn->SetValue(id == m_action);
btn->Bind(wxEVT_RADIOBUTTON, [this, id](wxCommandEvent&) { m_action = id; });
stb_sizer->Add(btn, 0, wxEXPAND | wxTOP, 5);
id++;
@ -6172,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,44 @@ 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);
}
}
if (is_any_volume()) {
for (unsigned int i : m_list) {
GLVolume& volume = *(*m_volumes)[i];
std::pair<int, int> instance = std::make_pair(volume.object_idx(), volume.instance_idx());
InstancesToZMap::iterator it = instances_max_z.find(instance);
double z_shift = SINKING_MIN_Z_THRESHOLD - it->second;
if (it != instances_max_z.end() && z_shift > 0.0)
volume.set_volume_offset(Z, volume.get_volume_offset(Z) + z_shift);
}
}
else {
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

@ -1747,14 +1747,14 @@ bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode)
std::vector<std::string> tags;
bool invalid = GCodeProcessor::contains_reserved_tags(gcode, 5, tags);
if (invalid) {
wxString reports = _L_PLURAL("The following line", "The following lines", tags.size());
reports += ":\n";
for (const std::string& keyword : tags) {
reports += ";" + keyword + "\n";
}
reports += _L("contain reserved keywords.") + "\n";
reports += _L("Please remove them, as they may cause problems in g-code visualization and printing time estimation.");
std::string lines = ":\n";
for (const std::string& keyword : tags)
lines += ";" + keyword + "\n";
wxString reports = format_wxstr(
_L_PLURAL("The following line %s contains reserved keywords.\nPlease remove it, as it may cause problems in G-code visualization and printing time estimation.",
"The following lines %s contain reserved keywords.\nPlease remove them, as they may cause problems in G-code visualization and printing time estimation.",
tags.size()),
lines);
//wxMessageDialog dialog(wxGetApp().mainframe, reports, _L("Found reserved keywords in") + " " + _(title), wxICON_WARNING | wxOK);
MessageDialog dialog(wxGetApp().mainframe, reports, _L("Found reserved keywords in") + " " + _(title), wxICON_WARNING | wxOK);
dialog.ShowModal();

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;
};
}