mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 16:21:24 -06:00
Fixed conflicts after merge with master
This commit is contained in:
commit
526700db76
89 changed files with 2002 additions and 840 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
12
src/hints/CMakeLists.txt
Normal 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
84
src/hints/HintsToPot.cpp
Normal 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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
project(libigl)
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
project(libigl)
|
||||
|
||||
add_library(libigl INTERFACE)
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¤t_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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define EXPENSIVE_DEBUG_CHECKS
|
||||
// #define EXPENSIVE_DEBUG_CHECKS
|
||||
#endif // NDEBUG
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
cmake_minimum_required(VERSION 2.8.12)
|
||||
project(miniz)
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
|
||||
add_library(miniz INTERFACE)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) = @_;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(); });
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[];
|
||||
};
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue