mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-22 00:01:09 -06:00
Fixed conflicts after merge with master
This commit is contained in:
commit
3369e1b599
85 changed files with 2018 additions and 969 deletions
|
@ -12,7 +12,7 @@ namespace Slic3r {
|
|||
#ifdef WIN32
|
||||
|
||||
//only dll name with .dll suffix - currently case sensitive
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll" });
|
||||
const std::vector<std::wstring> BlacklistedLibraryCheck::blacklist({ L"NahimicOSD.dll", L"SS2OSD.dll" });
|
||||
|
||||
bool BlacklistedLibraryCheck::get_blacklisted(std::vector<std::wstring>& names)
|
||||
{
|
||||
|
@ -33,22 +33,20 @@ std::wstring BlacklistedLibraryCheck::get_blacklisted_string()
|
|||
|
||||
bool BlacklistedLibraryCheck::perform_check()
|
||||
{
|
||||
// Get a handle to the process.
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, GetCurrentProcessId());
|
||||
if (NULL == hProcess)
|
||||
return false;
|
||||
// Get the pseudo-handle for the current process.
|
||||
HANDLE hCurrentProcess = GetCurrentProcess();
|
||||
|
||||
// Get a list of all the modules in this process.
|
||||
HMODULE hMods[1024];
|
||||
DWORD cbNeeded;
|
||||
if (EnumProcessModulesEx(hProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
|
||||
if (EnumProcessModulesEx(hCurrentProcess, hMods, sizeof(hMods), &cbNeeded, LIST_MODULES_ALL))
|
||||
{
|
||||
//printf("Total Dlls: %d\n", cbNeeded / sizeof(HMODULE));
|
||||
for (unsigned int i = 0; i < cbNeeded / sizeof(HMODULE); ++ i)
|
||||
{
|
||||
wchar_t szModName[MAX_PATH];
|
||||
// Get the full path to the module's file.
|
||||
if (GetModuleFileNameExW(hProcess, hMods[i], szModName, MAX_PATH))
|
||||
if (GetModuleFileNameExW(hCurrentProcess, hMods[i], szModName, MAX_PATH))
|
||||
{
|
||||
// Add to list if blacklisted
|
||||
if (BlacklistedLibraryCheck::is_blacklisted(szModName)) {
|
||||
|
@ -61,7 +59,6 @@ bool BlacklistedLibraryCheck::perform_check()
|
|||
}
|
||||
}
|
||||
|
||||
CloseHandle(hProcess);
|
||||
//printf("\n");
|
||||
return !m_found.empty();
|
||||
}
|
||||
|
|
|
@ -134,10 +134,10 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
|
|||
Polygons islands;
|
||||
for (const PrintObject *object : top_level_objects_with_brim) {
|
||||
//FIXME how about the brim type?
|
||||
auto brim_offset = float(scale_(object->config().brim_offset.value));
|
||||
auto brim_separation = float(scale_(object->config().brim_separation.value));
|
||||
Polygons islands_object;
|
||||
for (const ExPolygon &ex_poly : get_print_object_bottom_layer_expolygons(*object)) {
|
||||
Polygons contour_offset = offset(ex_poly.contour, brim_offset);
|
||||
Polygons contour_offset = offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare);
|
||||
for (Polygon &poly : contour_offset)
|
||||
poly.douglas_peucker(SCALED_RESOLUTION);
|
||||
|
||||
|
@ -166,7 +166,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print
|
|||
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
|
||||
const PrintObject *object = print.objects()[print_object_idx];
|
||||
const BrimType brim_type = object->config().brim_type.value;
|
||||
const float brim_offset = scale_(object->config().brim_offset.value);
|
||||
const float brim_separation = scale_(object->config().brim_separation.value);
|
||||
const float brim_width = scale_(object->config().brim_width.value);
|
||||
const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
|
||||
|
||||
|
@ -174,16 +174,16 @@ static ExPolygons top_level_outer_brim_area(const Print &print
|
|||
ExPolygons no_brim_area_object;
|
||||
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
|
||||
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim)
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_separation, ClipperLib::jtSquare), offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare)));
|
||||
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
|
||||
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset, ClipperLib::jtSquare));
|
||||
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
||||
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly.holes));
|
||||
|
||||
if (brim_type != BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_offset));
|
||||
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_separation, ClipperLib::jtSquare));
|
||||
|
||||
no_brim_area_object.emplace_back(ex_poly.contour);
|
||||
}
|
||||
|
@ -212,11 +212,11 @@ static ExPolygons inner_brim_area(const Print &print,
|
|||
ExPolygons no_brim_area;
|
||||
Polygons holes;
|
||||
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
|
||||
const PrintObject *object = print.objects()[print_object_idx];
|
||||
const BrimType brim_type = object->config().brim_type.value;
|
||||
const float brim_offset = scale_(object->config().brim_offset.value);
|
||||
const float brim_width = scale_(object->config().brim_width.value);
|
||||
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
|
||||
const PrintObject *object = print.objects()[print_object_idx];
|
||||
const BrimType brim_type = object->config().brim_type.value;
|
||||
const float brim_separation = scale_(object->config().brim_separation.value);
|
||||
const float brim_width = scale_(object->config().brim_width.value);
|
||||
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
|
||||
|
||||
ExPolygons brim_area_object;
|
||||
ExPolygons no_brim_area_object;
|
||||
|
@ -226,21 +226,21 @@ static ExPolygons inner_brim_area(const Print &print,
|
|||
if (top_outer_brim)
|
||||
no_brim_area_object.emplace_back(ex_poly);
|
||||
else
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_separation, ClipperLib::jtSquare), offset(ex_poly.contour, brim_separation, ClipperLib::jtSquare)));
|
||||
}
|
||||
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
|
||||
append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
|
||||
append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_separation, ClipperLib::jtSquare), offset_ex(ex_poly.holes, -brim_width - brim_separation, ClipperLib::jtSquare)));
|
||||
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
||||
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly.holes));
|
||||
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
|
||||
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset, ClipperLib::jtSquare));
|
||||
|
||||
append(holes_object, ex_poly.holes);
|
||||
}
|
||||
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_offset));
|
||||
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_separation, ClipperLib::jtSquare));
|
||||
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
append_and_translate(brim_area, brim_area_object, instance);
|
||||
|
@ -356,12 +356,12 @@ static void make_inner_brim(const Print &print,
|
|||
Flow flow = print.brim_flow();
|
||||
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
|
||||
Polygons loops;
|
||||
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare);
|
||||
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), ClipperLib::jtSquare);
|
||||
for (size_t i = 0; !islands_ex.empty(); ++i) {
|
||||
for (ExPolygon &poly_ex : islands_ex)
|
||||
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
polygons_append(loops, to_polygons(islands_ex));
|
||||
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare);
|
||||
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), ClipperLib::jtSquare);
|
||||
}
|
||||
|
||||
loops = union_pt_chained_outside_in(loops);
|
||||
|
@ -385,7 +385,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||
size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing()));
|
||||
for (size_t i = 0; i < num_loops; ++i) {
|
||||
try_cancel();
|
||||
islands = offset(islands, float(flow.scaled_spacing()), jtSquare);
|
||||
islands = offset(islands, float(flow.scaled_spacing()), ClipperLib::jtSquare);
|
||||
for (Polygon &poly : islands)
|
||||
poly.douglas_peucker(SCALED_RESOLUTION);
|
||||
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
|
||||
|
|
|
@ -49,6 +49,17 @@ const unsigned int VERSION_3MF = 1;
|
|||
const unsigned int VERSION_3MF_COMPATIBLE = 2;
|
||||
const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
|
||||
|
||||
// Painting gizmos data version numbers
|
||||
// 0 : 3MF files saved by older PrusaSlicer or the painting gizmo wasn't used. No version definition in them.
|
||||
// 1 : Introduction of painting gizmos data versioning. No other changes in painting gizmos data.
|
||||
const unsigned int FDM_SUPPORTS_PAINTING_VERSION = 1;
|
||||
const unsigned int SEAM_PAINTING_VERSION = 1;
|
||||
const unsigned int MM_PAINTING_VERSION = 1;
|
||||
|
||||
const std::string SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION = "slic3rpe:FdmSupportsPaintingVersion";
|
||||
const std::string SLIC3RPE_SEAM_PAINTING_VERSION = "slic3rpe:SeamPaintingVersion";
|
||||
const std::string SLIC3RPE_MM_PAINTING_VERSION = "slic3rpe:MmPaintingVersion";
|
||||
|
||||
const std::string MODEL_FOLDER = "3D/";
|
||||
const std::string MODEL_EXTENSION = ".model";
|
||||
const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
|
||||
|
@ -393,6 +404,10 @@ namespace Slic3r {
|
|||
unsigned int m_version;
|
||||
bool m_check_version;
|
||||
|
||||
unsigned int m_fdm_supports_painting_version = 0;
|
||||
unsigned int m_seam_painting_version = 0;
|
||||
unsigned int m_mm_painting_version = 0;
|
||||
|
||||
XML_Parser m_xml_parser;
|
||||
// Error code returned by the application side of the parser. In that case the expat may not reliably deliver the error state
|
||||
// after returning from XML_Parse() function, thus we keep the error state here.
|
||||
|
@ -420,6 +435,7 @@ namespace Slic3r {
|
|||
~_3MF_Importer();
|
||||
|
||||
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
|
||||
unsigned int version() const { return m_version; }
|
||||
|
||||
private:
|
||||
void _destroy_xml_parser();
|
||||
|
@ -542,6 +558,9 @@ namespace Slic3r {
|
|||
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version)
|
||||
{
|
||||
m_version = 0;
|
||||
m_fdm_supports_painting_version = 0;
|
||||
m_seam_painting_version = 0;
|
||||
m_mm_painting_version = 0;
|
||||
m_check_version = check_version;
|
||||
m_model = &model;
|
||||
m_unit_factor = 1.0f;
|
||||
|
@ -1668,6 +1687,12 @@ namespace Slic3r {
|
|||
return true;
|
||||
}
|
||||
|
||||
inline static void check_painting_version(unsigned int loaded_version, unsigned int highest_supported_version, const std::string &error_msg)
|
||||
{
|
||||
if (loaded_version > highest_supported_version)
|
||||
throw version_error(error_msg);
|
||||
}
|
||||
|
||||
bool _3MF_Importer::_handle_end_metadata()
|
||||
{
|
||||
if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION) {
|
||||
|
@ -1680,6 +1705,24 @@ namespace Slic3r {
|
|||
}
|
||||
}
|
||||
|
||||
if (m_curr_metadata_name == SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION) {
|
||||
m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
|
||||
check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
|
||||
_(L("The selected 3MF contains FDM supports painted object using a newer version of PrusaSlicer and is not compatible.")));
|
||||
}
|
||||
|
||||
if (m_curr_metadata_name == SLIC3RPE_SEAM_PAINTING_VERSION) {
|
||||
m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
|
||||
check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
|
||||
_(L("The selected 3MF contains seam painted object using a newer version of PrusaSlicer and is not compatible.")));
|
||||
}
|
||||
|
||||
if (m_curr_metadata_name == SLIC3RPE_MM_PAINTING_VERSION) {
|
||||
m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
|
||||
check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
|
||||
_(L("The selected 3MF contains multi-material painted object using a newer version of PrusaSlicer and is not compatible.")));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2293,6 +2336,16 @@ namespace Slic3r {
|
|||
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||
stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
|
||||
|
||||
if (model.is_fdm_support_painted())
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_FDM_SUPPORTS_PAINTING_VERSION << "\">" << FDM_SUPPORTS_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
|
||||
|
||||
if (model.is_seam_painted())
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_SEAM_PAINTING_VERSION << "\">" << SEAM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
|
||||
|
||||
if (model.is_mm_painted())
|
||||
stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_MM_PAINTING_VERSION << "\">" << MM_PAINTING_VERSION << "</" << METADATA_TAG << ">\n";
|
||||
|
||||
std::string name = xml_escape(boost::filesystem::path(filename).stem().string());
|
||||
stream << " <" << METADATA_TAG << " name=\"Title\">" << name << "</" << METADATA_TAG << ">\n";
|
||||
stream << " <" << METADATA_TAG << " name=\"Designer\">" << "</" << METADATA_TAG << ">\n";
|
||||
|
@ -2613,9 +2666,16 @@ namespace Slic3r {
|
|||
|
||||
bool _3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items)
|
||||
{
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
// This happens for empty projects
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (build_items.size() == 0) {
|
||||
add_error("No build item found");
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
}
|
||||
|
||||
stream << " <" << BUILD_TAG << ">\n";
|
||||
|
@ -2990,6 +3050,19 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
|
|||
return true;
|
||||
}
|
||||
|
||||
// Perform conversions based on the config values available.
|
||||
//FIXME provide a version of PrusaSlicer that stored the project file (3MF).
|
||||
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config)
|
||||
{
|
||||
if (! config.has("brim_separation")) {
|
||||
if (auto *opt_elephant_foot = config.option<ConfigOptionFloat>("elefant_foot_compensation", false); opt_elephant_foot) {
|
||||
// Conversion from older PrusaSlicer which applied brim separation equal to elephant foot compensation.
|
||||
auto *opt_brim_separation = config.option<ConfigOptionFloat>("brim_separation", true);
|
||||
opt_brim_separation->value = opt_elephant_foot->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version)
|
||||
{
|
||||
if (path == nullptr || model == nullptr)
|
||||
|
@ -3000,6 +3073,7 @@ bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionCo
|
|||
_3MF_Importer importer;
|
||||
bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
|
||||
importer.log_errors();
|
||||
handle_legacy_project_loaded(importer.version(), config);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
@ -744,27 +744,28 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
std::string path_tmp(path);
|
||||
path_tmp += ".tmp";
|
||||
|
||||
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
|
||||
if (file == nullptr)
|
||||
m_processor.initialize(path_tmp);
|
||||
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
|
||||
if (! file.is_open())
|
||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||
|
||||
try {
|
||||
m_placeholder_parser_failed_templates.clear();
|
||||
this->_do_export(*print, file, thumbnail_cb);
|
||||
fflush(file);
|
||||
if (ferror(file)) {
|
||||
fclose(file);
|
||||
file.flush();
|
||||
if (file.is_error()) {
|
||||
file.close();
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
||||
}
|
||||
} catch (std::exception & /* ex */) {
|
||||
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
|
||||
// Close and remove the file.
|
||||
fclose(file);
|
||||
file.close();
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
throw;
|
||||
}
|
||||
fclose(file);
|
||||
file.close();
|
||||
|
||||
if (! m_placeholder_parser_failed_templates.empty()) {
|
||||
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
|
||||
|
@ -782,7 +783,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info();
|
||||
m_processor.process_file(path_tmp, true, [print]() { print->throw_if_canceled(); });
|
||||
m_processor.finalize();
|
||||
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
|
||||
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics);
|
||||
if (result != nullptr) {
|
||||
|
@ -1046,7 +1047,7 @@ std::vector<const PrintInstance*> sort_object_instances_by_model_order(const Pri
|
|||
return instances;
|
||||
}
|
||||
|
||||
void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
|
@ -1111,10 +1112,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
// Write information on the generator.
|
||||
_write_format(file, "; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str());
|
||||
|
||||
DoExport::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
|
||||
[this, file](const char* sz) { this->_write(file, sz); },
|
||||
[&file](const char* sz) { file.write(sz); },
|
||||
[&print]() { print.throw_if_canceled(); });
|
||||
|
||||
// Write notes (content of the Print Settings tab -> Notes)
|
||||
|
@ -1125,10 +1126,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Remove the trailing '\r' from the '\r\n' sequence.
|
||||
if (! line.empty() && line.back() == '\r')
|
||||
line.pop_back();
|
||||
_write_format(file, "; %s\n", line.c_str());
|
||||
file.write_format("; %s\n", line.c_str());
|
||||
}
|
||||
if (! lines.empty())
|
||||
_write(file, "\n");
|
||||
file.write("\n");
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
||||
|
@ -1139,22 +1140,22 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
const double first_layer_height = print.config().first_layer_height.value;
|
||||
for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) {
|
||||
const PrintRegion ®ion = print.get_print_region(region_id);
|
||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
|
||||
_write_format(file, "; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
|
||||
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
|
||||
_write_format(file, "; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
|
||||
file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||
file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width());
|
||||
file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width());
|
||||
file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width());
|
||||
file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width());
|
||||
if (print.has_support_material())
|
||||
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
||||
file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
||||
if (print.config().first_layer_extrusion_width.value > 0)
|
||||
_write_format(file, "; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
|
||||
_write_format(file, "\n");
|
||||
file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width());
|
||||
file.write_format("\n");
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
||||
// adds tags for time estimators
|
||||
if (print.config().remaining_times.value)
|
||||
_write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str());
|
||||
|
||||
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
|
||||
m_placeholder_parser = print.placeholder_parser();
|
||||
|
@ -1218,7 +1219,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
|
||||
// Disable fan.
|
||||
if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
|
||||
_write(file, m_writer.set_fan(0, true));
|
||||
file.write(m_writer.set_fan(0, true));
|
||||
|
||||
// Let the start-up script prime the 1st printing tool.
|
||||
m_placeholder_parser.set("initial_tool", initial_extruder_id);
|
||||
|
@ -1261,10 +1262,10 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, false);
|
||||
|
||||
// adds tag for processor
|
||||
_write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
|
||||
// Write the custom start G-code
|
||||
_writeln(file, start_gcode);
|
||||
file.writeln(start_gcode);
|
||||
|
||||
// Process filament-specific gcode.
|
||||
/* if (has_wipe_tower) {
|
||||
|
@ -1272,14 +1273,14 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
} else {
|
||||
DynamicConfig config;
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
|
||||
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
|
||||
file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
|
||||
}
|
||||
*/
|
||||
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
|
||||
print.throw_if_canceled();
|
||||
|
||||
// Set other general things.
|
||||
_write(file, this->preamble());
|
||||
file.write(this->preamble());
|
||||
|
||||
// Calculate wiping points if needed
|
||||
DoExport::init_ooze_prevention(print, m_ooze_prevention);
|
||||
|
@ -1291,7 +1292,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
|
||||
// Set initial extruder only after custom start G-code.
|
||||
// Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
|
||||
_write(file, this->set_extruder(initial_extruder_id, 0.));
|
||||
file.write(this->set_extruder(initial_extruder_id, 0.));
|
||||
}
|
||||
|
||||
// Do all objects for each layer.
|
||||
|
@ -1317,8 +1318,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
|
||||
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
|
||||
m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
_write(file, this->retract());
|
||||
_write(file, this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
file.write(this->retract());
|
||||
file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object"));
|
||||
m_enable_cooling_markers = true;
|
||||
// Disable motion planner when traveling to first object point.
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
|
@ -1330,7 +1331,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Set first layer bed and extruder temperatures, don't wait for it to reach the temperature.
|
||||
this->_print_first_layer_bed_temperature(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
this->_print_first_layer_extruder_temperatures(file, print, between_objects_gcode, initial_extruder_id, false);
|
||||
_writeln(file, between_objects_gcode);
|
||||
file.writeln(between_objects_gcode);
|
||||
}
|
||||
// Reset the cooling buffer internal state (the current position, feed rate, accelerations).
|
||||
m_cooling_buffer->reset();
|
||||
|
@ -1346,7 +1347,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
}
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
_write(file, m_pressure_equalizer->process("", true));
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
++ finished_objects;
|
||||
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||
|
@ -1361,9 +1362,9 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Prusa Multi-Material wipe tower.
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
|
||||
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
if (print.config().single_extruder_multi_material_priming) {
|
||||
_write(file, m_wipe_tower->prime(*this));
|
||||
file.write(m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
|
@ -1375,15 +1376,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
bool overlap = bbox_prime.overlap(bbox_print);
|
||||
|
||||
if (print.config().gcode_flavor == gcfMarlinLegacy || print.config().gcode_flavor == gcfMarlinFirmware) {
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
|
||||
file.write(this->retract());
|
||||
file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
|
||||
if (overlap) {
|
||||
// Wait for the user to remove the priming extrusions.
|
||||
_write(file, "M1 Remove priming towers and click button.\n");
|
||||
file.write("M1 Remove priming towers and click button.\n");
|
||||
} else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
_write(file, "M1 S10\n");
|
||||
file.write("M1 S10\n");
|
||||
}
|
||||
} else {
|
||||
// This is not Marlin, M1 command is probably not supported.
|
||||
|
@ -1410,19 +1411,19 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
}
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
_write(file, m_pressure_equalizer->process("", true));
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
_write(file, m_wipe_tower->finalize(*this));
|
||||
file.write(m_wipe_tower->finalize(*this));
|
||||
}
|
||||
|
||||
// Write end commands to file.
|
||||
_write(file, this->retract());
|
||||
_write(file, m_writer.set_fan(false));
|
||||
file.write(this->retract());
|
||||
file.write(m_writer.set_fan(false));
|
||||
|
||||
// adds tag for processor
|
||||
_write_format(file, ";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
file.write_format(";%s%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role).c_str(), ExtrusionEntity::role_to_string(erCustom).c_str());
|
||||
|
||||
// Process filament-specific gcode in extruder order.
|
||||
{
|
||||
|
@ -1434,48 +1435,48 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Process the end_filament_gcode for the active filament only.
|
||||
int extruder_id = m_writer.extruder()->id();
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
|
||||
_writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config));
|
||||
file.writeln(this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(extruder_id), extruder_id, &config));
|
||||
} else {
|
||||
for (const std::string &end_gcode : print.config().end_filament_gcode.values) {
|
||||
int extruder_id = (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front());
|
||||
config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
|
||||
_writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config));
|
||||
file.writeln(this->placeholder_parser_process("end_filament_gcode", end_gcode, extruder_id, &config));
|
||||
}
|
||||
}
|
||||
_writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
|
||||
file.writeln(this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
|
||||
}
|
||||
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
_write(file, m_writer.postamble());
|
||||
file.write(m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
|
||||
file.write(m_writer.postamble());
|
||||
|
||||
// adds tags for time estimators
|
||||
if (print.config().remaining_times.value)
|
||||
_write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
|
||||
|
||||
print.throw_if_canceled();
|
||||
|
||||
// Get filament stats.
|
||||
_write(file, DoExport::update_print_stats_and_format_filament_stats(
|
||||
file.write(DoExport::update_print_stats_and_format_filament_stats(
|
||||
// Const inputs
|
||||
has_wipe_tower, print.wipe_tower_data(),
|
||||
m_writer.extruders(),
|
||||
// Modifies
|
||||
print.m_print_statistics));
|
||||
_write(file, "\n");
|
||||
_write_format(file, "; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight);
|
||||
_write_format(file, "; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
|
||||
file.write("\n");
|
||||
file.write_format("; total filament used [g] = %.2lf\n", print.m_print_statistics.total_weight);
|
||||
file.write_format("; total filament cost = %.2lf\n", print.m_print_statistics.total_cost);
|
||||
if (print.m_print_statistics.total_toolchanges > 0)
|
||||
_write_format(file, "; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
|
||||
_write_format(file, ";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
|
||||
file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges);
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
|
||||
|
||||
// Append full config, delimited by two 'phony' configuration keys prusaslicer_config = begin and prusaslicer_config = end.
|
||||
// The delimiters are structured as configuration key / value pairs to be parsable by older versions of PrusaSlicer G-code viewer.
|
||||
{
|
||||
_write(file, "\n; prusaslicer_config = begin\n");
|
||||
file.write("\n; prusaslicer_config = begin\n");
|
||||
std::string full_config;
|
||||
append_full_config(print, full_config);
|
||||
if (!full_config.empty())
|
||||
_write(file, full_config);
|
||||
_write(file, "; prusaslicer_config = end\n");
|
||||
file.write(full_config);
|
||||
file.write("; prusaslicer_config = end\n");
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
|
@ -1565,16 +1566,16 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
|
|||
|
||||
// Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
|
||||
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
|
||||
void GCode::print_machine_envelope(FILE *file, Print &print)
|
||||
void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
|
||||
{
|
||||
if ((print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware)
|
||||
&& print.config().machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) {
|
||||
fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
|
||||
file.write_format("M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
|
||||
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_z.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_e.values.front() + 0.5));
|
||||
fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
|
||||
file.write_format("M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
|
||||
int(print.config().machine_max_feedrate_x.values.front() + 0.5),
|
||||
int(print.config().machine_max_feedrate_y.values.front() + 0.5),
|
||||
int(print.config().machine_max_feedrate_z.values.front() + 0.5),
|
||||
|
@ -1587,18 +1588,18 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
|
|||
int travel_acc = print.config().gcode_flavor == gcfMarlinLegacy
|
||||
? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5)
|
||||
: int(print.config().machine_max_acceleration_travel.values.front() + 0.5);
|
||||
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
|
||||
file.write_format("M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
|
||||
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
|
||||
travel_acc);
|
||||
|
||||
assert(is_decimal_separator_point());
|
||||
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
file.write_format("M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
print.config().machine_max_jerk_x.values.front(),
|
||||
print.config().machine_max_jerk_y.values.front(),
|
||||
print.config().machine_max_jerk_z.values.front(),
|
||||
print.config().machine_max_jerk_e.values.front());
|
||||
fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
|
||||
file.write_format("M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
|
||||
int(print.config().machine_min_extruding_rate.values.front() + 0.5),
|
||||
int(print.config().machine_min_travel_rate.values.front() + 0.5));
|
||||
}
|
||||
|
@ -1608,7 +1609,7 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
|
|||
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
|
||||
// M140 - Set Extruder Temperature
|
||||
// M190 - Set Extruder Temperature and Wait
|
||||
void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
|
||||
void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
|
||||
{
|
||||
// Initial bed temperature based on the first extruder.
|
||||
int temp = print.config().first_layer_bed_temperature.get_at(first_printing_extruder_id);
|
||||
|
@ -1621,7 +1622,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
|
|||
// the custom start G-code emited these.
|
||||
std::string set_temp_gcode = m_writer.set_bed_temperature(temp, wait);
|
||||
if (! temp_set_by_gcode)
|
||||
_write(file, set_temp_gcode);
|
||||
file.write(set_temp_gcode);
|
||||
}
|
||||
|
||||
// Write 1st layer extruder temperatures into the G-code.
|
||||
|
@ -1629,7 +1630,7 @@ void GCode::_print_first_layer_bed_temperature(FILE *file, Print &print, const s
|
|||
// M104 - Set Extruder Temperature
|
||||
// M109 - Set Extruder Temperature and Wait
|
||||
// RepRapFirmware: G10 Sxx
|
||||
void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
|
||||
void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait)
|
||||
{
|
||||
// Is the bed temperature set by the provided custom G-code?
|
||||
int temp_by_gcode = -1;
|
||||
|
@ -1646,7 +1647,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
|
|||
// Set temperature of the first printing extruder only.
|
||||
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
|
||||
if (temp > 0)
|
||||
_write(file, m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
} else {
|
||||
// Set temperatures of all the printing extruders.
|
||||
for (unsigned int tool_id : print.extruders()) {
|
||||
|
@ -1654,7 +1655,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
|
|||
if (print.config().ooze_prevention.value)
|
||||
temp += print.config().standby_temperature_delta.value;
|
||||
if (temp > 0)
|
||||
_write(file, m_writer.set_temperature(temp, wait, tool_id));
|
||||
file.write(m_writer.set_temperature(temp, wait, tool_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1891,7 +1892,7 @@ namespace Skirt {
|
|||
// and performing the extruder specific extrusions together.
|
||||
void GCode::process_layer(
|
||||
// Write into the output file.
|
||||
FILE *file,
|
||||
GCodeOutputStream &file,
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
const std::vector<LayerToPrint> &layers,
|
||||
|
@ -1977,6 +1978,7 @@ void GCode::process_layer(
|
|||
}
|
||||
gcode += this->change_layer(print_z); // this will increase m_layer_index
|
||||
m_layer = &layer;
|
||||
m_object_layer_over_raft = false;
|
||||
if (! print.config().layer_gcode.value.empty()) {
|
||||
DynamicConfig config;
|
||||
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
|
||||
|
@ -2235,8 +2237,13 @@ void GCode::process_layer(
|
|||
gcode+="; PURGING FINISHED\n";
|
||||
|
||||
for (InstanceToPrint &instance_to_print : instances_to_print) {
|
||||
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
|
||||
// To control print speed of the 1st object layer printed over raft interface.
|
||||
bool object_layer_over_raft = layer_to_print.object_layer && layer_to_print.object_layer->id() > 0 &&
|
||||
instance_to_print.print_object.slicing_parameters().raft_layers() == layer_to_print.object_layer->id();
|
||||
m_config.apply(instance_to_print.print_object.config(), true);
|
||||
m_layer = layers[instance_to_print.layer_id].layer();
|
||||
m_layer = layer_to_print.layer();
|
||||
m_object_layer_over_raft = object_layer_over_raft;
|
||||
if (m_config.avoid_crossing_perimeters)
|
||||
m_avoid_crossing_perimeters.init_layer(*m_layer);
|
||||
if (this->config().gcode_label_objects)
|
||||
|
@ -2249,11 +2256,13 @@ void GCode::process_layer(
|
|||
m_last_obj_copy = this_object_copy;
|
||||
this->set_origin(unscale(offset));
|
||||
if (instance_to_print.object_by_extruder.support != nullptr && !print_wipe_extrusions) {
|
||||
m_layer = layers[instance_to_print.layer_id].support_layer;
|
||||
m_layer = layer_to_print.support_layer;
|
||||
m_object_layer_over_raft = false;
|
||||
gcode += this->extrude_support(
|
||||
// support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
|
||||
instance_to_print.object_by_extruder.support->chained_path_from(m_last_pos, instance_to_print.object_by_extruder.support_extrusion_role));
|
||||
m_layer = layers[instance_to_print.layer_id].layer();
|
||||
m_layer = layer_to_print.layer();
|
||||
m_object_layer_over_raft = object_layer_over_raft;
|
||||
}
|
||||
//FIXME order islands?
|
||||
// Sequential tool path ordering of multiple parts within the same object, aka. perimeter tracking (#5511)
|
||||
|
@ -2298,7 +2307,7 @@ void GCode::process_layer(
|
|||
// printf("G-code after filter:\n%s\n", out.c_str());
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
|
||||
_write(file, gcode);
|
||||
file.write(gcode);
|
||||
BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
|
||||
log_memory_info();
|
||||
}
|
||||
|
@ -2634,22 +2643,42 @@ std::string GCode::extrude_support(const ExtrusionEntityCollection &support_fill
|
|||
return gcode;
|
||||
}
|
||||
|
||||
void GCode::_write(FILE* file, const char *what)
|
||||
bool GCode::GCodeOutputStream::is_error() const
|
||||
{
|
||||
return ::ferror(this->f);
|
||||
}
|
||||
|
||||
void GCode::GCodeOutputStream::flush()
|
||||
{
|
||||
::fflush(this->f);
|
||||
}
|
||||
|
||||
void GCode::GCodeOutputStream::close()
|
||||
{
|
||||
if (this->f) {
|
||||
::fclose(this->f);
|
||||
this->f = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::GCodeOutputStream::write(const char *what)
|
||||
{
|
||||
if (what != nullptr) {
|
||||
const char* gcode = what;
|
||||
// writes string to file
|
||||
fwrite(gcode, 1, ::strlen(gcode), file);
|
||||
fwrite(gcode, 1, ::strlen(gcode), this->f);
|
||||
//FIXME don't allocate a string, maybe process a batch of lines?
|
||||
m_processor.process_buffer(std::string(gcode));
|
||||
}
|
||||
}
|
||||
|
||||
void GCode::_writeln(FILE* file, const std::string &what)
|
||||
void GCode::GCodeOutputStream::writeln(const std::string &what)
|
||||
{
|
||||
if (! what.empty())
|
||||
_write(file, (what.back() == '\n') ? what : (what + '\n'));
|
||||
this->write(what.back() == '\n' ? what : what + '\n');
|
||||
}
|
||||
|
||||
void GCode::_write_format(FILE* file, const char* format, ...)
|
||||
void GCode::GCodeOutputStream::write_format(const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
@ -2673,7 +2702,7 @@ void GCode::_write_format(FILE* file, const char* format, ...)
|
|||
char *bufptr = buffer_dynamic ? (char*)malloc(buflen) : buffer;
|
||||
int res = ::vsnprintf(bufptr, buflen, format, args);
|
||||
if (res > 0)
|
||||
_write(file, bufptr);
|
||||
this->write(bufptr);
|
||||
|
||||
if (buffer_dynamic)
|
||||
free(bufptr);
|
||||
|
@ -2705,6 +2734,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
double acceleration;
|
||||
if (this->on_first_layer() && m_config.first_layer_acceleration.value > 0) {
|
||||
acceleration = m_config.first_layer_acceleration.value;
|
||||
} else if (this->object_layer_over_raft() && m_config.first_layer_acceleration_over_raft.value > 0) {
|
||||
acceleration = m_config.first_layer_acceleration_over_raft.value;
|
||||
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
|
||||
acceleration = m_config.perimeter_acceleration.value;
|
||||
} else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) {
|
||||
|
@ -2749,6 +2780,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||
speed = m_volumetric_speed / path.mm3_per_mm;
|
||||
if (this->on_first_layer())
|
||||
speed = m_config.get_abs_value("first_layer_speed", speed);
|
||||
else if (this->object_layer_over_raft())
|
||||
speed = m_config.get_abs_value("first_layer_speed_over_raft", speed);
|
||||
if (m_config.max_volumetric_speed.value > 0) {
|
||||
// cap speed with max_volumetric_speed anyway (even if user is not using autospeed)
|
||||
speed = std::min(
|
||||
|
|
|
@ -125,7 +125,8 @@ public:
|
|||
m_last_processor_extrusion_role(erNone),
|
||||
m_layer_count(0),
|
||||
m_layer_index(-1),
|
||||
m_layer(nullptr),
|
||||
m_layer(nullptr),
|
||||
m_object_layer_over_raft(false),
|
||||
m_volumetric_speed(0),
|
||||
m_last_pos_defined(false),
|
||||
m_last_extrusion_role(erNone),
|
||||
|
@ -138,7 +139,7 @@ public:
|
|||
m_silent_time_estimator_enabled(false),
|
||||
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
|
||||
{}
|
||||
~GCode() {}
|
||||
~GCode() = default;
|
||||
|
||||
// throws std::runtime_exception on error,
|
||||
// throws CanceledException through print->throw_if_canceled().
|
||||
|
@ -183,13 +184,40 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb);
|
||||
class GCodeOutputStream {
|
||||
public:
|
||||
GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {}
|
||||
~GCodeOutputStream() { this->close(); }
|
||||
|
||||
bool is_open() const { return f; }
|
||||
bool is_error() const;
|
||||
|
||||
void flush();
|
||||
void close();
|
||||
|
||||
// Write a string into a file.
|
||||
void write(const std::string& what) { this->write(what.c_str()); }
|
||||
void write(const char* what);
|
||||
|
||||
// Write a string into a file.
|
||||
// Add a newline, if the string does not end with a newline already.
|
||||
// Used to export a custom G-code section processed by the PlaceholderParser.
|
||||
void writeln(const std::string& what);
|
||||
|
||||
// Formats and write into a file the given data.
|
||||
void write_format(const char* format, ...);
|
||||
|
||||
private:
|
||||
FILE *f = nullptr;
|
||||
GCodeProcessor &m_processor;
|
||||
};
|
||||
void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb);
|
||||
|
||||
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
|
||||
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
|
||||
void process_layer(
|
||||
// Write into the output file.
|
||||
FILE *file,
|
||||
GCodeOutputStream &file,
|
||||
const Print &print,
|
||||
// Set of object & print layers of the same PrintObject and with the same print_z.
|
||||
const std::vector<LayerToPrint> &layers,
|
||||
|
@ -316,9 +344,11 @@ private:
|
|||
unsigned int m_layer_count;
|
||||
// Progress bar indicator. Increments from -1 up to layer_count.
|
||||
int m_layer_index;
|
||||
// Current layer processed. Insequential printing mode, only a single copy will be printed.
|
||||
// Current layer processed. In sequential printing mode, only a single copy will be printed.
|
||||
// In non-sequential mode, all its copies will be printed.
|
||||
const Layer* m_layer;
|
||||
// m_layer is an object layer and it is being printed over raft surface.
|
||||
bool m_object_layer_over_raft;
|
||||
double m_volumetric_speed;
|
||||
// Support for the extrusion role markers. Which marker is active?
|
||||
ExtrusionRole m_last_extrusion_role;
|
||||
|
@ -355,24 +385,14 @@ private:
|
|||
// Processor
|
||||
GCodeProcessor m_processor;
|
||||
|
||||
// Write a string into a file.
|
||||
void _write(FILE* file, const std::string& what) { this->_write(file, what.c_str()); }
|
||||
void _write(FILE* file, const char *what);
|
||||
|
||||
// Write a string into a file.
|
||||
// Add a newline, if the string does not end with a newline already.
|
||||
// Used to export a custom G-code section processed by the PlaceholderParser.
|
||||
void _writeln(FILE* file, const std::string& what);
|
||||
|
||||
// Formats and write into a file the given data.
|
||||
void _write_format(FILE* file, const char* format, ...);
|
||||
|
||||
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
|
||||
void print_machine_envelope(FILE *file, Print &print);
|
||||
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void print_machine_envelope(GCodeOutputStream &file, Print &print);
|
||||
void _print_first_layer_bed_temperature(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
void _print_first_layer_extruder_temperatures(GCodeOutputStream &file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
|
||||
// On the first printing layer. This flag triggers first layer speeds.
|
||||
bool on_first_layer() const { return m_layer != nullptr && m_layer->id() == 0; }
|
||||
// To control print speed of 1st object layer over raft interface.
|
||||
bool object_layer_over_raft() const { return m_object_layer_over_raft; }
|
||||
|
||||
friend ObjectByExtruder& object_by_extruder(
|
||||
std::map<unsigned int, std::vector<ObjectByExtruder>> &by_extruder,
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
|
||||
#include <chrono>
|
||||
|
||||
static const float DEFAULT_TOOLPATH_WIDTH = 0.4f;
|
||||
static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
|
||||
|
||||
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
|
||||
|
@ -344,20 +347,34 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves)
|
||||
struct FilePtr {
|
||||
FilePtr(FILE *f) : f(f) {}
|
||||
~FilePtr() { this->close(); }
|
||||
void close() {
|
||||
if (this->f) {
|
||||
::fclose(this->f);
|
||||
this->f = nullptr;
|
||||
}
|
||||
}
|
||||
FILE* f = nullptr;
|
||||
};
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
{
|
||||
boost::nowide::ifstream in(filename);
|
||||
if (!in.good())
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
if (in.f == nullptr)
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for reading.\n"));
|
||||
|
||||
// temporary file to contain modified gcode
|
||||
std::string out_path = filename + ".postprocess";
|
||||
FILE* out = boost::nowide::fopen(out_path.c_str(), "wb");
|
||||
if (out == nullptr)
|
||||
FilePtr out{ boost::nowide::fopen(out_path.c_str(), "wb") };
|
||||
if (out.f == nullptr) {
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nCannot open file for writing.\n"));
|
||||
}
|
||||
|
||||
auto time_in_minutes = [](float time_in_seconds) {
|
||||
return int(::roundf(time_in_seconds / 60.0f));
|
||||
assert(time_in_seconds >= 0.f);
|
||||
return int((time_in_seconds + 0.5f) / 60.0f);
|
||||
};
|
||||
|
||||
auto time_in_last_minute = [](float time_in_seconds) {
|
||||
|
@ -389,7 +406,6 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
return std::string(line_M73);
|
||||
};
|
||||
|
||||
GCodeReader parser;
|
||||
std::string gcode_line;
|
||||
size_t g1_lines_counter = 0;
|
||||
// keeps track of last exported pair <percent, remaining time>
|
||||
|
@ -408,11 +424,12 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
std::string export_line;
|
||||
|
||||
// replace placeholder lines with the proper final value
|
||||
auto process_placeholders = [&](const std::string& gcode_line) {
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
auto process_placeholders = [&](std::string& gcode_line) {
|
||||
unsigned int extra_lines_count = 0;
|
||||
|
||||
// remove trailing '\n'
|
||||
std::string line = gcode_line.substr(0, gcode_line.length() - 1);
|
||||
auto line = std::string_view(gcode_line).substr(0, gcode_line.length() - 1);
|
||||
|
||||
std::string ret;
|
||||
if (line.length() > 1) {
|
||||
|
@ -453,7 +470,10 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
}
|
||||
}
|
||||
|
||||
return std::tuple(!ret.empty(), ret.empty() ? gcode_line : ret, (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
|
||||
if (! ret.empty())
|
||||
// Not moving the move operator on purpose, so that the gcode_line allocation will grow and it will not be reallocated after handful of lines are processed.
|
||||
gcode_line = ret;
|
||||
return std::tuple(!ret.empty(), (extra_lines_count == 0) ? extra_lines_count : extra_lines_count - 1);
|
||||
};
|
||||
|
||||
// check for temporary lines
|
||||
|
@ -476,11 +496,19 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
g1_times_cache_it.emplace_back(machine.g1_times_cache.begin());
|
||||
|
||||
// add lines M73 to exported gcode
|
||||
auto process_line_G1 = [&]() {
|
||||
auto process_line_G1 = [
|
||||
// Lambdas, mostly for string formatting, all with an empty capture block.
|
||||
time_in_minutes, format_time_float, format_line_M73_main, format_line_M73_stop_int, format_line_M73_stop_float, time_in_last_minute,
|
||||
&self = std::as_const(*this),
|
||||
// Caches, to be modified
|
||||
&g1_times_cache_it, &last_exported_main, &last_exported_stop,
|
||||
// String output
|
||||
&export_line]
|
||||
(const size_t g1_lines_counter) {
|
||||
unsigned int exported_lines_count = 0;
|
||||
if (export_remaining_time_enabled) {
|
||||
if (self.export_remaining_time_enabled) {
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
const TimeMachine& machine = machines[i];
|
||||
const TimeMachine& machine = self.machines[i];
|
||||
if (machine.enabled) {
|
||||
// export pair <percent, remaining time>
|
||||
// Skip all machine.g1_times_cache below g1_lines_counter.
|
||||
|
@ -544,60 +572,81 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
};
|
||||
|
||||
// helper function to write to disk
|
||||
auto write_string = [&](const std::string& str) {
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
|
||||
if (ferror(out)) {
|
||||
in.close();
|
||||
fclose(out);
|
||||
size_t out_file_pos = 0;
|
||||
lines_ends.clear();
|
||||
auto write_string = [&export_line, &out, &out_path, &out_file_pos, &lines_ends](const std::string& str) {
|
||||
fwrite((const void*)export_line.c_str(), 1, export_line.length(), out.f);
|
||||
if (ferror(out.f)) {
|
||||
out.close();
|
||||
boost::nowide::remove(out_path.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nIs the disk full?\n"));
|
||||
}
|
||||
for (size_t i = 0; i < export_line.size(); ++ i)
|
||||
if (export_line[i] == '\n')
|
||||
lines_ends.emplace_back(out_file_pos + i + 1);
|
||||
out_file_pos += export_line.size();
|
||||
export_line.clear();
|
||||
};
|
||||
|
||||
unsigned int line_id = 0;
|
||||
std::vector<std::pair<unsigned int, unsigned int>> offsets;
|
||||
|
||||
while (std::getline(in, gcode_line)) {
|
||||
if (!in.good()) {
|
||||
fclose(out);
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
}
|
||||
{
|
||||
// Read the input stream 64kB at a time, extract lines and process them.
|
||||
std::vector<char> buffer(65536 * 10, 0);
|
||||
// Line buffer.
|
||||
assert(gcode_line.empty());
|
||||
for (;;) {
|
||||
size_t cnt_read = ::fread(buffer.data(), 1, buffer.size(), in.f);
|
||||
if (::ferror(in.f))
|
||||
throw Slic3r::RuntimeError(std::string("Time estimator post process export failed.\nError while reading from file.\n"));
|
||||
bool eof = cnt_read == 0;
|
||||
auto it = buffer.begin();
|
||||
auto it_bufend = buffer.begin() + cnt_read;
|
||||
while (it != it_bufend || (eof && ! gcode_line.empty())) {
|
||||
// Find end of line.
|
||||
bool eol = false;
|
||||
auto it_end = it;
|
||||
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
|
||||
// End of line is indicated also if end of file was reached.
|
||||
eol |= eof && it_end == it_bufend;
|
||||
gcode_line.insert(gcode_line.end(), it, it_end);
|
||||
if (eol) {
|
||||
++line_id;
|
||||
|
||||
++line_id;
|
||||
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
auto [processed, result, lines_added_count] = process_placeholders(gcode_line);
|
||||
if (processed && lines_added_count > 0)
|
||||
offsets.push_back({ line_id, lines_added_count });
|
||||
gcode_line = result;
|
||||
if (!processed) {
|
||||
// remove temporary lines
|
||||
if (is_temporary_decoration(gcode_line))
|
||||
continue;
|
||||
|
||||
// add lines M73 where needed
|
||||
parser.parse_line(gcode_line,
|
||||
[&](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (line.cmd_is("G1")) {
|
||||
unsigned int extra_lines_count = process_line_G1();
|
||||
++g1_lines_counter;
|
||||
gcode_line += "\n";
|
||||
// replace placeholder lines
|
||||
auto [processed, lines_added_count] = process_placeholders(gcode_line);
|
||||
if (processed && lines_added_count > 0)
|
||||
offsets.push_back({ line_id, lines_added_count });
|
||||
if (! processed && ! is_temporary_decoration(gcode_line) && GCodeReader::GCodeLine::cmd_is(gcode_line, "G1")) {
|
||||
// remove temporary lines, add lines M73 where needed
|
||||
unsigned int extra_lines_count = process_line_G1(g1_lines_counter ++);
|
||||
if (extra_lines_count > 0)
|
||||
offsets.push_back({ line_id, extra_lines_count });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export_line += gcode_line;
|
||||
if (export_line.length() > 65535)
|
||||
write_string(export_line);
|
||||
export_line += gcode_line;
|
||||
if (export_line.length() > 65535)
|
||||
write_string(export_line);
|
||||
gcode_line.clear();
|
||||
}
|
||||
// Skip EOL.
|
||||
it = it_end;
|
||||
if (it != it_bufend && *it == '\r')
|
||||
++ it;
|
||||
if (it != it_bufend && *it == '\n')
|
||||
++ it;
|
||||
}
|
||||
if (eof)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!export_line.empty())
|
||||
write_string(export_line);
|
||||
|
||||
fclose(out);
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
// updates moves' gcode ids which have been modified by the insertion of the M73 lines
|
||||
|
@ -698,7 +747,9 @@ void GCodeProcessor::Result::reset() {
|
|||
}
|
||||
#else
|
||||
void GCodeProcessor::Result::reset() {
|
||||
moves = std::vector<GCodeProcessor::MoveVertex>();
|
||||
|
||||
moves.clear();
|
||||
lines_ends.clear();
|
||||
bed_shape = Pointfs();
|
||||
settings_ids.reset();
|
||||
extruders_count = 0;
|
||||
|
@ -777,6 +828,9 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i
|
|||
}
|
||||
|
||||
GCodeProcessor::GCodeProcessor()
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
: m_options_z_corrector(m_result)
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
{
|
||||
reset();
|
||||
m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].line_m73_main_mask = "M73 P%s R%s\n";
|
||||
|
@ -951,10 +1005,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
|
||||
// replace missing values with default
|
||||
std::string default_color = "#FF8000";
|
||||
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
|
||||
if (m_result.extruder_colors[i].empty())
|
||||
m_result.extruder_colors[i] = default_color;
|
||||
m_result.extruder_colors[i] = "#FF8000";
|
||||
}
|
||||
|
||||
m_extruder_colors.resize(m_result.extruder_colors.size());
|
||||
|
@ -1139,7 +1192,6 @@ void GCodeProcessor::reset()
|
|||
m_cp_color.reset();
|
||||
|
||||
m_producer = EProducer::Unknown;
|
||||
m_producers_enabled = false;
|
||||
|
||||
m_time_processor.reset();
|
||||
m_used_filaments.reset();
|
||||
|
@ -1152,6 +1204,10 @@ void GCodeProcessor::reset()
|
|||
m_last_default_color_id = 0;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.reset();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.reset();
|
||||
m_height_compare.reset();
|
||||
|
@ -1159,27 +1215,26 @@ void GCodeProcessor::reset()
|
|||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback)
|
||||
void GCodeProcessor::process_file(const std::string& filename, std::function<void()> cancel_callback)
|
||||
{
|
||||
auto last_cancel_callback_time = std::chrono::high_resolution_clock::now();
|
||||
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
m_start_time = std::chrono::high_resolution_clock::now();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
// pre-processing
|
||||
// parse the gcode file to detect its producer
|
||||
if (m_producers_enabled) {
|
||||
{
|
||||
m_parser.parse_file(filename, [this](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
const std::string_view cmd = line.cmd();
|
||||
if (cmd.length() == 0) {
|
||||
if (cmd.empty()) {
|
||||
const std::string_view comment = line.comment();
|
||||
if (comment.length() > 1 && detect_producer(comment))
|
||||
m_parser.quit_parsing();
|
||||
}
|
||||
});
|
||||
m_parser.reset();
|
||||
|
||||
// if the gcode was produced by PrusaSlicer,
|
||||
// extract the config from it
|
||||
|
@ -1201,18 +1256,45 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
m_parser.parse_file(filename, [this, cancel_callback, &last_cancel_callback_time](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (cancel_callback != nullptr) {
|
||||
// call the cancel callback every 100 ms
|
||||
auto curr_time = std::chrono::high_resolution_clock::now();
|
||||
if (std::chrono::duration_cast<std::chrono::milliseconds>(curr_time - last_cancel_callback_time).count() > 100) {
|
||||
size_t parse_line_callback_cntr = 10000;
|
||||
m_parser.parse_file(filename, [this, cancel_callback, &parse_line_callback_cntr](GCodeReader& reader, const GCodeReader::GCodeLine& line) {
|
||||
if (-- parse_line_callback_cntr == 0) {
|
||||
// Don't call the cancel_callback() too often, do it every at every 10000'th line.
|
||||
parse_line_callback_cntr = 10000;
|
||||
if (cancel_callback)
|
||||
cancel_callback();
|
||||
last_cancel_callback_time = curr_time;
|
||||
}
|
||||
}
|
||||
process_gcode_line(line);
|
||||
});
|
||||
this->process_gcode_line(line, true);
|
||||
});
|
||||
|
||||
this->finalize();
|
||||
}
|
||||
|
||||
void GCodeProcessor::initialize(const std::string& filename)
|
||||
{
|
||||
assert(is_decimal_separator_point());
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_start_time = std::chrono::high_resolution_clock::now();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
// process gcode
|
||||
m_result.filename = filename;
|
||||
m_result.id = ++s_result_id;
|
||||
// 1st move must be a dummy move
|
||||
m_result.moves.emplace_back(MoveVertex());
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
{
|
||||
//FIXME maybe cache GCodeLine gline to be over multiple parse_buffer() invocations.
|
||||
m_parser.parse_buffer(buffer, [this](GCodeReader&, const GCodeReader::GCodeLine& line) {
|
||||
this->process_gcode_line(line, false);
|
||||
});
|
||||
}
|
||||
|
||||
void GCodeProcessor::finalize()
|
||||
{
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
if (move.type == EMoveType::Wipe) {
|
||||
|
@ -1234,10 +1316,6 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
|
||||
update_estimated_times_stats();
|
||||
|
||||
// post-process to add M73 lines into the gcode
|
||||
if (apply_postprocess)
|
||||
m_time_processor.post_process(filename, m_result.moves);
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
std::cout << "\n";
|
||||
m_mm3_per_mm_compare.output();
|
||||
|
@ -1245,8 +1323,9 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
m_width_compare.output();
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start_time).count();
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
}
|
||||
|
||||
|
@ -1340,7 +1419,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
if (pos != cmt.npos) {
|
||||
std::string data_str = cmt.substr(pos + 1);
|
||||
std::vector<std::string> values_str;
|
||||
boost::split(values_str, data_str, boost::is_any_of("|"), boost::token_compress_on);
|
||||
boost::split(values_str, data_str, boost::is_any_of("|,"), boost::token_compress_on);
|
||||
for (const std::string& s : values_str) {
|
||||
out.emplace_back(static_cast<float>(string_to_double_decimal_point(s)));
|
||||
}
|
||||
|
@ -1364,10 +1443,16 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
m_result.filament_densities.clear();
|
||||
extract_floats(comment, "filamentDensities", m_result.filament_densities);
|
||||
}
|
||||
else if (comment.find("extruderDiameter") != comment.npos) {
|
||||
std::vector<float> extruder_diameters;
|
||||
extract_floats(comment, "extruderDiameter", extruder_diameters);
|
||||
m_result.extruders_count = extruder_diameters.size();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
|
||||
if (m_result.extruders_count == 0)
|
||||
m_result.extruders_count = std::max<size_t>(1, std::min(m_result.filament_diameters.size(), m_result.filament_densities.size()));
|
||||
|
||||
if (bed_size.is_defined()) {
|
||||
m_result.bed_shape = {
|
||||
|
@ -1379,7 +1464,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
|
||||
void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled)
|
||||
{
|
||||
/* std::cout << line.raw() << std::endl; */
|
||||
|
||||
|
@ -1391,61 +1476,170 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
|
|||
const std::string_view cmd = line.cmd();
|
||||
if (cmd.length() > 1) {
|
||||
// process command lines
|
||||
switch (::toupper(cmd[0]))
|
||||
switch (cmd[0])
|
||||
{
|
||||
case 'g':
|
||||
case 'G':
|
||||
{
|
||||
switch (::atoi(&cmd[1]))
|
||||
{
|
||||
case 0: { process_G0(line); break; } // Move
|
||||
case 1: { process_G1(line); break; } // Move
|
||||
case 10: { process_G10(line); break; } // Retract
|
||||
case 11: { process_G11(line); break; } // Unretract
|
||||
case 20: { process_G20(line); break; } // Set Units to Inches
|
||||
case 21: { process_G21(line); break; } // Set Units to Millimeters
|
||||
case 22: { process_G22(line); break; } // Firmware controlled retract
|
||||
case 23: { process_G23(line); break; } // Firmware controlled unretract
|
||||
case 28: { process_G28(line); break; } // Move to origin
|
||||
case 90: { process_G90(line); break; } // Set to Absolute Positioning
|
||||
case 91: { process_G91(line); break; } // Set to Relative Positioning
|
||||
case 92: { process_G92(line); break; } // Set Position
|
||||
default: { break; }
|
||||
switch (cmd.size()) {
|
||||
case 2:
|
||||
switch (cmd[1]) {
|
||||
case '0': { process_G0(line); break; } // Move
|
||||
case '1': { process_G1(line); break; } // Move
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
switch (cmd[1]) {
|
||||
case '1':
|
||||
switch (cmd[2]) {
|
||||
case '0': { process_G10(line); break; } // Retract
|
||||
case '1': { process_G11(line); break; } // Unretract
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
switch (cmd[2]) {
|
||||
case '0': { process_G20(line); break; } // Set Units to Inches
|
||||
case '1': { process_G21(line); break; } // Set Units to Millimeters
|
||||
case '2': { process_G22(line); break; } // Firmware controlled retract
|
||||
case '3': { process_G23(line); break; } // Firmware controlled unretract
|
||||
case '8': { process_G28(line); break; } // Move to origin
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case '9':
|
||||
switch (cmd[2]) {
|
||||
case '0': { process_G90(line); break; } // Set to Absolute Positioning
|
||||
case '1': { process_G91(line); break; } // Set to Relative Positioning
|
||||
case '2': { process_G92(line); break; } // Set Position
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
{
|
||||
switch (::atoi(&cmd[1]))
|
||||
{
|
||||
case 1: { process_M1(line); break; } // Sleep or Conditional stop
|
||||
case 82: { process_M82(line); break; } // Set extruder to absolute mode
|
||||
case 83: { process_M83(line); break; } // Set extruder to relative mode
|
||||
case 104: { process_M104(line); break; } // Set extruder temperature
|
||||
case 106: { process_M106(line); break; } // Set fan speed
|
||||
case 107: { process_M107(line); break; } // Disable fan
|
||||
case 108: { process_M108(line); break; } // Set tool (Sailfish)
|
||||
case 109: { process_M109(line); break; } // Set extruder temperature and wait
|
||||
case 132: { process_M132(line); break; } // Recall stored home offsets
|
||||
case 135: { process_M135(line); break; } // Set tool (MakerWare)
|
||||
case 201: { process_M201(line); break; } // Set max printing acceleration
|
||||
case 203: { process_M203(line); break; } // Set maximum feedrate
|
||||
case 204: { process_M204(line); break; } // Set default acceleration
|
||||
case 205: { process_M205(line); break; } // Advanced settings
|
||||
case 221: { process_M221(line); break; } // Set extrude factor override percentage
|
||||
case 401: { process_M401(line); break; } // Repetier: Store x, y and z position
|
||||
case 402: { process_M402(line); break; } // Repetier: Go to stored position
|
||||
case 566: { process_M566(line); break; } // Set allowable instantaneous speed change
|
||||
case 702: { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
default: { break; }
|
||||
switch (cmd.size()) {
|
||||
case 2:
|
||||
switch (cmd[1]) {
|
||||
case '1': { process_M1(line); break; } // Sleep or Conditional stop
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'T':
|
||||
{
|
||||
process_T(line); // Select Tool
|
||||
case 3:
|
||||
switch (cmd[1]) {
|
||||
case '8':
|
||||
switch (cmd[2]) {
|
||||
case '2': { process_M82(line); break; } // Set extruder to absolute mode
|
||||
case '3': { process_M83(line); break; } // Set extruder to relative mode
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
switch (cmd[1]) {
|
||||
case '1':
|
||||
switch (cmd[2]) {
|
||||
case '0':
|
||||
switch (cmd[3]) {
|
||||
case '4': { process_M104(line); break; } // Set extruder temperature
|
||||
case '6': { process_M106(line); break; } // Set fan speed
|
||||
case '7': { process_M107(line); break; } // Disable fan
|
||||
case '8': { process_M108(line); break; } // Set tool (Sailfish)
|
||||
case '9': { process_M109(line); break; } // Set extruder temperature and wait
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case '3':
|
||||
switch (cmd[3]) {
|
||||
case '2': { process_M132(line); break; } // Recall stored home offsets
|
||||
case '5': { process_M135(line); break; } // Set tool (MakerWare)
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
switch (cmd[2]) {
|
||||
case '0':
|
||||
switch (cmd[3]) {
|
||||
case '1': { process_M201(line); break; } // Set max printing acceleration
|
||||
case '3': { process_M203(line); break; } // Set maximum feedrate
|
||||
case '4': { process_M204(line); break; } // Set default acceleration
|
||||
case '5': { process_M205(line); break; } // Advanced settings
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case '2':
|
||||
switch (cmd[3]) {
|
||||
case '1': { process_M221(line); break; } // Set extrude factor override percentage
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '4':
|
||||
switch (cmd[2]) {
|
||||
case '0':
|
||||
switch (cmd[3]) {
|
||||
case '1': { process_M401(line); break; } // Repetier: Store x, y and z position
|
||||
case '2': { process_M402(line); break; } // Repetier: Go to stored position
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '5':
|
||||
switch (cmd[2]) {
|
||||
case '6':
|
||||
switch (cmd[3]) {
|
||||
case '6': { process_M566(line); break; } // Set allowable instantaneous speed change
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '7':
|
||||
switch (cmd[2]) {
|
||||
case '0':
|
||||
switch (cmd[3]) {
|
||||
case '2': { process_M702(line); break; } // Unload the current filament into the MK3 MMU2 unit at the end of print.
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
process_T(line); // Select Tool
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1453,7 +1647,7 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
|
|||
if (comment.length() > 2 && comment.front() == ';')
|
||||
// Process tags embedded into comments. Tag comments always start at the start of a line
|
||||
// with a comment and continue with a tag without any whitespace separator.
|
||||
process_tags(comment.substr(1));
|
||||
process_tags(comment.substr(1), producers_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1502,10 +1696,10 @@ template<typename T>
|
|||
}
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_tags(const std::string_view comment)
|
||||
void GCodeProcessor::process_tags(const std::string_view comment, bool producers_enabled)
|
||||
{
|
||||
// producers tags
|
||||
if (m_producers_enabled && process_producers_tags(comment))
|
||||
if (producers_enabled && process_producers_tags(comment))
|
||||
return;
|
||||
|
||||
// extrusion role tag
|
||||
|
@ -1529,7 +1723,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) {
|
||||
if (!producers_enabled || m_producer == EProducer::PrusaSlicer) {
|
||||
// height tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Height))) {
|
||||
if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height))
|
||||
|
@ -1619,6 +1813,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::ColorChange, extruder_id + 1, color, "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
process_custom_gcode_time(CustomGCode::ColorChange);
|
||||
process_filaments(CustomGCode::ColorChange);
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
@ -1638,6 +1835,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::PausePrint, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
process_custom_gcode_time(CustomGCode::PausePrint);
|
||||
return;
|
||||
|
@ -1649,6 +1849,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
CustomGCode::Item item = { static_cast<double>(m_end_position[2]), CustomGCode::Custom, m_extruder_id + 1, "", "" };
|
||||
m_result.custom_gcode_per_print_z.emplace_back(item);
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_options_z_corrector.set();
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
return;
|
||||
}
|
||||
|
@ -2210,9 +2413,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
return;
|
||||
|
||||
EMoveType type = move_type(delta_pos);
|
||||
if (type == EMoveType::Extrude && m_end_position[Z] == 0.0f)
|
||||
type = EMoveType::Travel;
|
||||
|
||||
if (type == EMoveType::Extrude) {
|
||||
float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
|
||||
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
|
||||
|
@ -2232,10 +2432,23 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
else {
|
||||
if (m_end_position[Z] > m_extruded_last_z + EPSILON) {
|
||||
m_height = m_end_position[Z] - m_extruded_last_z;
|
||||
#if !ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
#endif // !ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
}
|
||||
}
|
||||
|
||||
if (m_height == 0.0f)
|
||||
m_height = DEFAULT_TOOLPATH_HEIGHT;
|
||||
|
||||
if (m_end_position[Z] == 0.0f)
|
||||
m_end_position[Z] = m_height;
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
m_extruded_last_z = m_end_position[Z];
|
||||
m_options_z_corrector.update(m_height);
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_height_compare.update(m_height, m_extrusion_role);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
@ -2252,17 +2465,17 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
// cross section: rectangle + 2 semicircles
|
||||
m_width = delta_pos[E] * static_cast<float>(M_PI * sqr(filament_radius)) / (delta_xyz * m_height) + static_cast<float>(1.0 - 0.25 * M_PI) * m_height;
|
||||
|
||||
if (m_width == 0.0f)
|
||||
m_width = DEFAULT_TOOLPATH_WIDTH;
|
||||
|
||||
// clamp width to avoid artifacts which may arise from wrong values of m_height
|
||||
m_width = std::min(m_width, std::max(1.0f, 4.0f * m_height));
|
||||
m_width = std::min(m_width, std::max(2.0f, 4.0f * m_height));
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_width_compare.update(m_width, m_extrusion_role);
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
|
||||
type = EMoveType::Travel;
|
||||
|
||||
// time estimate section
|
||||
auto move_length = [](const AxisCoords& delta_pos) {
|
||||
float sq_xyz_length = sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]);
|
||||
|
@ -2916,7 +3129,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
|||
m_line_id + 1 :
|
||||
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
|
||||
|
||||
MoveVertex vertex = {
|
||||
m_result.moves.push_back({
|
||||
m_last_line_id,
|
||||
type,
|
||||
m_extrusion_role,
|
||||
|
@ -2931,8 +3144,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
|
|||
m_fan_speed,
|
||||
m_extruder_temps[m_extruder_id],
|
||||
static_cast<float>(m_result.moves.size())
|
||||
};
|
||||
m_result.moves.emplace_back(vertex);
|
||||
});
|
||||
|
||||
// stores stop time placeholders for later use
|
||||
if (type == EMoveType::Color_change || type == EMoveType::Pause_Print) {
|
||||
|
|
|
@ -306,7 +306,7 @@ namespace Slic3r {
|
|||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<MoveVertex>& moves);
|
||||
void post_process(const std::string& filename, std::vector<MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
|
@ -350,6 +350,8 @@ namespace Slic3r {
|
|||
std::string filename;
|
||||
unsigned int id;
|
||||
std::vector<MoveVertex> moves;
|
||||
// Positions of ends of lines of the final G-code this->filename after TimeProcessor::post_process() finalizes the G-code.
|
||||
std::vector<size_t> lines_ends;
|
||||
Pointfs bed_shape;
|
||||
SettingsIds settings_ids;
|
||||
size_t extruders_count;
|
||||
|
@ -388,6 +390,45 @@ namespace Slic3r {
|
|||
bool has_first_vertex() const { return m_first_vertex.has_value(); }
|
||||
};
|
||||
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
// Helper class used to fix the z for color change, pause print and
|
||||
// custom gcode markes
|
||||
class OptionsZCorrector
|
||||
{
|
||||
Result& m_result;
|
||||
std::optional<size_t> m_move_id;
|
||||
std::optional<size_t> m_custom_gcode_per_print_z_id;
|
||||
|
||||
public:
|
||||
explicit OptionsZCorrector(Result& result) : m_result(result) {
|
||||
}
|
||||
|
||||
void set() {
|
||||
m_move_id = m_result.moves.size() - 1;
|
||||
m_custom_gcode_per_print_z_id = m_result.custom_gcode_per_print_z.size() - 1;
|
||||
}
|
||||
|
||||
void update(float height) {
|
||||
if (!m_move_id.has_value() || !m_custom_gcode_per_print_z_id.has_value())
|
||||
return;
|
||||
|
||||
const Vec3f position = m_result.moves.back().position;
|
||||
|
||||
MoveVertex& move = m_result.moves.emplace_back(m_result.moves[*m_move_id]);
|
||||
move.position = position;
|
||||
move.height = height;
|
||||
m_result.moves.erase(m_result.moves.begin() + *m_move_id);
|
||||
m_result.custom_gcode_per_print_z[*m_custom_gcode_per_print_z_id].print_z = position.z();
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_move_id.reset();
|
||||
m_custom_gcode_per_print_z_id.reset();
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
struct DataChecker
|
||||
{
|
||||
|
@ -492,9 +533,15 @@ namespace Slic3r {
|
|||
CpColor m_cp_color;
|
||||
bool m_use_volumetric_e;
|
||||
SeamsDetector m_seams_detector;
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
OptionsZCorrector m_options_z_corrector;
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
size_t m_last_default_color_id;
|
||||
#endif // ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
|
@ -511,7 +558,6 @@ namespace Slic3r {
|
|||
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
EProducer m_producer;
|
||||
bool m_producers_enabled;
|
||||
|
||||
TimeProcessor m_time_processor;
|
||||
UsedFilaments m_used_filaments;
|
||||
|
@ -534,7 +580,6 @@ namespace Slic3r {
|
|||
return m_time_processor.machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].enabled;
|
||||
}
|
||||
void enable_machine_envelope_processing(bool enabled) { m_time_processor.machine_envelope_processing_enabled = enabled; }
|
||||
void enable_producers(bool enabled) { m_producers_enabled = enabled; }
|
||||
void reset();
|
||||
|
||||
const Result& get_result() const { return m_result; }
|
||||
|
@ -542,7 +587,12 @@ namespace Slic3r {
|
|||
|
||||
// Process the gcode contained in the file with the given filename
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void process_file(const std::string& filename, bool apply_postprocess, std::function<void()> cancel_callback = nullptr);
|
||||
void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
|
||||
|
||||
// Streaming interface, for processing G-codes just generated by PrusaSlicer in a pipelined fashion.
|
||||
void initialize(const std::string& filename);
|
||||
void process_buffer(const std::string& buffer);
|
||||
void finalize();
|
||||
|
||||
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
|
@ -555,10 +605,10 @@ namespace Slic3r {
|
|||
private:
|
||||
void apply_config(const DynamicPrintConfig& config);
|
||||
void apply_config_simplify3d(const std::string& filename);
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line);
|
||||
void process_gcode_line(const GCodeReader::GCodeLine& line, bool producers_enabled);
|
||||
|
||||
// Process tags embedded into comments
|
||||
void process_tags(const std::string_view comment);
|
||||
void process_tags(const std::string_view comment, bool producers_enabled);
|
||||
bool process_producers_tags(const std::string_view comment);
|
||||
bool process_prusaslicer_tags(const std::string_view comment);
|
||||
bool process_cura_tags(const std::string_view comment);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <charconv>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
@ -32,7 +33,7 @@ void GCodeReader::apply_config(const DynamicPrintConfig &config)
|
|||
m_extrusion_axis = get_extrusion_axis_char(m_config);
|
||||
}
|
||||
|
||||
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
const char* GCodeReader::parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
{
|
||||
PROFILE_FUNC();
|
||||
|
||||
|
@ -70,9 +71,16 @@ const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline,
|
|||
}
|
||||
if (axis != NUM_AXES_WITH_UNKNOWN) {
|
||||
// Try to parse the numeric value.
|
||||
#ifdef WIN32
|
||||
double v;
|
||||
auto [pend, ec] = std::from_chars(++ c, end, v);
|
||||
if (pend != c && is_end_of_word(*pend)) {
|
||||
#else
|
||||
// The older version of GCC and Clang support std::from_chars just for integers, so strtod we used it instead.
|
||||
char *pend = nullptr;
|
||||
double v = strtod(++ c, &pend);
|
||||
if (pend != nullptr && is_end_of_word(*pend)) {
|
||||
#endif
|
||||
// The axis value has been parsed correctly.
|
||||
if (axis != UNKNOWN_AXIS)
|
||||
gline.m_axis[int(axis)] = float(v);
|
||||
|
@ -125,13 +133,42 @@ void GCodeReader::update_coordinates(GCodeLine &gline, std::pair<const char*, co
|
|||
}
|
||||
}
|
||||
|
||||
void GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||
bool GCodeReader::parse_file(const std::string &file, callback_t callback)
|
||||
{
|
||||
boost::nowide::ifstream f(file);
|
||||
f.sync_with_stdio(false);
|
||||
std::vector<char> buffer(65536 * 10, 0);
|
||||
std::string line;
|
||||
m_parsing = true;
|
||||
while (m_parsing && std::getline(f, line))
|
||||
this->parse_line(line, callback);
|
||||
GCodeLine gline;
|
||||
while (m_parsing && ! f.eof()) {
|
||||
f.read(buffer.data(), buffer.size());
|
||||
if (! f.eof() && ! f.good())
|
||||
// Reading the input file failed.
|
||||
return false;
|
||||
auto it = buffer.begin();
|
||||
auto it_bufend = buffer.begin() + f.gcount();
|
||||
while (it != it_bufend) {
|
||||
bool eol = false;
|
||||
auto it_end = it;
|
||||
for (; it_end != it_bufend && ! (eol = *it_end == '\r' || *it_end == '\n'); ++ it_end) ;
|
||||
eol |= f.eof() && it_end == it_bufend;
|
||||
if (eol) {
|
||||
gline.reset();
|
||||
if (line.empty())
|
||||
this->parse_line(&(*it), &(*it_end), gline, callback);
|
||||
else {
|
||||
line.insert(line.end(), it, it_end);
|
||||
this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback);
|
||||
line.clear();
|
||||
}
|
||||
} else
|
||||
line.insert(line.end(), it, it_end);
|
||||
// Skip all the empty lines.
|
||||
for (it = it_end; it != it_bufend && (*it == '\r' || *it == '\n'); ++ it) ;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GCodeReader::GCodeLine::has(char axis) const
|
||||
|
|
|
@ -44,11 +44,7 @@ public:
|
|||
float y = this->has(Y) ? (this->y() - reader.y()) : 0;
|
||||
return sqrt(x*x + y*y);
|
||||
}
|
||||
bool cmd_is(const char *cmd_test) const {
|
||||
const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str());
|
||||
size_t len = strlen(cmd_test);
|
||||
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
|
||||
}
|
||||
bool cmd_is(const char *cmd_test) const { return cmd_is(m_raw, cmd_test); }
|
||||
bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; }
|
||||
bool retracting(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) < 0; }
|
||||
bool travel() const { return this->cmd_is("G1") && ! this->has(E); }
|
||||
|
@ -66,6 +62,12 @@ public:
|
|||
float e() const { return m_axis[E]; }
|
||||
float f() const { return m_axis[F]; }
|
||||
|
||||
static bool cmd_is(const std::string &gcode_line, const char *cmd_test) {
|
||||
const char *cmd = GCodeReader::skip_whitespaces(gcode_line.c_str());
|
||||
size_t len = strlen(cmd_test);
|
||||
return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_raw;
|
||||
float m_axis[NUM_AXES];
|
||||
|
@ -75,7 +77,8 @@ public:
|
|||
|
||||
typedef std::function<void(GCodeReader&, const GCodeLine&)> callback_t;
|
||||
|
||||
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { memset(m_position, 0, sizeof(m_position)); }
|
||||
GCodeReader() : m_verbose(false), m_extrusion_axis('E') { this->reset(); }
|
||||
void reset() { memset(m_position, 0, sizeof(m_position)); }
|
||||
void apply_config(const GCodeConfig &config);
|
||||
void apply_config(const DynamicPrintConfig &config);
|
||||
|
||||
|
@ -83,11 +86,12 @@ public:
|
|||
void parse_buffer(const std::string &buffer, Callback callback)
|
||||
{
|
||||
const char *ptr = buffer.c_str();
|
||||
const char *end = ptr + buffer.size();
|
||||
GCodeLine gline;
|
||||
m_parsing = true;
|
||||
while (m_parsing && *ptr != 0) {
|
||||
gline.reset();
|
||||
ptr = this->parse_line(ptr, gline, callback);
|
||||
ptr = this->parse_line(ptr, end, gline, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,20 +99,21 @@ public:
|
|||
{ this->parse_buffer(buffer, [](GCodeReader&, const GCodeReader::GCodeLine&){}); }
|
||||
|
||||
template<typename Callback>
|
||||
const char* parse_line(const char *ptr, GCodeLine &gline, Callback &callback)
|
||||
const char* parse_line(const char *ptr, const char *end, GCodeLine &gline, Callback &callback)
|
||||
{
|
||||
std::pair<const char*, const char*> cmd;
|
||||
const char *end = parse_line_internal(ptr, gline, cmd);
|
||||
const char *line_end = parse_line_internal(ptr, end, gline, cmd);
|
||||
callback(*this, gline);
|
||||
update_coordinates(gline, cmd);
|
||||
return end;
|
||||
return line_end;
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void parse_line(const std::string &line, Callback callback)
|
||||
{ GCodeLine gline; this->parse_line(line.c_str(), gline, callback); }
|
||||
{ GCodeLine gline; this->parse_line(line.c_str(), line.c_str() + line.size(), gline, callback); }
|
||||
|
||||
void parse_file(const std::string &file, callback_t callback);
|
||||
// Returns false if reading the file failed.
|
||||
bool parse_file(const std::string &file, callback_t callback);
|
||||
void quit_parsing() { m_parsing = false; }
|
||||
|
||||
float& x() { return m_position[X]; }
|
||||
|
@ -127,7 +132,7 @@ public:
|
|||
// void set_extrusion_axis(char axis) { m_extrusion_axis = axis; }
|
||||
|
||||
private:
|
||||
const char* parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
const char* parse_line_internal(const char *ptr, const char *end, GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
void update_coordinates(GCodeLine &gline, std::pair<const char*, const char*> &command);
|
||||
|
||||
static bool is_whitespace(char c) { return c == ' ' || c == '\t'; }
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
#include "GCodeWriter.hpp"
|
||||
#include "CustomGCode.hpp"
|
||||
#include <algorithm>
|
||||
#include <charconv>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <assert.h>
|
||||
|
||||
#define XYZF_EXPORT_DIGITS 3
|
||||
#define E_EXPORT_DIGITS 5
|
||||
|
||||
#define FLAVOR_IS(val) this->config.gcode_flavor == val
|
||||
#define FLAVOR_IS_NOT(val) this->config.gcode_flavor != val
|
||||
#define COMMENT(comment) if (this->config.gcode_comments && !comment.empty()) gcode << " ; " << comment;
|
||||
#define PRECISION(val, precision) std::fixed << std::setprecision(precision) << (val)
|
||||
#define XYZF_NUM(val) PRECISION(val, 3)
|
||||
#define E_NUM(val) PRECISION(val, 5)
|
||||
#define XYZF_NUM(val) PRECISION(val, XYZF_EXPORT_DIGITS)
|
||||
#define E_NUM(val) PRECISION(val, E_EXPORT_DIGITS)
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -288,16 +292,89 @@ std::string GCodeWriter::toolchange(unsigned int extruder_id)
|
|||
return gcode.str();
|
||||
}
|
||||
|
||||
class G1Writer {
|
||||
private:
|
||||
static constexpr const size_t buflen = 256;
|
||||
char buf[buflen];
|
||||
char *buf_end;
|
||||
std::to_chars_result ptr_err;
|
||||
|
||||
public:
|
||||
G1Writer() {
|
||||
this->buf[0] = 'G';
|
||||
this->buf[1] = '1';
|
||||
this->buf_end = this->buf + buflen;
|
||||
this->ptr_err.ptr = this->buf + 2;
|
||||
}
|
||||
|
||||
void emit_axis(const char axis, const double v, size_t digits) {
|
||||
*ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = axis;
|
||||
#ifdef WIN32
|
||||
this->ptr_err = std::to_chars(this->ptr_err.ptr, this->buf_end, v, std::chars_format::fixed, digits);
|
||||
#else
|
||||
int buf_capacity = int(this->buf_end - this->ptr_err.ptr);
|
||||
int ret = snprintf(this->ptr_err.ptr, buf_capacity, "%.*lf", int(digits), v);
|
||||
if (ret <= 0 || ret > buf_capacity)
|
||||
ptr_err.ec = std::errc::value_too_large;
|
||||
else
|
||||
this->ptr_err.ptr = this->ptr_err.ptr + ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
void emit_xy(const Vec2d &point) {
|
||||
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_xyz(const Vec3d &point) {
|
||||
this->emit_axis('X', point.x(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_axis('Y', point.y(), XYZF_EXPORT_DIGITS);
|
||||
this->emit_z(point.z());
|
||||
}
|
||||
|
||||
void emit_z(const double z) {
|
||||
this->emit_axis('Z', z, XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_e(const std::string &axis, double v) {
|
||||
if (! axis.empty()) {
|
||||
// not gcfNoExtrusion
|
||||
this->emit_axis(axis[0], v, E_EXPORT_DIGITS);
|
||||
}
|
||||
}
|
||||
|
||||
void emit_f(double speed) {
|
||||
this->emit_axis('F', speed, XYZF_EXPORT_DIGITS);
|
||||
}
|
||||
|
||||
void emit_string(const std::string &s) {
|
||||
strncpy(ptr_err.ptr, s.c_str(), s.size());
|
||||
ptr_err.ptr += s.size();
|
||||
}
|
||||
|
||||
void emit_comment(bool allow_comments, const std::string &comment) {
|
||||
if (allow_comments && ! comment.empty()) {
|
||||
*ptr_err.ptr ++ = ' '; *ptr_err.ptr ++ = ';'; *ptr_err.ptr ++ = ' ';
|
||||
this->emit_string(comment);
|
||||
}
|
||||
}
|
||||
|
||||
std::string string() {
|
||||
*ptr_err.ptr ++ = '\n';
|
||||
return std::string(this->buf, ptr_err.ptr - buf);
|
||||
}
|
||||
};
|
||||
|
||||
std::string GCodeWriter::set_speed(double F, const std::string &comment, const std::string &cooling_marker) const
|
||||
{
|
||||
assert(F > 0.);
|
||||
assert(F < 100000.);
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 F" << XYZF_NUM(F);
|
||||
COMMENT(comment);
|
||||
gcode << cooling_marker;
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
|
||||
G1Writer w;
|
||||
w.emit_f(F);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
w.emit_string(cooling_marker);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
|
||||
|
@ -305,13 +382,11 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com
|
|||
m_pos(0) = point(0);
|
||||
m_pos(1) = point(1);
|
||||
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 X" << XYZF_NUM(point(0))
|
||||
<< " Y" << XYZF_NUM(point(1))
|
||||
<< " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
|
||||
COMMENT(comment);
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
G1Writer w;
|
||||
w.emit_xy(point);
|
||||
w.emit_f(this->config.travel_speed.value * 60.0);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment)
|
||||
|
@ -340,14 +415,11 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
|||
m_lifted = 0;
|
||||
m_pos = point;
|
||||
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 X" << XYZF_NUM(point(0))
|
||||
<< " Y" << XYZF_NUM(point(1))
|
||||
<< " Z" << XYZF_NUM(point(2))
|
||||
<< " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
|
||||
COMMENT(comment);
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
G1Writer w;
|
||||
w.emit_xyz(point);
|
||||
w.emit_f(this->config.travel_speed.value * 60.0);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
|
||||
|
@ -377,12 +449,11 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
|
|||
if (speed == 0.)
|
||||
speed = this->config.travel_speed.value;
|
||||
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 Z" << XYZF_NUM(z)
|
||||
<< " F" << XYZF_NUM(speed * 60.0);
|
||||
COMMENT(comment);
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
G1Writer w;
|
||||
w.emit_z(z);
|
||||
w.emit_f(speed * 60.0);
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
bool GCodeWriter::will_move_z(double z) const
|
||||
|
@ -402,16 +473,12 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std:
|
|||
m_pos(0) = point(0);
|
||||
m_pos(1) = point(1);
|
||||
m_extruder->extrude(dE);
|
||||
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 X" << XYZF_NUM(point(0))
|
||||
<< " Y" << XYZF_NUM(point(1));
|
||||
if (! m_extrusion_axis.empty())
|
||||
// not gcfNoExtrusion
|
||||
gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E());
|
||||
COMMENT(comment);
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
|
||||
G1Writer w;
|
||||
w.emit_xy(point);
|
||||
w.emit_e(m_extrusion_axis, m_extruder->E());
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
|
||||
|
@ -420,16 +487,11 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std
|
|||
m_lifted = 0;
|
||||
m_extruder->extrude(dE);
|
||||
|
||||
std::ostringstream gcode;
|
||||
gcode << "G1 X" << XYZF_NUM(point(0))
|
||||
<< " Y" << XYZF_NUM(point(1))
|
||||
<< " Z" << XYZF_NUM(point(2));
|
||||
if (! m_extrusion_axis.empty())
|
||||
// not gcfNoExtrusion
|
||||
gcode << " " << m_extrusion_axis << E_NUM(m_extruder->E());
|
||||
COMMENT(comment);
|
||||
gcode << "\n";
|
||||
return gcode.str();
|
||||
G1Writer w;
|
||||
w.emit_xyz(point);
|
||||
w.emit_e(m_extrusion_axis, m_extruder->E());
|
||||
w.emit_comment(this->config.gcode_comments, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string GCodeWriter::retract(bool before_wipe)
|
||||
|
|
|
@ -161,8 +161,10 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||
if (!result)
|
||||
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
||||
|
||||
#if !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (model.objects.empty())
|
||||
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
|
||||
#endif // !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
for (ModelObject *o : model.objects)
|
||||
{
|
||||
|
@ -557,6 +559,21 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
|
|||
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
|
||||
}
|
||||
|
||||
bool Model::is_fdm_support_painted() const
|
||||
{
|
||||
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fdm_support_painted(); });
|
||||
}
|
||||
|
||||
bool Model::is_seam_painted() const
|
||||
{
|
||||
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_seam_painted(); });
|
||||
}
|
||||
|
||||
bool Model::is_mm_painted() const
|
||||
{
|
||||
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_mm_painted(); });
|
||||
}
|
||||
|
||||
ModelObject::~ModelObject()
|
||||
{
|
||||
this->clear_volumes();
|
||||
|
@ -733,6 +750,16 @@ void ModelObject::clear_volumes()
|
|||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
||||
bool ModelObject::is_fdm_support_painted() const
|
||||
{
|
||||
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_fdm_support_painted(); });
|
||||
}
|
||||
|
||||
bool ModelObject::is_seam_painted() const
|
||||
{
|
||||
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_seam_painted(); });
|
||||
}
|
||||
|
||||
bool ModelObject::is_mm_painted() const
|
||||
{
|
||||
return std::any_of(this->volumes.cbegin(), this->volumes.cend(), [](const ModelVolume *mv) { return mv->is_mm_painted(); });
|
||||
|
|
|
@ -285,6 +285,10 @@ public:
|
|||
void clear_volumes();
|
||||
void sort_volumes(bool full_sort);
|
||||
bool is_multiparts() const { return volumes.size() > 1; }
|
||||
// Checks if any of object volume is painted using the fdm support painting gizmo.
|
||||
bool is_fdm_support_painted() const;
|
||||
// Checks if any of object volume is painted using the seam painting gizmo.
|
||||
bool is_seam_painted() const;
|
||||
// Checks if any of object volume is painted using the multi-material painting gizmo.
|
||||
bool is_mm_painted() const;
|
||||
|
||||
|
@ -723,6 +727,8 @@ public:
|
|||
this->mmu_segmentation_facets.set_new_unique_id();
|
||||
}
|
||||
|
||||
bool is_fdm_support_painted() const { return !this->supported_facets.empty(); }
|
||||
bool is_seam_painted() const { return !this->seam_facets.empty(); }
|
||||
bool is_mm_painted() const { return !this->mmu_segmentation_facets.empty(); }
|
||||
|
||||
protected:
|
||||
|
@ -1127,6 +1133,13 @@ public:
|
|||
// Propose an output path, replace extension. The new_extension shall contain the initial dot.
|
||||
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
|
||||
|
||||
// Checks if any of objects is painted using the fdm support painting gizmo.
|
||||
bool is_fdm_support_painted() const;
|
||||
// Checks if any of objects is painted using the seam painting gizmo.
|
||||
bool is_seam_painted() const;
|
||||
// Checks if any of objects is painted using the multi-material painting gizmo.
|
||||
bool is_mm_painted() const;
|
||||
|
||||
private:
|
||||
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }
|
||||
void assign_new_unique_ids_recursive();
|
||||
|
|
|
@ -120,7 +120,8 @@ void Polyline::simplify(double tolerance)
|
|||
this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
|
||||
}
|
||||
|
||||
/* This method simplifies all *lines* contained in the supplied area */
|
||||
#if 0
|
||||
// This method simplifies all *lines* contained in the supplied area
|
||||
template <class T>
|
||||
void Polyline::simplify_by_visibility(const T &area)
|
||||
{
|
||||
|
@ -141,6 +142,7 @@ void Polyline::simplify_by_visibility(const T &area)
|
|||
}
|
||||
template void Polyline::simplify_by_visibility<ExPolygon>(const ExPolygon &area);
|
||||
template void Polyline::simplify_by_visibility<ExPolygonCollection>(const ExPolygonCollection &area);
|
||||
#endif
|
||||
|
||||
void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
|
||||
{
|
||||
|
|
|
@ -72,7 +72,7 @@ public:
|
|||
void extend_start(double distance);
|
||||
Points equally_spaced_points(double distance) const;
|
||||
void simplify(double tolerance);
|
||||
template <class T> void simplify_by_visibility(const T &area);
|
||||
// template <class T> void simplify_by_visibility(const T &area);
|
||||
void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
|
||||
bool is_straight() const;
|
||||
bool is_closed() const { return this->points.front() == this->points.back(); }
|
||||
|
|
|
@ -428,9 +428,9 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
#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",
|
||||
"bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "travel_speed_z", "first_layer_speed", "first_layer_speed_over_raft", "perimeter_acceleration", "infill_acceleration",
|
||||
"bridge_acceleration", "first_layer_acceleration", "first_layer_acceleration_over_raft", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
|
||||
"min_skirt_length", "brim_width", "brim_separation", "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",
|
||||
|
|
|
@ -240,7 +240,6 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
|||
if (! errors_cummulative.empty())
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
// ysToDo : set prefered filament or sla_material (relates to print technology) and force o use of preffered printer model if it was added
|
||||
this->load_selections(config, preferred_selection);
|
||||
|
||||
return substitutions;
|
||||
|
@ -466,20 +465,9 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||
// will be selected by the following call of this->update_compatible(PresetSelectCompatibleType::Always).
|
||||
|
||||
const Preset *initial_printer = printers.find_preset(initial_printer_profile_name);
|
||||
// If executed due to a Config Wizard update, preferred_printer contains the first newly installed printer, otherwise nullptr.
|
||||
const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant);
|
||||
printers.select_preset_by_name(
|
||||
(preferred_printer != nullptr /*&& (initial_printer == nullptr || !initial_printer->is_visible)*/) ?
|
||||
preferred_printer->name :
|
||||
initial_printer_profile_name,
|
||||
true);
|
||||
|
||||
// select preferred filament/sla_material profile if any exists and is visible
|
||||
if (!preferred_selection.filament.empty())
|
||||
if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible)
|
||||
initial_filament_profile_name = it->name;
|
||||
if (!preferred_selection.sla_material.empty())
|
||||
if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible)
|
||||
initial_sla_material_profile_name = it->name;
|
||||
printers.select_preset_by_name(preferred_printer ? preferred_printer->name : initial_printer_profile_name, true);
|
||||
|
||||
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
|
||||
prints.select_preset_by_name_strict(initial_print_profile_name);
|
||||
|
@ -507,6 +495,21 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||
this->update_compatible(PresetSelectCompatibleType::Always);
|
||||
this->update_multi_material_filament_presets();
|
||||
|
||||
if (initial_printer != nullptr && (preferred_printer == nullptr || initial_printer == preferred_printer)) {
|
||||
// Don't run the following code, as we want to activate default filament / SLA material profiles when installing and selecting a new printer.
|
||||
// Only run this code if just a filament / SLA material was installed by Config Wizard for an active Printer.
|
||||
auto printer_technology = printers.get_selected_preset().printer_technology();
|
||||
if (printer_technology == ptFFF && ! preferred_selection.filament.empty()) {
|
||||
if (auto it = filaments.find_preset_internal(preferred_selection.filament); it != filaments.end() && it->is_visible) {
|
||||
filaments.select_preset_by_name_strict(preferred_selection.filament);
|
||||
this->filament_presets.front() = filaments.get_selected_preset_name();
|
||||
}
|
||||
} else if (printer_technology == ptSLA && ! preferred_selection.sla_material.empty()) {
|
||||
if (auto it = sla_materials.find_preset_internal(preferred_selection.sla_material); it != sla_materials.end() && it->is_visible)
|
||||
sla_materials.select_preset_by_name_strict(preferred_selection.sla_material);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the initial physical printer name.
|
||||
std::string initial_physical_printer_name = remove_ini_suffix(config.get("presets", "physical_printer"));
|
||||
|
||||
|
|
|
@ -88,7 +88,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
"filament_cost",
|
||||
"filament_spool_weight",
|
||||
"first_layer_acceleration",
|
||||
"first_layer_acceleration_over_raft",
|
||||
"first_layer_bed_temperature",
|
||||
"first_layer_speed_over_raft",
|
||||
"gcode_comments",
|
||||
"gcode_label_objects",
|
||||
"infill_acceleration",
|
||||
|
|
|
@ -505,10 +505,10 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionEnum<BrimType>(btOuterOnly));
|
||||
|
||||
def = this->add("brim_offset", coFloat);
|
||||
def->label = L("Brim offset");
|
||||
def = this->add("brim_separation", coFloat);
|
||||
def->label = L("Brim separation gap");
|
||||
def->category = L("Skirt and brim");
|
||||
def->tooltip = L("The offset of the brim from the printed object. The offset is applied after the elephant foot compensation.");
|
||||
def->tooltip = L("Offset of brim from the printed object. The offset is applied after the elephant foot compensation.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
|
@ -1152,6 +1152,15 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("first_layer_acceleration_over_raft", coFloat);
|
||||
def->label = L("First object layer over raft interface");
|
||||
def->tooltip = L("This is the acceleration your printer will use for first layer of object above raft interface. Set zero "
|
||||
"to disable acceleration control for first layer of object above raft interface.");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("first_layer_bed_temperature", coInts);
|
||||
def->label = L("First layer");
|
||||
def->full_label = L("First layer bed temperature");
|
||||
|
@ -1194,6 +1203,16 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
|
||||
|
||||
def = this->add("first_layer_speed_over_raft", coFloatOrPercent);
|
||||
def->label = L("Speed of object first layer over raft interface");
|
||||
def->tooltip = L("If expressed as absolute value in mm/s, this speed will be applied to all the print moves "
|
||||
"of the first object layer above raft interface, regardless of their type. If expressed as a percentage "
|
||||
"(for example: 40%) it will scale the default speeds.");
|
||||
def->sidetext = L("mm/s or %");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(30, false));
|
||||
|
||||
def = this->add("first_layer_temperature", coInts);
|
||||
def->label = L("First layer");
|
||||
def->full_label = L("First layer nozzle temperature");
|
||||
|
|
|
@ -449,13 +449,15 @@ protected: \
|
|||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
PrintObjectConfig,
|
||||
|
||||
((ConfigOptionFloat, brim_offset))
|
||||
((ConfigOptionFloat, brim_separation))
|
||||
((ConfigOptionEnum<BrimType>, brim_type))
|
||||
((ConfigOptionFloat, brim_width))
|
||||
((ConfigOptionBool, clip_multipart_objects))
|
||||
((ConfigOptionBool, dont_support_bridges))
|
||||
((ConfigOptionFloat, elefant_foot_compensation))
|
||||
((ConfigOptionFloatOrPercent, extrusion_width))
|
||||
((ConfigOptionFloat, first_layer_acceleration_over_raft))
|
||||
((ConfigOptionFloatOrPercent, first_layer_speed_over_raft))
|
||||
((ConfigOptionBool, infill_only_where_needed))
|
||||
// Force the generation of solid shells between adjacent materials/volumes.
|
||||
((ConfigOptionBool, interface_shells))
|
||||
|
|
|
@ -500,7 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
bool invalidated = false;
|
||||
for (const t_config_option_key &opt_key : opt_keys) {
|
||||
if ( opt_key == "brim_width"
|
||||
|| opt_key == "brim_offset"
|
||||
|| opt_key == "brim_separation"
|
||||
|| opt_key == "brim_type") {
|
||||
// Brim is printed below supports, support invalidates brim and skirt.
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
|
@ -2293,14 +2293,24 @@ void PrintObject::project_and_append_custom_facets(
|
|||
const indexed_triangle_set custom_facets = seam
|
||||
? mv->seam_facets.get_facets_strict(*mv, type)
|
||||
: mv->supported_facets.get_facets_strict(*mv, type);
|
||||
if (! custom_facets.indices.empty())
|
||||
#if 0
|
||||
project_triangles_to_slabs(this->layers(), custom_facets,
|
||||
(this->trafo_centered() * mv->get_matrix()).cast<float>(),
|
||||
seam, out);
|
||||
#else
|
||||
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &out, [](){});
|
||||
#endif
|
||||
if (! custom_facets.indices.empty()) {
|
||||
if (seam)
|
||||
project_triangles_to_slabs(this->layers(), custom_facets,
|
||||
(this->trafo_centered() * mv->get_matrix()).cast<float>(),
|
||||
seam, out);
|
||||
else {
|
||||
std::vector<Polygons> projected;
|
||||
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){});
|
||||
// Merge these projections with the output, layer by layer.
|
||||
assert(! projected.empty());
|
||||
assert(out.empty() || out.size() == projected.size());
|
||||
if (out.empty())
|
||||
out = std::move(projected);
|
||||
else
|
||||
for (size_t i = 0; i < out.size(); ++ i)
|
||||
append(out[i], std::move(projected[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
|
||||
using namespace Slic3r;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// #define EXPENSIVE_DEBUG_CHECKS
|
||||
#endif // NDEBUG
|
||||
|
||||
// only private namespace not neccessary be in .hpp
|
||||
namespace QuadricEdgeCollapse {
|
||||
using Vertices = std::vector<stl_vertex>;
|
||||
|
@ -79,10 +83,13 @@ namespace QuadricEdgeCollapse {
|
|||
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);
|
||||
void reorder_edges(EdgeInfos &e_infos, const VertexInfo &v_info, uint32_t ti0, uint32_t ti1);
|
||||
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);
|
||||
bool create_no_volume(uint32_t vi0, uint32_t vi1, uint32_t ti0, uint32_t ti1,
|
||||
const VertexInfo &v_info0, const VertexInfo &v_info1, 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);
|
||||
|
@ -92,14 +99,25 @@ namespace QuadricEdgeCollapse {
|
|||
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
|
||||
#ifdef EXPENSIVE_DEBUG_CHECKS
|
||||
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 */
|
||||
#endif /* EXPENSIVE_DEBUG_CHECKS */
|
||||
|
||||
} // namespace QuadricEdgeCollapse
|
||||
// constants --> may be move to config
|
||||
const uint32_t check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel
|
||||
const size_t max_triangle_count_for_one_vertex = 50;
|
||||
// change speed of progress bargraph
|
||||
const int status_init_size = 10; // in percents
|
||||
// parts of init size
|
||||
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;
|
||||
} // namespace QuadricEdgeCollapse
|
||||
|
||||
using namespace QuadricEdgeCollapse;
|
||||
|
||||
|
@ -110,10 +128,6 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
std::function<void(void)> throw_on_cancel,
|
||||
std::function<void(int)> status_fn)
|
||||
{
|
||||
// constants --> may be move to config
|
||||
const int status_init_size = 10; // in percents
|
||||
const int check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel
|
||||
|
||||
// check input
|
||||
if (triangle_count >= its.indices.size()) return;
|
||||
float maximal_error = (max_error == nullptr)? std::numeric_limits<float>::max() : *max_error;
|
||||
|
@ -122,7 +136,8 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
if (status_fn == nullptr) status_fn = [](int) {};
|
||||
|
||||
StatusFn init_status_fn = [&](int percent) {
|
||||
status_fn(std::round((percent * status_init_size) / 100.));
|
||||
float n_percent = percent * status_init_size / 100.f;
|
||||
status_fn(static_cast<int>(std::round(n_percent)));
|
||||
};
|
||||
|
||||
TriangleInfos t_infos; // only normals with information about deleted triangle
|
||||
|
@ -145,7 +160,6 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
mpq.reserve(its.indices.size());
|
||||
for (Error &error :errors) mpq.push(error);
|
||||
|
||||
const size_t max_triangle_count_for_one_vertex = 50;
|
||||
CopyEdgeInfos ceis;
|
||||
ceis.reserve(max_triangle_count_for_one_vertex);
|
||||
EdgeInfos e_infos_swap;
|
||||
|
@ -162,8 +176,9 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
(1. - reduced);
|
||||
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);
|
||||
// modulo for update status, call each percent only once
|
||||
uint32_t status_mod = std::max(uint32_t(16),
|
||||
count_triangle_to_reduce / (100 - status_init_size));
|
||||
|
||||
uint32_t iteration_number = 0;
|
||||
float last_collapsed_error = 0.f;
|
||||
|
@ -195,14 +210,21 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
q += v_info1.q;
|
||||
Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices);
|
||||
// set of triangle indices that change quadric
|
||||
uint32_t ti1 = -1; // triangle 1 index
|
||||
auto ti1_opt = (v_info0.count < v_info1.count)?
|
||||
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()) {
|
||||
ti1 = *ti1_opt;
|
||||
reorder_edges(e_infos, v_info0, ti0, ti1);
|
||||
reorder_edges(e_infos, v_info1, ti0, ti1);
|
||||
}
|
||||
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)) {
|
||||
degenerate(vi0, ti0, ti1, v_info1, e_infos, its.indices) ||
|
||||
degenerate(vi1, ti0, ti1, v_info0, e_infos, its.indices) ||
|
||||
create_no_volume(vi0, vi1, ti0, ti1, v_info0, v_info1, e_infos, its.indices) ||
|
||||
is_flipped(new_vertex0, ti0, ti1, v_info0, t_infos, e_infos, its) ||
|
||||
is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) {
|
||||
// try other triangle's edge
|
||||
Vec3d errors = calculate_3errors(t0, its.vertices, v_infos);
|
||||
Vec3i ord = (errors[0] < errors[1]) ?
|
||||
|
@ -227,29 +249,25 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
mpq.push(e);
|
||||
continue;
|
||||
}
|
||||
uint32_t ti1 = *ti1_opt;
|
||||
|
||||
last_collapsed_error = e.value;
|
||||
changed_triangle_indices.clear();
|
||||
changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4);
|
||||
|
||||
// for each vertex0 triangles
|
||||
uint32_t v_info0_end = v_info0.start + v_info0.count;
|
||||
uint32_t v_info0_end = v_info0.start + v_info0.count - 2;
|
||||
for (uint32_t di = v_info0.start; di < v_info0_end; ++di) {
|
||||
assert(di < e_infos.size());
|
||||
uint32_t ti = e_infos[di].t_index;
|
||||
if (ti == ti0) continue; // ti0 will be deleted
|
||||
if (ti == ti1) continue; // ti1 will be deleted
|
||||
changed_triangle_indices.emplace_back(ti);
|
||||
}
|
||||
|
||||
// for each vertex1 triangles
|
||||
uint32_t v_info1_end = v_info1.start + v_info1.count;
|
||||
uint32_t v_info1_end = v_info1.start + v_info1.count - 2;
|
||||
for (uint32_t di = v_info1.start; di < v_info1_end; ++di) {
|
||||
assert(di < e_infos.size());
|
||||
EdgeInfo &e_info = e_infos[di];
|
||||
uint32_t ti = e_info.t_index;
|
||||
if (ti == ti0) continue; // ti0 will be deleted
|
||||
if (ti == ti1) continue; // ti1 will be deleted
|
||||
Triangle &t = its.indices[ti];
|
||||
t[e_info.edge] = vi0; // change index
|
||||
changed_triangle_indices.emplace_back(ti);
|
||||
|
@ -282,7 +300,9 @@ void Slic3r::its_quadric_edge_collapse(
|
|||
t_info1.set_deleted();
|
||||
// triangle counter decrementation
|
||||
actual_triangle_count-=2;
|
||||
#ifdef EXPENSIVE_DEBUG_CHECKS
|
||||
assert(check_neighbors(its, t_infos, v_infos, e_infos));
|
||||
#endif // EXPENSIVE_DEBUG_CHECKS
|
||||
}
|
||||
|
||||
// compact triangle
|
||||
|
@ -384,13 +404,6 @@ SymMat QuadricEdgeCollapse::create_quadric(const Triangle &t,
|
|||
std::tuple<TriangleInfos, VertexInfos, EdgeInfos, Errors>
|
||||
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());
|
||||
|
@ -506,6 +519,38 @@ std::optional<uint32_t> QuadricEdgeCollapse::find_triangle_index1(uint32_t
|
|||
return {};
|
||||
}
|
||||
|
||||
void QuadricEdgeCollapse::reorder_edges(EdgeInfos & e_infos,
|
||||
const VertexInfo &v_info,
|
||||
uint32_t ti0,
|
||||
uint32_t ti1)
|
||||
{
|
||||
// swap edge info of ti0 and ti1 to end(last one and one before)
|
||||
size_t v_info_end = v_info.start + v_info.count - 2;
|
||||
EdgeInfo &e_info_ti0 = e_infos[v_info_end];
|
||||
EdgeInfo &e_info_ti1 = e_infos[v_info_end+1];
|
||||
bool is_swaped = false;
|
||||
for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
|
||||
EdgeInfo &e_info = e_infos[ei];
|
||||
if (e_info.t_index == ti0) {
|
||||
std::swap(e_info, e_info_ti0);
|
||||
if (is_swaped) return;
|
||||
if (e_info.t_index == ti1) {
|
||||
std::swap(e_info, e_info_ti1);
|
||||
return;
|
||||
}
|
||||
is_swaped = true;
|
||||
} else if (e_info.t_index == ti1) {
|
||||
std::swap(e_info, e_info_ti1);
|
||||
if (is_swaped) return;
|
||||
if (e_info.t_index == ti0) {
|
||||
std::swap(e_info, e_info_ti0);
|
||||
return;
|
||||
}
|
||||
is_swaped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
|
||||
uint32_t ti0,
|
||||
uint32_t ti1,
|
||||
|
@ -519,12 +564,10 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
|
|||
static const float dot_thr = 0.2f; // Value from simplify mesh cca 80 DEG
|
||||
|
||||
// for each vertex triangles
|
||||
size_t v_info_end = v_info.start + v_info.count;
|
||||
size_t v_info_end = v_info.start + v_info.count-2;
|
||||
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 = its.indices[e_info.t_index];
|
||||
const Vec3f &normal = t_infos[e_info.t_index].n;
|
||||
const Vec3f &vf = its.vertices[t[(e_info.edge + 1) % 3]];
|
||||
|
@ -554,12 +597,10 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi,
|
|||
{
|
||||
// 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;
|
||||
size_t v_info_end = v_info.start + v_info.count - 2;
|
||||
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;
|
||||
|
@ -567,6 +608,52 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool QuadricEdgeCollapse::create_no_volume(
|
||||
uint32_t vi0 , uint32_t vi1,
|
||||
uint32_t ti0 , uint32_t ti1,
|
||||
const VertexInfo &v_info0, const VertexInfo &v_info1,
|
||||
const EdgeInfos & e_infos, const Indices &indices)
|
||||
{
|
||||
// check that triangles around vertex0 doesn't have half edge
|
||||
// with opposit order in set of triangles around vertex1
|
||||
// protect from creation of two triangles with oposit order - no volume space
|
||||
size_t v_info0_end = v_info0.start + v_info0.count - 2;
|
||||
size_t v_info1_end = v_info1.start + v_info1.count - 2;
|
||||
for (size_t ei0 = v_info0.start; ei0 < v_info0_end; ++ei0) {
|
||||
const EdgeInfo &e_info0 = e_infos[ei0];
|
||||
const Triangle &t0 = indices[e_info0.t_index];
|
||||
// edge CCW vertex indices are t0vi0, t0vi1
|
||||
size_t t0i = 0;
|
||||
uint32_t t0vi0 = static_cast<uint32_t>(t0[t0i]);
|
||||
if (t0vi0 == vi0) {
|
||||
++t0i;
|
||||
t0vi0 = static_cast<uint32_t>(t0[t0i]);
|
||||
}
|
||||
++t0i;
|
||||
uint32_t t0vi1 = static_cast<uint32_t>(t0[t0i]);
|
||||
if (t0vi1 == vi0) {
|
||||
++t0i;
|
||||
t0vi1 = static_cast<uint32_t>(t0[t0i]);
|
||||
}
|
||||
for (size_t ei1 = v_info1.start; ei1 < v_info1_end; ++ei1) {
|
||||
const EdgeInfo &e_info1 = e_infos[ei1];
|
||||
const Triangle &t1 = indices[e_info1.t_index];
|
||||
size_t t1i = 0;
|
||||
for (; t1i < 3; ++t1i) if (static_cast<uint32_t>(t1[t1i]) == t0vi1) break;
|
||||
if (t1i >= 3) continue; // without vertex index from triangle 0
|
||||
// check if second index is same too
|
||||
++t1i;
|
||||
if (t1i == 3) t1i = 0; // triangle loop(modulo 3)
|
||||
if (static_cast<uint32_t>(t1[t1i]) == vi1) {
|
||||
++t1i;
|
||||
if (t1i == 3) t1i = 0; // triangle loop(modulo 3)
|
||||
}
|
||||
if (static_cast<uint32_t>(t1[t1i]) == t0vi0) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t,
|
||||
const Vertices & vertices,
|
||||
const VertexInfos &v_infos)
|
||||
|
@ -732,7 +819,8 @@ void QuadricEdgeCollapse::compact(const VertexInfos & v_infos,
|
|||
its.indices.erase(its.indices.begin() + ti_new, its.indices.end());
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
#ifdef EXPENSIVE_DEBUG_CHECKS
|
||||
|
||||
// store triangle surrounding to file
|
||||
void QuadricEdgeCollapse::store_surround(const char *obj_filename,
|
||||
size_t triangle_index,
|
||||
|
@ -842,4 +930,4 @@ bool QuadricEdgeCollapse::check_neighbors(const indexed_triangle_set &its,
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
#endif /* EXPENSIVE_DEBUG_CHECKS */
|
||||
|
|
|
@ -2810,22 +2810,22 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
|
|||
Polygons brim;
|
||||
if (object.has_brim()) {
|
||||
// Calculate the area covered by the brim.
|
||||
const BrimType brim_type = object.config().brim_type;
|
||||
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
|
||||
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
|
||||
const auto brim_offset = scaled<float>(object.config().brim_offset.value + object.config().brim_width.value);
|
||||
const BrimType brim_type = object.config().brim_type;
|
||||
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
|
||||
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
|
||||
const auto brim_separation = scaled<float>(object.config().brim_separation.value + object.config().brim_width.value);
|
||||
for (const ExPolygon &ex : object.layers().front()->lslices) {
|
||||
if (brim_outer && brim_inner)
|
||||
polygons_append(brim, offset(ex, brim_offset));
|
||||
polygons_append(brim, offset(ex, brim_separation));
|
||||
else {
|
||||
if (brim_outer)
|
||||
polygons_append(brim, offset(ex.contour, brim_offset, ClipperLib::jtRound, float(scale_(0.1))));
|
||||
polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1))));
|
||||
else
|
||||
brim.emplace_back(ex.contour);
|
||||
if (brim_inner) {
|
||||
Polygons holes = ex.holes;
|
||||
polygons_reverse(holes);
|
||||
holes = offset(holes, - brim_offset, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
holes = offset(holes, - brim_separation, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
polygons_reverse(holes);
|
||||
polygons_append(brim, std::move(holes));
|
||||
} else
|
||||
|
|
|
@ -37,24 +37,37 @@
|
|||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha0 techs
|
||||
// 2.4.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA0 1
|
||||
#define ENABLE_2_4_0_ALPHA1 1
|
||||
|
||||
// Enable delayed rendering of transparent volumes
|
||||
#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable the fix of importing color print view from gcode files into GCodeViewer
|
||||
#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable drawing contours, at cut level, for sinking volumes
|
||||
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define ENABLE_SINKING_CONTOURS (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable implementation of retract acceleration in gcode processor
|
||||
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define ENABLE_RETRACT_ACCELERATION (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// 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)
|
||||
#define ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable rendering seams (and other options) in preview using models
|
||||
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define ENABLE_SEAMS_USING_MODELS (1 && ENABLE_2_4_0_ALPHA1)
|
||||
// Enable save and save as commands to be enabled also when the plater is empty and allow to load empty projects
|
||||
#define ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED (1 && ENABLE_2_4_0_ALPHA1)
|
||||
|
||||
|
||||
//====================
|
||||
// 2.4.0.alpha2 techs
|
||||
//====================
|
||||
#define ENABLE_2_4_0_ALPHA2 1
|
||||
|
||||
// Enable rendering seams (and other options) in preview using batched models on systems not supporting OpenGL 3.3
|
||||
#define ENABLE_SEAMS_USING_BATCHED_MODELS (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable fixing the z position of color change, pause print and custom gcode markers in preview
|
||||
#define ENABLE_FIX_PREVIEW_OPTIONS_Z (1 && ENABLE_SEAMS_USING_MODELS && ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER && ENABLE_2_4_0_ALPHA2)
|
||||
// Enable replacing a missing file during reload from disk command
|
||||
#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA0)
|
||||
#define ENABLE_RELOAD_FROM_DISK_REPLACE_FILE (1 && ENABLE_2_4_0_ALPHA2)
|
||||
|
||||
|
||||
#endif // _prusaslicer_technologies_h_
|
||||
|
|
|
@ -984,16 +984,11 @@ std::string log_memory_info(bool ignore_loglevel)
|
|||
} PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX;
|
||||
#endif /* PROCESS_MEMORY_COUNTERS_EX */
|
||||
|
||||
|
||||
HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId());
|
||||
if (hProcess != nullptr) {
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
|
||||
out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
|
||||
else
|
||||
out += " Used memory: N/A";
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||
if (GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc)))
|
||||
out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + "; PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + "; Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")";
|
||||
else
|
||||
out += " Used memory: N/A";
|
||||
#elif defined(__linux__) or defined(__APPLE__)
|
||||
// Get current memory usage.
|
||||
#ifdef __APPLE__
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
[Desktop Entry]
|
||||
Name=PrusaSlicer
|
||||
GenericName=3D Printing Software
|
||||
Icon=com.prusa3d.PrusaSlicer
|
||||
Exec=prusa-slicer %F
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=model/stl;model/x-wavefront-obj;model/3mf;model/x-geomview-off;application/x-amf;
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA
|
||||
StartupNotify=false
|
||||
StartupWMClass=prusa-slicer
|
|
@ -1,62 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop">
|
||||
<id>com.prusa3d.PrusaSlicer</id>
|
||||
<launchable type="desktop-id">com.prusa3d.PrusaSlicer.desktop</launchable>
|
||||
<provides>
|
||||
<id>prusa-slicer.desktop</id>
|
||||
</provides>
|
||||
<name>PrusaSlicer</name>
|
||||
<summary>Powerful 3D printing slicer optimized for Prusa printers</summary>
|
||||
<metadata_license>0BSD</metadata_license>
|
||||
<project_license>AGPL-3.0-only</project_license>
|
||||
<description>
|
||||
<p>
|
||||
PrusaSlicer takes 3D models (STL, OBJ, AMF) and converts them into G-code
|
||||
instructions for FFF printers or PNG layers for mSLA 3D printers. It's
|
||||
compatible with any modern printer based on the RepRap toolchain, including all
|
||||
those based on the Marlin, Prusa, Sprinter and Repetier firmware. It also works
|
||||
with Mach3, LinuxCNC and Machinekit controllers.
|
||||
</p>
|
||||
<p>
|
||||
PrusaSlicer is based on Slic3r by Alessandro Ranelucci and the RepRap community.
|
||||
</p>
|
||||
<p>
|
||||
What are some of PrusaSlicer's main features?
|
||||
</p>
|
||||
<ul>
|
||||
<li>multi-platform (Linux/Mac/Win) and packaged as standalone-app with no dependencies required</li>
|
||||
<li>complete command-line interface to use it with no GUI</li>
|
||||
<li>multi-material (multiple extruders) object printing</li>
|
||||
<li>multiple G-code flavors supported (RepRap, Makerbot, Mach3, Machinekit etc.)</li>
|
||||
<li>ability to plate multiple objects having distinct print settings</li>
|
||||
<li>multithread processing</li>
|
||||
<li>STL auto-repair (tolerance for broken models)</li>
|
||||
<li>wide automated unit testing</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="homepage">https://www.prusa3d.com/prusaslicer/</url>
|
||||
<url type="help">https://help.prusa3d.com</url>
|
||||
<url type="bugtracker">https://github.com/prusa3d/PrusaSlicer/issues</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>https://user-images.githubusercontent.com/590307/78981854-24d07580-7b21-11ea-9441-77923534a659.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://user-images.githubusercontent.com/590307/78981860-2863fc80-7b21-11ea-8c2d-8ff79ced2578.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>https://user-images.githubusercontent.com/590307/78981862-28fc9300-7b21-11ea-9b0d-d03e16b709d3.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="2.2.0" date="2020-03-21">
|
||||
<description>
|
||||
<p>This is final release of PrusaSlicer 2.2.0 introducing SLA hollowing and hole drilling, support for 3rd party printer vendors, 3Dconnexion support,
|
||||
automatic variable layer height, macOS dark mode support, greatly improved ColorPrint feature and much, much more.
|
||||
Several bugs found in the previous release candidate are fixed in this final release. See the respective change logs of the previous releases for all the
|
||||
new features, improvements and bugfixes in the 2.2.0 series.</p>
|
||||
</description>
|
||||
</release>
|
||||
</releases>
|
||||
</component>
|
|
@ -141,11 +141,13 @@ void Bed3D::Axes::render() const
|
|||
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model, bool force_as_custom)
|
||||
{
|
||||
auto check_texture = [](const std::string& texture) {
|
||||
return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture);
|
||||
boost::system::error_code ec; // so the exists call does not throw (e.g. after a permission problem)
|
||||
return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture, ec);
|
||||
};
|
||||
|
||||
auto check_model = [](const std::string& model) {
|
||||
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model);
|
||||
boost::system::error_code ec;
|
||||
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model, ec);
|
||||
};
|
||||
|
||||
EType type;
|
||||
|
@ -161,12 +163,16 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
|
|||
}
|
||||
|
||||
std::string texture_filename = custom_texture.empty() ? texture : custom_texture;
|
||||
if (!check_texture(texture_filename))
|
||||
if (! texture_filename.empty() && ! check_texture(texture_filename)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to load bed texture: " << texture_filename;
|
||||
texture_filename.clear();
|
||||
}
|
||||
|
||||
std::string model_filename = custom_model.empty() ? model : custom_model;
|
||||
if (!check_model(model_filename))
|
||||
if (! model_filename.empty() && ! check_model(model_filename)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to load bed model: " << model_filename;
|
||||
model_filename.clear();
|
||||
}
|
||||
|
||||
if (m_shape == shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
|
||||
// No change, no need to update the UI.
|
||||
|
|
|
@ -574,11 +574,8 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn
|
|||
// Some FFF status was invalidated, and the G-code was not exported yet.
|
||||
// Let the G-code preview UI know that the final G-code preview is not valid.
|
||||
// In addition, this early memory deallocation reduces memory footprint.
|
||||
if (m_gcode_result != nullptr) {
|
||||
//FIXME calling platter from here is not a staple of a good architecture.
|
||||
GUI::wxGetApp().plater()->stop_mapping_gcode_window();
|
||||
if (m_gcode_result != nullptr)
|
||||
m_gcode_result->reset();
|
||||
}
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
|
|
|
@ -281,7 +281,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
toggle_field(el, have_skirt);
|
||||
|
||||
bool have_brim = config->opt_enum<BrimType>("brim_type") != btNoBrim;
|
||||
for (auto el : { "brim_width", "brim_offset" })
|
||||
for (auto el : { "brim_width", "brim_separation" })
|
||||
toggle_field(el, have_brim);
|
||||
// perimeter_extruder uses the same logic as in Print::extruders()
|
||||
toggle_field("perimeter_extruder", have_perimeters || have_brim);
|
||||
|
@ -312,7 +312,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
|||
toggle_field("support_material_speed", have_support_material || have_brim || have_skirt);
|
||||
|
||||
toggle_field("raft_contact_distance", have_raft && !have_support_soluble);
|
||||
toggle_field("raft_expansion", have_raft);
|
||||
for (auto el : { "raft_expansion", "first_layer_acceleration_over_raft", "first_layer_speed_over_raft" })
|
||||
toggle_field(el, have_raft);
|
||||
|
||||
bool has_ironing = config->opt_bool("ironing");
|
||||
for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_speed" })
|
||||
|
|
|
@ -382,40 +382,44 @@ void DesktopIntegrationDialog::perform_desktop_integration()
|
|||
app_config->set("desktop_integration_app_path", GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix));
|
||||
|
||||
// Repeat for Gcode viewer - use same paths as for slicer files
|
||||
// Icon
|
||||
if (!target_dir_icons.empty())
|
||||
{
|
||||
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (copy_icon(icon_path, dest_path))
|
||||
// save path to icon
|
||||
app_config->set("desktop_integration_icon_viewer_path", dest_path);
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
|
||||
}
|
||||
// Do NOT add gcode viewer desktop file on ChromeOS
|
||||
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
|
||||
// Icon
|
||||
if (!target_dir_icons.empty())
|
||||
{
|
||||
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
|
||||
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
|
||||
if (copy_icon(icon_path, dest_path))
|
||||
// save path to icon
|
||||
app_config->set("desktop_integration_icon_viewer_path", dest_path);
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
|
||||
}
|
||||
|
||||
// Desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Prusa Gcode Viewer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer-gcodeviewer%2%\n"
|
||||
"Exec=\"%3%\" --gcodeviewer %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/x.gcode;\n"
|
||||
"Categories=Graphics;3DGraphics;\n"
|
||||
"Keywords=3D;Printing;Slicer;\n"
|
||||
"StartupNotify=false\n", name_suffix, version_suffix, excutable_path);
|
||||
// Desktop file
|
||||
std::string desktop_file = GUI::format(
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Prusa Gcode Viewer%1%\n"
|
||||
"GenericName=3D Printing Software\n"
|
||||
"Icon=PrusaSlicer-gcodeviewer%2%\n"
|
||||
"Exec=\"%3%\" --gcodeviewer %%F\n"
|
||||
"Terminal=false\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/x.gcode;\n"
|
||||
"Categories=Graphics;3DGraphics;\n"
|
||||
"Keywords=3D;Printing;Slicer;\n"
|
||||
"StartupNotify=false\n", name_suffix, version_suffix, excutable_path);
|
||||
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file))
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
||||
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
|
||||
if (create_desktop_file(desktop_path, desktop_file))
|
||||
// save path to desktop file
|
||||
app_config->set("desktop_integration_app_viewer_path", desktop_path);
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
|
||||
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
|
||||
}
|
||||
}
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
|
||||
}
|
||||
void DesktopIntegrationDialog::undo_desktop_intgration()
|
||||
|
@ -433,17 +437,20 @@ void DesktopIntegrationDialog::undo_desktop_intgration()
|
|||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viwer .desktop
|
||||
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viewer icon
|
||||
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
// No gcode viewer at ChromeOS
|
||||
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
|
||||
// gcode viewer .desktop
|
||||
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
// gcode viewer icon
|
||||
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
|
||||
if (!path.empty()) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
|
||||
std::remove(path.c_str());
|
||||
}
|
||||
}
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
|
||||
}
|
||||
|
|
|
@ -230,18 +230,21 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
}
|
||||
double val;
|
||||
|
||||
bool is_na_value = m_opt.nullable && str == na_value();
|
||||
|
||||
const char dec_sep = is_decimal_separator_point() ? '.' : ',';
|
||||
const char dec_sep_alt = dec_sep == '.' ? ',' : '.';
|
||||
// Replace the first incorrect separator in decimal number.
|
||||
if (str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
// Replace the first incorrect separator in decimal number,
|
||||
// if this value doesn't "N/A" value in some language
|
||||
// see https://github.com/prusa3d/PrusaSlicer/issues/6921
|
||||
if (!is_na_value && str.Replace(dec_sep_alt, dec_sep, false) != 0)
|
||||
set_value(str, false);
|
||||
|
||||
|
||||
if (str == dec_sep)
|
||||
val = 0.0;
|
||||
else
|
||||
{
|
||||
if (m_opt.nullable && str == na_value())
|
||||
if (is_na_value)
|
||||
val = ConfigOptionFloatsNullable::nil_value();
|
||||
else if (!str.ToDouble(&val))
|
||||
{
|
||||
|
|
|
@ -70,18 +70,9 @@ static std::vector<std::array<float, 4>> decode_colors(const std::vector<std::st
|
|||
return output;
|
||||
}
|
||||
|
||||
static float round_to_nearest(float value, unsigned int decimals)
|
||||
static float round_to_nearest_percent(float value)
|
||||
{
|
||||
float res = 0.0f;
|
||||
if (decimals == 0)
|
||||
res = std::round(value);
|
||||
else {
|
||||
char buf[64];
|
||||
// locales should not matter, both sprintf and stof are sensitive, so...
|
||||
sprintf(buf, "%.*g", decimals, value);
|
||||
res = std::stof(buf);
|
||||
}
|
||||
return res;
|
||||
return std::round(value * 100.f) * 0.01f;
|
||||
}
|
||||
|
||||
void GCodeViewer::VBuffer::reset()
|
||||
|
@ -146,7 +137,7 @@ bool GCodeViewer::Path::matches(const GCodeProcessor::MoveVertex& move) const
|
|||
// use rounding to reduce the number of generated paths
|
||||
return type == move.type && extruder_id == move.extruder_id && cp_color_id == move.cp_color_id && role == move.extrusion_role &&
|
||||
move.position.z() <= sub_paths.front().first.position.z() && feedrate == move.feedrate && fan_speed == move.fan_speed &&
|
||||
height == round_to_nearest(move.height, 2) && width == round_to_nearest(move.width, 2) &&
|
||||
height == round_to_nearest_percent(move.height) && width == round_to_nearest_percent(move.width) &&
|
||||
matches_percent(volumetric_rate, move.volumetric_rate(), 0.05f);
|
||||
}
|
||||
case EMoveType::Travel: {
|
||||
|
@ -183,7 +174,7 @@ void GCodeViewer::TBuffer::add_path(const GCodeProcessor::MoveVertex& move, unsi
|
|||
Path::Endpoint endpoint = { b_id, i_id, s_id, move.position };
|
||||
// use rounding to reduce the number of generated paths
|
||||
paths.push_back({ move.type, move.extrusion_role, move.delta_extruder,
|
||||
round_to_nearest(move.height, 2), round_to_nearest(move.width, 2),
|
||||
round_to_nearest_percent(move.height), round_to_nearest_percent(move.width),
|
||||
move.feedrate, move.fan_speed, move.temperature,
|
||||
move.volumetric_rate(), move.extruder_id, move.cp_color_id, { { endpoint, endpoint } } });
|
||||
}
|
||||
|
@ -293,45 +284,14 @@ void GCodeViewer::SequentialView::Marker::render() const
|
|||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode()
|
||||
void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends)
|
||||
{
|
||||
if (m_filename.empty())
|
||||
return;
|
||||
|
||||
assert(! m_file.is_open());
|
||||
if (m_file.is_open())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// generate mapping for accessing data in file by line number
|
||||
boost::nowide::ifstream f(m_filename);
|
||||
|
||||
f.seekg(0, f.end);
|
||||
uint64_t file_length = static_cast<uint64_t>(f.tellg());
|
||||
f.seekg(0, f.beg);
|
||||
|
||||
std::string line;
|
||||
uint64_t offset = 0;
|
||||
while (std::getline(f, line)) {
|
||||
uint64_t line_length = static_cast<uint64_t>(line.length());
|
||||
m_lines_map.push_back({ offset, line_length });
|
||||
offset += static_cast<uint64_t>(line_length) + 1;
|
||||
}
|
||||
|
||||
if (offset != file_length) {
|
||||
// if the final offset does not match with file length, lines are terminated with CR+LF
|
||||
// so update all offsets accordingly
|
||||
for (size_t i = 0; i < m_lines_map.size(); ++i) {
|
||||
m_lines_map[i].first += static_cast<uint64_t>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to load data from " << m_filename << ". Cannot show G-code window.";
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
m_filename = filename;
|
||||
m_lines_ends = std::move(lines_ends);
|
||||
|
||||
m_selected_line_id = 0;
|
||||
m_last_lines_size = 0;
|
||||
|
@ -354,7 +314,9 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
|
|||
ret.reserve(end_id - start_id + 1);
|
||||
for (uint64_t id = start_id; id <= end_id; ++id) {
|
||||
// read line from file
|
||||
std::string gline(m_file.data() + m_lines_map[id - 1].first, m_lines_map[id - 1].second);
|
||||
const size_t start = id == 1 ? 0 : m_lines_ends[id - 2];
|
||||
const size_t len = m_lines_ends[id - 1] - start;
|
||||
std::string gline(m_file.data() + start, len);
|
||||
|
||||
std::string command;
|
||||
std::string parameters;
|
||||
|
@ -388,7 +350,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
|
|||
static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f };
|
||||
static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f };
|
||||
|
||||
if (!m_visible || m_filename.empty() || m_lines_map.empty() || curr_line_id == 0)
|
||||
if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0)
|
||||
return;
|
||||
|
||||
// window height
|
||||
|
@ -406,8 +368,8 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
|
|||
const uint64_t half_lines_count = lines_count / 2;
|
||||
uint64_t start_id = (curr_line_id >= half_lines_count) ? curr_line_id - half_lines_count : 0;
|
||||
uint64_t end_id = start_id + lines_count - 1;
|
||||
if (end_id >= static_cast<uint64_t>(m_lines_map.size())) {
|
||||
end_id = static_cast<uint64_t>(m_lines_map.size()) - 1;
|
||||
if (end_id >= static_cast<uint64_t>(m_lines_ends.size())) {
|
||||
end_id = static_cast<uint64_t>(m_lines_ends.size()) - 1;
|
||||
start_id = end_id - lines_count + 1;
|
||||
}
|
||||
|
||||
|
@ -512,7 +474,7 @@ void GCodeViewer::SequentialView::render(float legend_height) const
|
|||
}
|
||||
|
||||
const std::vector<GCodeViewer::Color> GCodeViewer::Extrusion_Role_Colors {{
|
||||
{ 0.75f, 0.75f, 0.75f, 1.0f }, // erNone
|
||||
{ 0.90f, 0.70f, 0.70f, 1.0f }, // erNone
|
||||
{ 1.00f, 0.90f, 0.30f, 1.0f }, // erPerimeter
|
||||
{ 1.00f, 0.49f, 0.22f, 1.0f }, // erExternalPerimeter
|
||||
{ 0.12f, 0.12f, 1.00f, 1.0f }, // erOverhangPerimeter
|
||||
|
@ -615,8 +577,7 @@ void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print&
|
|||
// release gpu memory, if used
|
||||
reset();
|
||||
|
||||
m_sequential_view.gcode_window.set_filename(gcode_result.filename);
|
||||
m_sequential_view.gcode_window.load_gcode();
|
||||
m_sequential_view.gcode_window.load_gcode(gcode_result.filename, gcode_result.lines_ends);
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
|
@ -704,8 +665,8 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
|
|||
// update tool colors
|
||||
m_tool_colors = decode_colors(str_tool_colors);
|
||||
|
||||
// ensure at least one (default) color is defined
|
||||
if (m_tool_colors.empty())
|
||||
// ensure there are enough colors defined
|
||||
while (m_tool_colors.size() < std::max(size_t(1), gcode_result.extruders_count))
|
||||
m_tool_colors.push_back(decode_color("#FF8000"));
|
||||
|
||||
// update ranges for coloring / legend
|
||||
|
@ -721,11 +682,11 @@ void GCodeViewer::refresh(const GCodeProcessor::Result& gcode_result, const std:
|
|||
{
|
||||
case EMoveType::Extrude:
|
||||
{
|
||||
m_extrusions.ranges.height.update_from(round_to_nearest(curr.height, 2));
|
||||
m_extrusions.ranges.width.update_from(round_to_nearest(curr.width, 2));
|
||||
m_extrusions.ranges.height.update_from(round_to_nearest_percent(curr.height));
|
||||
m_extrusions.ranges.width.update_from(round_to_nearest_percent(curr.width));
|
||||
m_extrusions.ranges.fan_speed.update_from(curr.fan_speed);
|
||||
m_extrusions.ranges.temperature.update_from(curr.temperature);
|
||||
m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest(curr.volumetric_rate(), 2));
|
||||
m_extrusions.ranges.volumetric_rate.update_from(round_to_nearest_percent(curr.volumetric_rate()));
|
||||
[[fallthrough]];
|
||||
}
|
||||
case EMoveType::Travel:
|
||||
|
@ -807,7 +768,26 @@ void GCodeViewer::render()
|
|||
case EMoveType::Unretract:
|
||||
case EMoveType::Seam: {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 1)) {
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::InstancedModel;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::InstancedModel;
|
||||
}
|
||||
else {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::BatchedModel;
|
||||
buffer.vertices.format = VBuffer::EFormat::PositionNormal3;
|
||||
buffer.shader = "gouraud_light";
|
||||
|
||||
buffer.model.data = diamond(16);
|
||||
buffer.model.color = option_color(type);
|
||||
buffer.model.instances.format = InstanceVBuffer::EFormat::BatchedModel;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
if (wxGetApp().is_gl_version_greater_or_equal_to(3, 3)) {
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Model;
|
||||
buffer.shader = "gouraud_light_instanced";
|
||||
buffer.model.model.init_from(diamond(16));
|
||||
|
@ -819,6 +799,7 @@ void GCodeViewer::render()
|
|||
buffer.shader = wxGetApp().is_glsl_version_greater_or_equal_to(1, 20) ? "options_120" : "options_110";
|
||||
}
|
||||
break;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#else
|
||||
buffer.render_primitive_type = TBuffer::ERenderPrimitiveType::Point;
|
||||
buffer.vertices.format = VBuffer::EFormat::Position;
|
||||
|
@ -1100,7 +1081,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||
// save vertices to file
|
||||
fprintf(fp, "\n# vertices\n");
|
||||
for (const Vec3f& v : out_vertices) {
|
||||
fprintf(fp, "v %g %g %g\n", v.x(), v.y(), v.x());
|
||||
fprintf(fp, "v %g %g %g\n", v.x(), v.y(), v.z());
|
||||
}
|
||||
|
||||
// save normals to file
|
||||
|
@ -1155,16 +1136,6 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||
fclose(fp);
|
||||
}
|
||||
|
||||
void GCodeViewer::start_mapping_gcode_window()
|
||||
{
|
||||
m_sequential_view.gcode_window.load_gcode();
|
||||
}
|
||||
|
||||
void GCodeViewer::stop_mapping_gcode_window()
|
||||
{
|
||||
m_sequential_view.gcode_window.stop_mapping_file();
|
||||
}
|
||||
|
||||
void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||
{
|
||||
// max index buffer size, in bytes
|
||||
|
@ -1219,9 +1190,9 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
};
|
||||
auto add_indices_as_line = [](const GCodeProcessor::MoveVertex& prev, const GCodeProcessor::MoveVertex& curr, TBuffer& buffer,
|
||||
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
|
||||
if (prev.type != curr.type || !buffer.paths.back().matches(curr)) {
|
||||
if (buffer.paths.empty() || prev.type != curr.type || !buffer.paths.back().matches(curr)) {
|
||||
// add starting index
|
||||
indices.push_back(static_cast<unsigned int>(indices.size()));
|
||||
indices.push_back(static_cast<IBufferType>(indices.size()));
|
||||
buffer.add_path(curr, ibuffer_id, indices.size() - 1, move_id - 1);
|
||||
buffer.paths.back().sub_paths.front().first.position = prev.position;
|
||||
}
|
||||
|
@ -1229,11 +1200,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
Path& last_path = buffer.paths.back();
|
||||
if (last_path.sub_paths.front().first.i_id != last_path.sub_paths.back().last.i_id) {
|
||||
// add previous index
|
||||
indices.push_back(static_cast<unsigned int>(indices.size()));
|
||||
indices.push_back(static_cast<IBufferType>(indices.size()));
|
||||
}
|
||||
|
||||
// add current index
|
||||
indices.push_back(static_cast<unsigned int>(indices.size()));
|
||||
indices.push_back(static_cast<IBufferType>(indices.size()));
|
||||
last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position };
|
||||
};
|
||||
|
||||
|
@ -1436,20 +1407,63 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
// format data into the buffers to be rendered as model
|
||||
// format data into the buffers to be rendered as instanced model
|
||||
auto add_model_instance = [](const GCodeProcessor::MoveVertex& curr, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
|
||||
// append position
|
||||
instances.push_back(curr.position.x());
|
||||
instances.push_back(curr.position.y());
|
||||
instances.push_back(curr.position.z());
|
||||
// append width
|
||||
instances.push_back(1.5f * curr.width);
|
||||
instances.push_back(curr.width);
|
||||
// append height
|
||||
instances.push_back(1.5f * curr.height);
|
||||
instances.push_back(curr.height);
|
||||
|
||||
// append id
|
||||
instances_ids.push_back(move_id);
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// format data into the buffers to be rendered as batched model
|
||||
auto add_vertices_as_model_batch = [](const GCodeProcessor::MoveVertex& curr, const GLModel::InitializationData& data, VertexBuffer& vertices, InstanceBuffer& instances, InstanceIdBuffer& instances_ids, size_t move_id) {
|
||||
const double width = static_cast<double>(1.5f * curr.width);
|
||||
const double height = static_cast<double>(1.5f * curr.height);
|
||||
|
||||
const Transform3d trafo = Geometry::assemble_transform((curr.position - 0.5f * curr.height * Vec3f::UnitZ()).cast<double>(), Vec3d::Zero(), { width, width, height });
|
||||
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> normal_matrix = trafo.matrix().template block<3, 3>(0, 0).inverse().transpose();
|
||||
|
||||
for (const auto& entity : data.entities) {
|
||||
// append vertices
|
||||
for (size_t i = 0; i < entity.positions.size(); ++i) {
|
||||
// append position
|
||||
const Vec3d position = trafo * entity.positions[i].cast<double>();
|
||||
vertices.push_back(static_cast<float>(position.x()));
|
||||
vertices.push_back(static_cast<float>(position.y()));
|
||||
vertices.push_back(static_cast<float>(position.z()));
|
||||
|
||||
// append normal
|
||||
const Vec3d normal = normal_matrix * entity.normals[i].cast<double>();
|
||||
vertices.push_back(static_cast<float>(normal.x()));
|
||||
vertices.push_back(static_cast<float>(normal.y()));
|
||||
vertices.push_back(static_cast<float>(normal.z()));
|
||||
}
|
||||
}
|
||||
|
||||
// append instance position
|
||||
instances.push_back(curr.position.x());
|
||||
instances.push_back(curr.position.y());
|
||||
instances.push_back(curr.position.z());
|
||||
// append instance id
|
||||
instances_ids.push_back(move_id);
|
||||
};
|
||||
|
||||
auto add_indices_as_model_batch = [](const GLModel::InitializationData& data, IndexBuffer& indices, IBufferType base_index) {
|
||||
for (const auto& entity : data.entities) {
|
||||
for (size_t i = 0; i < entity.indices.size(); ++i) {
|
||||
indices.push_back(static_cast<IBufferType>(entity.indices[i] + base_index));
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
@ -1533,7 +1547,12 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
|
||||
// if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
|
||||
// add another vertex buffer
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t vertices_size_to_add = (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) ? t_buffer.model.data.vertices_size_bytes() : t_buffer.max_vertices_per_segment_size_bytes();
|
||||
if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - vertices_size_to_add) {
|
||||
#else
|
||||
if (v_multibuffer.back().size() * sizeof(float) > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
v_multibuffer.push_back(VertexBuffer());
|
||||
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Triangle) {
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
|
@ -1550,6 +1569,24 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; }
|
||||
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, i); break; }
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
case TBuffer::ERenderPrimitiveType::InstancedModel:
|
||||
{
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.instances_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
break;
|
||||
}
|
||||
case TBuffer::ERenderPrimitiveType::BatchedModel:
|
||||
{
|
||||
add_vertices_as_model_batch(curr, t_buffer.model.data, v_buffer, inst_buffer, inst_id_buffer, i);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.batched_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
break;
|
||||
}
|
||||
#else
|
||||
case TBuffer::ERenderPrimitiveType::Model:
|
||||
{
|
||||
add_model_instance(curr, inst_buffer, inst_id_buffer, i);
|
||||
|
@ -1559,6 +1596,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
break;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
}
|
||||
|
||||
// collect options zs for later use
|
||||
|
@ -1741,6 +1779,23 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
for (size_t i = 0; i < m_buffers.size(); ++i) {
|
||||
TBuffer& t_buffer = m_buffers[i];
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) {
|
||||
const InstanceBuffer& inst_buffer = instances[i];
|
||||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) {
|
||||
const InstanceBuffer& inst_buffer = instances[i];
|
||||
if (!inst_buffer.empty()) {
|
||||
t_buffer.model.instances.buffer = inst_buffer;
|
||||
t_buffer.model.instances.s_ids = instances_ids[i];
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) {
|
||||
const InstanceBuffer& inst_buffer = instances[i];
|
||||
if (!inst_buffer.empty()) {
|
||||
|
@ -1749,6 +1804,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
}
|
||||
}
|
||||
else {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
const MultiVertexBuffer& v_multibuffer = vertices[i];
|
||||
for (const VertexBuffer& v_buffer : v_multibuffer) {
|
||||
|
@ -1842,10 +1898,20 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
|
||||
// if adding the indices for the current segment exceeds the threshold size of the current index buffer
|
||||
// create another index buffer
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t indiced_size_to_add = (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) ? t_buffer.model.data.indices_size_bytes() : t_buffer.max_indices_per_segment_size_bytes();
|
||||
if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - indiced_size_to_add) {
|
||||
#else
|
||||
if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - t_buffer.max_indices_per_segment_size_bytes()) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
i_multibuffer.push_back(IndexBuffer());
|
||||
vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point &&
|
||||
t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) {
|
||||
#else
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
|
||||
}
|
||||
|
@ -1853,14 +1919,24 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
|
||||
// if adding the vertices for the current segment exceeds the threshold size of the current vertex buffer
|
||||
// create another index buffer
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t vertices_size_to_add = (t_buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) ? t_buffer.model.data.vertices_size_bytes() : t_buffer.max_vertices_per_segment_size_bytes();
|
||||
if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - vertices_size_to_add) {
|
||||
#else
|
||||
if (curr_vertex_buffer.second * t_buffer.vertices.vertex_size_bytes() > t_buffer.vertices.max_size_bytes() - t_buffer.max_vertices_per_segment_size_bytes()) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
i_multibuffer.push_back(IndexBuffer());
|
||||
|
||||
++curr_vertex_buffer.first;
|
||||
curr_vertex_buffer.second = 0;
|
||||
vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point &&
|
||||
t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) {
|
||||
#else
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
Path& last_path = t_buffer.paths.back();
|
||||
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, i - 1);
|
||||
}
|
||||
|
@ -1884,6 +1960,13 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
add_indices_as_solid(prev, curr, next, t_buffer, curr_vertex_buffer.second, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, i);
|
||||
break;
|
||||
}
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
case TBuffer::ERenderPrimitiveType::BatchedModel: {
|
||||
add_indices_as_model_batch(t_buffer.model.data, i_buffer, curr_vertex_buffer.second);
|
||||
curr_vertex_buffer.second += t_buffer.model.data.vertices_count();
|
||||
break;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
default: { break; }
|
||||
}
|
||||
}
|
||||
|
@ -1898,7 +1981,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
|||
for (size_t i = 0; i < m_buffers.size(); ++i) {
|
||||
TBuffer& t_buffer = m_buffers[i];
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::InstancedModel) {
|
||||
#else
|
||||
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
const MultiIndexBuffer& i_multibuffer = indices[i];
|
||||
for (const IndexBuffer& i_buffer : i_multibuffer) {
|
||||
|
@ -2174,7 +2261,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
continue;
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel ||
|
||||
buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) {
|
||||
#else
|
||||
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
for (size_t id : buffer.model.instances.s_ids) {
|
||||
if (id < m_layers.get_endpoints_at(m_layers_z_range[0]).first || m_layers.get_endpoints_at(m_layers_z_range[1]).last < id)
|
||||
continue;
|
||||
|
@ -2239,7 +2331,12 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
bool found = false;
|
||||
for (const TBuffer& buffer : m_buffers) {
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel ||
|
||||
buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) {
|
||||
#else
|
||||
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
for (size_t i = 0; i < buffer.model.instances.s_ids.size(); ++i) {
|
||||
if (buffer.model.instances.s_ids[i] == m_sequential_view.current.last) {
|
||||
size_t offset = i * buffer.model.instances.instance_size_floats();
|
||||
|
@ -2400,15 +2497,24 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
}
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// second pass: for buffers using instanced and batched models, update the instances render ranges
|
||||
#else
|
||||
// second pass: for buffers using instanced models, update the instances render ranges
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
for (size_t b = 0; b < m_buffers.size(); ++b) {
|
||||
TBuffer& buffer = const_cast<TBuffer&>(m_buffers[b]);
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::InstancedModel &&
|
||||
buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel)
|
||||
#else
|
||||
if (buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Model)
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
continue;
|
||||
|
||||
buffer.model.instances.render_ranges.reset();
|
||||
|
||||
if (!buffer.visible)
|
||||
if (!buffer.visible || buffer.model.instances.s_ids.empty())
|
||||
continue;
|
||||
|
||||
buffer.model.instances.render_ranges.ranges.push_back({ 0, 0, 0, buffer.model.color });
|
||||
|
@ -2419,7 +2525,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
|
|||
if (m_sequential_view.current.first <= buffer.model.instances.s_ids.back() && buffer.model.instances.s_ids.front() <= m_sequential_view.current.last) {
|
||||
for (size_t id : buffer.model.instances.s_ids) {
|
||||
if (has_second_range) {
|
||||
#if ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
if (id < m_sequential_view.endpoints.first) {
|
||||
#else
|
||||
if (id <= m_sequential_view.endpoints.first) {
|
||||
#endif // ENABLE_FIX_PREVIEW_OPTIONS_Z
|
||||
++buffer.model.instances.render_ranges.ranges.front().offset;
|
||||
if (id <= m_sequential_view.current.first)
|
||||
++buffer.model.instances.render_ranges.ranges.back().offset;
|
||||
|
@ -2665,7 +2775,11 @@ void GCodeViewer::render_toolpaths()
|
|||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
auto render_as_instanced_model = [this]
|
||||
#else
|
||||
auto render_as_instanced_model = []
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
(TBuffer& buffer, GLShaderProgram & shader) {
|
||||
for (auto& range : buffer.model.instances.render_ranges.ranges) {
|
||||
if (range.vbo == 0 && range.count > 0) {
|
||||
|
@ -2685,6 +2799,66 @@ void GCodeViewer::render_toolpaths()
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
auto render_as_batched_model = [this](TBuffer& buffer, GLShaderProgram& shader) {
|
||||
#else
|
||||
auto render_as_batched_model = [](TBuffer& buffer, GLShaderProgram& shader) {
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
struct Range
|
||||
{
|
||||
unsigned int first;
|
||||
unsigned int last;
|
||||
bool intersects(const Range& other) const { return (other.last < first || other.first > last) ? false : true; }
|
||||
};
|
||||
Range buffer_range = { 0, 0 };
|
||||
size_t indices_per_instance = buffer.model.data.indices_count();
|
||||
|
||||
for (size_t j = 0; j < buffer.indices.size(); ++j) {
|
||||
const IBuffer& i_buffer = buffer.indices[j];
|
||||
buffer_range.last = buffer_range.first + i_buffer.count / indices_per_instance;
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, i_buffer.vbo));
|
||||
glsafe(::glVertexPointer(buffer.vertices.position_size_floats(), GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.position_offset_bytes()));
|
||||
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
|
||||
bool has_normals = buffer.vertices.normal_size_floats() > 0;
|
||||
if (has_normals) {
|
||||
glsafe(::glNormalPointer(GL_FLOAT, buffer.vertices.vertex_size_bytes(), (const void*)buffer.vertices.normal_offset_bytes()));
|
||||
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
|
||||
}
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, i_buffer.ibo));
|
||||
|
||||
for (auto& range : buffer.model.instances.render_ranges.ranges) {
|
||||
Range range_range = { range.offset, range.offset + range.count };
|
||||
if (range_range.intersects(buffer_range)) {
|
||||
shader.set_uniform("uniform_color", range.color);
|
||||
unsigned int offset = (range_range.first > buffer_range.first) ? range_range.first - buffer_range.first : 0;
|
||||
size_t offset_bytes = static_cast<size_t>(offset) * indices_per_instance * sizeof(IBufferType);
|
||||
Range render_range = { std::max(range_range.first, buffer_range.first), std::min(range_range.last, buffer_range.last) };
|
||||
size_t count = static_cast<size_t>(render_range.last - render_range.first) * indices_per_instance;
|
||||
if (count > 0) {
|
||||
glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)count, GL_UNSIGNED_SHORT, (const void*)offset_bytes));
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
++m_statistics.gl_batched_models_calls_count;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
|
||||
|
||||
if (has_normals)
|
||||
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
|
||||
|
||||
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
|
||||
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
|
||||
|
||||
buffer_range.first = buffer_range.last;
|
||||
}
|
||||
};
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
||||
auto line_width = [](double zoom) {
|
||||
|
@ -2708,11 +2882,22 @@ void GCodeViewer::render_toolpaths()
|
|||
shader->start_using();
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) {
|
||||
#else
|
||||
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Model) {
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
shader->set_uniform("emission_factor", 0.25f);
|
||||
render_as_instanced_model(buffer, *shader);
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
}
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) {
|
||||
shader->set_uniform("emission_factor", 0.25f);
|
||||
render_as_batched_model(buffer, *shader);
|
||||
shader->set_uniform("emission_factor", 0.0f);
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
else {
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
for (size_t j = 0; j < buffer.indices.size(); ++j) {
|
||||
|
@ -2860,7 +3045,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
|
||||
bool imperial_units = wxGetApp().app_config->get("use_inches") == "1";
|
||||
|
||||
auto append_item = [this, icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
|
||||
auto append_item = [icon_size, percent_bar_size, &imgui, imperial_units](EItemType type, const Color& color, const std::string& label,
|
||||
bool visible = true, const std::string& time = "", float percent = 0.0f, float max_percent = 0.0f, const std::array<float, 4>& offsets = { 0.0f, 0.0f, 0.0f, 0.0f },
|
||||
double used_filament_m = 0.0, double used_filament_g = 0.0,
|
||||
std::function<void()> callback = nullptr) {
|
||||
|
@ -2878,6 +3063,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
}
|
||||
case EItemType::Circle: {
|
||||
ImVec2 center(0.5f * (pos.x + pos.x + icon_size), 0.5f * (pos.y + pos.y + icon_size));
|
||||
#if !ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (m_buffers[buffer_id(EMoveType::Retract)].shader == "options_120") {
|
||||
draw_list->AddCircleFilled(center, 0.5f * icon_size,
|
||||
ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16);
|
||||
|
@ -2887,6 +3073,7 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
draw_list->AddCircleFilled(center, radius, ImGui::GetColorU32({ 0.5f * color[0], 0.5f * color[1], 0.5f * color[2], 1.0f }), 16);
|
||||
}
|
||||
else
|
||||
#endif // !ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
draw_list->AddCircleFilled(center, 0.5f * icon_size, ImGui::GetColorU32({ color[0], color[1], color[2], 1.0f }), 16);
|
||||
|
||||
break;
|
||||
|
@ -3532,7 +3719,11 @@ void GCodeViewer::render_legend(float& legend_height)
|
|||
auto add_option = [this, append_item](EMoveType move_type, EOptionsColors color, const std::string& text) {
|
||||
const TBuffer& buffer = m_buffers[buffer_id(move_type)];
|
||||
if (buffer.visible && buffer.has_data())
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
append_item(EItemType::Circle, Options_Colors[static_cast<unsigned int>(color)], text);
|
||||
#else
|
||||
append_item((buffer.shader == "options_110") ? EItemType::Rect : EItemType::Circle, Options_Colors[static_cast<unsigned int>(color)], text);
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
};
|
||||
|
||||
// options section
|
||||
|
@ -3752,7 +3943,11 @@ void GCodeViewer::render_statistics()
|
|||
add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count);
|
||||
add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count);
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
ImGui::Separator();
|
||||
add_counter(std::string("Instanced models:"), m_statistics.gl_instanced_models_calls_count);
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
add_counter(std::string("Batched models:"), m_statistics.gl_batched_models_calls_count);
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
|
@ -3784,6 +3979,9 @@ void GCodeViewer::render_statistics()
|
|||
add_counter(std::string("Extrude segments count:"), m_statistics.extrude_segments_count);
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
add_counter(std::string("Instances count:"), m_statistics.instances_count);
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
add_counter(std::string("Batched count:"), m_statistics.batched_count);
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
ImGui::Separator();
|
||||
add_counter(std::string("VBuffers count:"), m_statistics.vbuffers_count);
|
||||
|
|
|
@ -105,9 +105,16 @@ class GCodeViewer
|
|||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// buffer containing instances data used to render a toolpaths using instanced or batched models
|
||||
// instance record format:
|
||||
// instanced models: 5 floats -> position.x|position.y|position.z|width|height (which are sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced())
|
||||
// batched models: 3 floats -> position.x|position.y|position.z
|
||||
#else
|
||||
// buffer containing instances data used to render a toolpaths using instanced models
|
||||
// instance record format: 5 floats -> position.x|position.y|position.z|width|height
|
||||
// which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced()
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
struct InstanceVBuffer
|
||||
{
|
||||
// ranges used to render only subparts of the intances
|
||||
|
@ -130,6 +137,16 @@ class GCodeViewer
|
|||
void reset();
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
enum class EFormat : unsigned char
|
||||
{
|
||||
InstancedModel,
|
||||
BatchedModel
|
||||
};
|
||||
|
||||
EFormat format;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
||||
// cpu-side buffer containing all instances data
|
||||
InstanceBuffer buffer;
|
||||
// indices of the moves for all instances
|
||||
|
@ -138,7 +155,18 @@ class GCodeViewer
|
|||
|
||||
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t instance_size_floats() const {
|
||||
switch (format)
|
||||
{
|
||||
case EFormat::InstancedModel: { return 5; }
|
||||
case EFormat::BatchedModel: { return 3; }
|
||||
default: { return 0; }
|
||||
}
|
||||
}
|
||||
#else
|
||||
size_t instance_size_floats() const { return 5; }
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); }
|
||||
|
||||
void reset();
|
||||
|
@ -280,7 +308,12 @@ class GCodeViewer
|
|||
Line,
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
Triangle,
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
InstancedModel,
|
||||
BatchedModel
|
||||
#else
|
||||
Model
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#else
|
||||
Triangle
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
@ -298,6 +331,9 @@ class GCodeViewer
|
|||
GLModel model;
|
||||
Color color;
|
||||
InstanceVBuffer instances;
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
GLModel::InitializationData data;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
@ -362,7 +398,15 @@ class GCodeViewer
|
|||
case ERenderPrimitiveType::Triangle: {
|
||||
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
case ERenderPrimitiveType::InstancedModel: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
|
||||
case ERenderPrimitiveType::BatchedModel: {
|
||||
return model.data.vertices_count() > 0 && model.data.indices_count() &&
|
||||
!vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
|
||||
}
|
||||
#else
|
||||
case ERenderPrimitiveType::Model: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
|
@ -520,6 +564,9 @@ class GCodeViewer
|
|||
int64_t gl_triangles_calls_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t gl_instanced_models_calls_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
int64_t gl_batched_models_calls_count{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
// memory
|
||||
int64_t results_size{ 0 };
|
||||
|
@ -541,6 +588,9 @@ class GCodeViewer
|
|||
int64_t extrude_segments_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
int64_t instances_count{ 0 };
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
int64_t batched_count{ 0 };
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
int64_t vbuffers_count{ 0 };
|
||||
int64_t ibuffers_count{ 0 };
|
||||
|
@ -569,6 +619,9 @@ class GCodeViewer
|
|||
gl_triangles_calls_count = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
gl_instanced_models_calls_count = 0;
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
gl_batched_models_calls_count = 0;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
|
||||
|
@ -594,6 +647,9 @@ class GCodeViewer
|
|||
extrude_segments_count = 0;
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
instances_count = 0;
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
batched_count = 0;
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
vbuffers_count = 0;
|
||||
ibuffers_count = 0;
|
||||
|
@ -639,18 +695,17 @@ public:
|
|||
std::string m_filename;
|
||||
boost::iostreams::mapped_file_source m_file;
|
||||
// map for accessing data in file by line number
|
||||
std::vector<std::pair<uint64_t, uint64_t>> m_lines_map;
|
||||
std::vector<size_t> m_lines_ends;
|
||||
// current visible lines
|
||||
std::vector<Line> m_lines;
|
||||
|
||||
public:
|
||||
GCodeWindow() = default;
|
||||
~GCodeWindow() { stop_mapping_file(); }
|
||||
void set_filename(const std::string& filename) { m_filename = filename; }
|
||||
void load_gcode();
|
||||
void load_gcode(const std::string& filename, const std::vector<size_t> &lines_ends);
|
||||
void reset() {
|
||||
stop_mapping_file();
|
||||
m_lines_map.clear();
|
||||
m_lines_ends.clear();
|
||||
m_lines.clear();
|
||||
m_filename.clear();
|
||||
}
|
||||
|
@ -777,8 +832,6 @@ public:
|
|||
|
||||
void export_toolpaths_to_obj(const char* filename) const;
|
||||
|
||||
void start_mapping_gcode_window();
|
||||
void stop_mapping_gcode_window();
|
||||
void toggle_gcode_window_visibility() { m_sequential_view.gcode_window.toggle_visibility(); }
|
||||
|
||||
#if ENABLE_FIX_IMPORTING_COLOR_PRINT_VIEW_INTO_GCODEVIEWER
|
||||
|
|
|
@ -1114,16 +1114,6 @@ int GLCanvas3D::check_volumes_outside_state() const
|
|||
return (int)state;
|
||||
}
|
||||
|
||||
void GLCanvas3D::start_mapping_gcode_window()
|
||||
{
|
||||
m_gcode_viewer.start_mapping_gcode_window();
|
||||
}
|
||||
|
||||
void GLCanvas3D::stop_mapping_gcode_window()
|
||||
{
|
||||
m_gcode_viewer.stop_mapping_gcode_window();
|
||||
}
|
||||
|
||||
void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx)
|
||||
{
|
||||
m_render_sla_auxiliaries = visible;
|
||||
|
@ -4108,13 +4098,24 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
|
|||
}
|
||||
}
|
||||
|
||||
#if !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (visible_volumes.empty())
|
||||
return;
|
||||
#endif // !ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
BoundingBoxf3 volumes_box;
|
||||
for (const GLVolume* vol : visible_volumes) {
|
||||
volumes_box.merge(vol->transformed_bounding_box());
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (!visible_volumes.empty()) {
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
for (const GLVolume* vol : visible_volumes) {
|
||||
volumes_box.merge(vol->transformed_bounding_box());
|
||||
}
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
}
|
||||
else
|
||||
// This happens for empty projects
|
||||
volumes_box = wxGetApp().plater()->get_bed().get_bounding_box(true);
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
Camera camera;
|
||||
camera.set_type(camera_type);
|
||||
|
|
|
@ -621,9 +621,6 @@ public:
|
|||
const GCodeViewer::SequentialView& get_gcode_sequential_view() const { return m_gcode_viewer.get_sequential_view(); }
|
||||
void update_gcode_sequential_view_current(unsigned int first, unsigned int last) { m_gcode_viewer.update_sequential_view_current(first, last); }
|
||||
|
||||
void start_mapping_gcode_window();
|
||||
void stop_mapping_gcode_window();
|
||||
|
||||
void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
|
||||
void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1);
|
||||
void update_instance_printable_state_for_object(size_t obj_idx);
|
||||
|
|
|
@ -19,6 +19,26 @@
|
|||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t GLModel::InitializationData::vertices_count() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
for (const Entity& entity : entities) {
|
||||
ret += entity.positions.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t GLModel::InitializationData::indices_count() const
|
||||
{
|
||||
size_t ret = 0;
|
||||
for (const Entity& entity : entities) {
|
||||
ret += entity.indices.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
|
||||
void GLModel::init_from(const InitializationData& data)
|
||||
{
|
||||
if (!m_render_data.empty()) // call reset() if you want to reuse this model
|
||||
|
|
|
@ -48,6 +48,15 @@ namespace GUI {
|
|||
};
|
||||
|
||||
std::vector<Entity> entities;
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
size_t vertices_count() const;
|
||||
size_t vertices_size_floats() const { return vertices_count() * 6; }
|
||||
size_t vertices_size_bytes() const { return vertices_size_floats() * sizeof(float); }
|
||||
|
||||
size_t indices_count() const;
|
||||
size_t indices_size_bytes() const { return indices_count() * sizeof(unsigned int); }
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
@ -33,13 +33,21 @@ std::pair<bool, std::string> GLShadersManager::init()
|
|||
|
||||
bool valid = true;
|
||||
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview
|
||||
#else
|
||||
// used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
valid &= append_shader("gouraud_light", { "gouraud_light.vs", "gouraud_light.fs" });
|
||||
// used to render printbed
|
||||
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
|
||||
// used to render options in gcode preview
|
||||
#if ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
|
||||
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
|
||||
#else
|
||||
#if ENABLE_SEAMS_USING_MODELS
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 1))
|
||||
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
|
||||
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
|
||||
else {
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
|
@ -49,6 +57,7 @@ std::pair<bool, std::string> GLShadersManager::init()
|
|||
#if ENABLE_SEAMS_USING_MODELS
|
||||
}
|
||||
#endif // ENABLE_SEAMS_USING_MODELS
|
||||
#endif // ENABLE_SEAMS_USING_BATCHED_MODELS
|
||||
// used to render extrusion and travel paths as lines in gcode preview
|
||||
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
|
||||
// used to render objects in 3d editor
|
||||
|
|
|
@ -2082,10 +2082,10 @@ std::vector<std::pair<unsigned int, std::string>> GUI_App::get_selected_presets(
|
|||
|
||||
// This is called when closing the application, when loading a config file or when starting the config wizard
|
||||
// to notify the user whether he is aware that some preset changes will be lost.
|
||||
bool GUI_App::check_and_save_current_preset_changes(const wxString& header)
|
||||
bool GUI_App::check_and_save_current_preset_changes(const wxString& header, const wxString& caption)
|
||||
{
|
||||
if (/*this->plater()->model().objects.empty() && */has_current_preset_changes()) {
|
||||
UnsavedChangesDialog dlg(header);
|
||||
UnsavedChangesDialog dlg(header, caption);
|
||||
if (wxGetApp().app_config->get("default_action_on_close_application") == "none" && dlg.ShowModal() == wxID_CANCEL)
|
||||
return false;
|
||||
|
||||
|
@ -2334,10 +2334,13 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
|
|||
{
|
||||
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
|
||||
|
||||
if (reason == ConfigWizard::RR_USER)
|
||||
if (PresetUpdater::UpdateResult result = preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD);
|
||||
result == PresetUpdater::R_ALL_CANCELED)
|
||||
if (reason == ConfigWizard::RR_USER) {
|
||||
wxString header = _L("Updates to Configuration Wizard may cause an another preset selection and lost of preset modification as a result.\n"
|
||||
"So, check unsaved changes and save them if necessary.") + "\n";
|
||||
if (!check_and_save_current_preset_changes(header, _L("ConfigWizard is opening")) ||
|
||||
preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) == PresetUpdater::R_ALL_CANCELED)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! m_wizard) {
|
||||
wxBusyCursor wait;
|
||||
|
|
|
@ -247,7 +247,7 @@ public:
|
|||
bool has_current_preset_changes() const;
|
||||
void update_saved_preset_from_current_preset();
|
||||
std::vector<std::pair<unsigned int, std::string>> get_selected_presets() const;
|
||||
bool check_and_save_current_preset_changes(const wxString& header = wxString());
|
||||
bool check_and_save_current_preset_changes(const wxString& header = wxString(), const wxString& caption = wxString());
|
||||
bool check_print_host_queue();
|
||||
bool checked_tab(Tab* tab);
|
||||
void load_current_presets(bool check_printer_presets = true);
|
||||
|
|
|
@ -1044,13 +1044,8 @@ void ObjectList::key_event(wxKeyEvent& event)
|
|||
{
|
||||
if (event.GetKeyCode() == WXK_TAB)
|
||||
Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
|
||||
else if (event.GetKeyCode() == WXK_DELETE
|
||||
#ifdef __WXOSX__
|
||||
|| event.GetKeyCode() == WXK_BACK
|
||||
#endif //__WXOSX__
|
||||
) {
|
||||
else if (event.GetKeyCode() == WXK_DELETE || event.GetKeyCode() == WXK_BACK )
|
||||
remove();
|
||||
}
|
||||
else if (event.GetKeyCode() == WXK_F5)
|
||||
wxGetApp().plater()->reload_all_from_disk();
|
||||
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
|
||||
|
@ -1702,8 +1697,7 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files
|
|||
snapshot_label += ", " + wxString::FromUTF8(paths[i].filename().string().c_str());
|
||||
|
||||
take_snapshot(snapshot_label);
|
||||
std::vector<size_t> res = wxGetApp().plater()->load_files(paths, true, false);
|
||||
if (!res.empty())
|
||||
if (! wxGetApp().plater()->load_files(paths, true, false).empty())
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
|
@ -2562,6 +2556,9 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D
|
|||
|
||||
void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray* selections/* = nullptr*/, bool added_object/* = false*/)
|
||||
{
|
||||
if (obj_idx >= m_objects->size())
|
||||
return;
|
||||
|
||||
const ModelObject* model_object = (*m_objects)[obj_idx];
|
||||
wxDataViewItem item_obj = m_objects_model->GetItemById(obj_idx);
|
||||
assert(item_obj.IsOk());
|
||||
|
@ -4028,17 +4025,12 @@ void ObjectList::simplify()
|
|||
|
||||
// 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) {
|
||||
if (! gizmos_mgr.check_gizmos_closed_except(GLGizmosManager::EType::Simplify))
|
||||
return;
|
||||
|
||||
if (gizmos_mgr.get_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);
|
||||
}
|
||||
|
|
|
@ -232,6 +232,20 @@ void GLGizmoBase::render_input_window(float x, float y, float bottom_limit)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string GLGizmoBase::get_name(bool include_shortcut) const
|
||||
{
|
||||
int key = get_shortcut_key();
|
||||
assert( key >= WXK_CONTROL_A && key <= WXK_CONTROL_Z);
|
||||
std::string out = on_get_name();
|
||||
if (include_shortcut)
|
||||
out += std::string(" [") + char(int('A') + key - int(WXK_CONTROL_A)) + "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components
|
||||
// were not interpolated by alpha blending or multi sampling.
|
||||
unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char green, unsigned char blue)
|
||||
|
|
|
@ -120,7 +120,7 @@ public:
|
|||
void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); }
|
||||
void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); }
|
||||
|
||||
std::string get_name() const { return on_get_name(); }
|
||||
std::string get_name(bool include_shortcut = true) const;
|
||||
|
||||
int get_group_id() const { return m_group_id; }
|
||||
void set_group_id(int id) { m_group_id = id; }
|
||||
|
@ -135,6 +135,7 @@ public:
|
|||
bool is_activable() const { return on_is_activable(); }
|
||||
bool is_selectable() const { return on_is_selectable(); }
|
||||
CommonGizmosDataID get_requirements() const { return on_get_requirements(); }
|
||||
virtual bool wants_enter_leave_snapshots() const { return false; }
|
||||
void set_common_data_pool(CommonGizmosDataPool* ptr) { m_c = ptr; }
|
||||
|
||||
unsigned int get_sprite_id() const { return m_sprite_id; }
|
||||
|
|
|
@ -49,7 +49,7 @@ bool GLGizmoCut::on_init()
|
|||
|
||||
std::string GLGizmoCut::on_get_name() const
|
||||
{
|
||||
return (_L("Cut") + " [C]").ToUTF8().data();
|
||||
return _u8L("Cut");
|
||||
}
|
||||
|
||||
void GLGizmoCut::on_set_state()
|
||||
|
|
|
@ -28,7 +28,7 @@ void GLGizmoFdmSupports::on_shutdown()
|
|||
|
||||
std::string GLGizmoFdmSupports::on_get_name() const
|
||||
{
|
||||
return (_L("Paint-on supports") + " [L]").ToUTF8().data();
|
||||
return _u8L("Paint-on supports");
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,7 +85,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
|
|||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
|
|
|
@ -37,7 +37,7 @@ CommonGizmosDataID GLGizmoFlatten::on_get_requirements() const
|
|||
|
||||
std::string GLGizmoFlatten::on_get_name() const
|
||||
{
|
||||
return (_L("Place on face") + " [F]").ToUTF8().data();
|
||||
return _u8L("Place on face");
|
||||
}
|
||||
|
||||
bool GLGizmoFlatten::on_is_activable() const
|
||||
|
|
|
@ -505,7 +505,7 @@ RENDER_AGAIN:
|
|||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float settings_sliders_left =
|
||||
|
@ -773,7 +773,7 @@ bool GLGizmoHollow::on_is_selectable() const
|
|||
|
||||
std::string GLGizmoHollow::on_get_name() const
|
||||
{
|
||||
return (_(L("Hollow and drill")) + " [H]").ToUTF8().data();
|
||||
return _u8L("Hollow and drill");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ void GLGizmoMmuSegmentation::on_shutdown()
|
|||
std::string GLGizmoMmuSegmentation::on_get_name() const
|
||||
{
|
||||
// FIXME Lukas H.: Discuss and change shortcut
|
||||
return (_L("Multimaterial painting") + " [N]").ToUTF8().data();
|
||||
return _u8L("Multimaterial painting");
|
||||
}
|
||||
|
||||
bool GLGizmoMmuSegmentation::on_is_selectable() const
|
||||
|
@ -124,13 +124,12 @@ bool GLGizmoMmuSegmentation::on_init()
|
|||
m_desc["sphere"] = _L("Sphere");
|
||||
m_desc["pointer"] = _L("Pointer");
|
||||
|
||||
m_desc["tool_type"] = _L("Tool type");
|
||||
m_desc["tool_type"] = _L("Tool type");
|
||||
m_desc["tool_brush"] = _L("Brush");
|
||||
m_desc["tool_seed_fill"] = _L("Seed fill");
|
||||
m_desc["tool_smart_fill"] = _L("Smart fill");
|
||||
m_desc["tool_bucket_fill"] = _L("Bucket fill");
|
||||
|
||||
m_desc["seed_fill"] = _L("Seed fill");
|
||||
m_desc["seed_fill_angle"] = _L("Seed fill angle");
|
||||
m_desc["smart_fill_angle"] = _L("Smart fill angle");
|
||||
|
||||
init_extruders_data();
|
||||
|
||||
|
@ -240,16 +239,16 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f);
|
||||
const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.f);
|
||||
const float seed_fill_slider_left = m_imgui->calc_text_size(m_desc.at("seed_fill_angle")).x + m_imgui->scaled(1.f);
|
||||
const float smart_fill_slider_left = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.f);
|
||||
|
||||
const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
|
||||
const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
|
||||
const float cursor_type_radio_circle = m_imgui->calc_text_size(m_desc["circle"]).x + m_imgui->scaled(2.5f);
|
||||
const float cursor_type_radio_sphere = m_imgui->calc_text_size(m_desc["sphere"]).x + m_imgui->scaled(2.5f);
|
||||
const float cursor_type_radio_pointer = m_imgui->calc_text_size(m_desc["pointer"]).x + m_imgui->scaled(2.5f);
|
||||
|
||||
const float button_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.f);
|
||||
|
@ -259,9 +258,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
const float combo_label_width = std::max(m_imgui->calc_text_size(m_desc.at("first_color")).x,
|
||||
m_imgui->calc_text_size(m_desc.at("second_color")).x) + m_imgui->scaled(1.f);
|
||||
|
||||
const float tool_type_radio_brush = m_imgui->calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f);
|
||||
const float tool_type_radio_brush = m_imgui->calc_text_size(m_desc["tool_brush"]).x + m_imgui->scaled(2.5f);
|
||||
const float tool_type_radio_bucket_fill = m_imgui->calc_text_size(m_desc["tool_bucket_fill"]).x + m_imgui->scaled(2.5f);
|
||||
const float tool_type_radio_seed_fill = m_imgui->calc_text_size(m_desc["tool_seed_fill"]).x + m_imgui->scaled(2.5f);
|
||||
const float tool_type_radio_smart_fill = m_imgui->calc_text_size(m_desc["tool_smart_fill"]).x + m_imgui->scaled(2.5f);
|
||||
|
||||
float caption_max = 0.f;
|
||||
float total_text_max = 0.;
|
||||
|
@ -272,12 +271,12 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
caption_max += m_imgui->scaled(1.f);
|
||||
total_text_max += m_imgui->scaled(1.f);
|
||||
|
||||
float sliders_width = std::max(seed_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
|
||||
float sliders_width = std::max(smart_fill_slider_left, std::max(cursor_slider_left, clipping_slider_left));
|
||||
float window_width = minimal_slider_width + sliders_width;
|
||||
window_width = std::max(window_width, total_text_max);
|
||||
window_width = std::max(window_width, button_width);
|
||||
window_width = std::max(window_width, cursor_type_radio_circle + cursor_type_radio_sphere + cursor_type_radio_pointer);
|
||||
window_width = std::max(window_width, tool_type_radio_brush + tool_type_radio_bucket_fill + tool_type_radio_seed_fill);
|
||||
window_width = std::max(window_width, tool_type_radio_brush + tool_type_radio_bucket_fill + tool_type_radio_smart_fill);
|
||||
window_width = std::max(window_width, 2.f * buttons_width + m_imgui->scaled(1.f));
|
||||
|
||||
auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) {
|
||||
|
@ -321,7 +320,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
ImGui::AlignTextToFramePadding();
|
||||
m_imgui->text(m_desc.at("tool_type"));
|
||||
|
||||
float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_seed_fill + m_imgui->scaled(2.f)) / 2.f;
|
||||
float tool_type_offset = (window_width - tool_type_radio_brush - tool_type_radio_bucket_fill - tool_type_radio_smart_fill + m_imgui->scaled(2.f)) / 2.f;
|
||||
|
||||
ImGui::NewLine();
|
||||
|
||||
|
@ -345,9 +344,9 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
}
|
||||
|
||||
ImGui::SameLine(tool_type_offset + tool_type_radio_brush + m_imgui->scaled(0.f));
|
||||
ImGui::PushItemWidth(tool_type_radio_seed_fill);
|
||||
if (m_imgui->radio_button(m_desc["tool_seed_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::SEED_FILL)) {
|
||||
m_tool_type = GLGizmoMmuSegmentation::ToolType::SEED_FILL;
|
||||
ImGui::PushItemWidth(tool_type_radio_smart_fill);
|
||||
if (m_imgui->radio_button(m_desc["tool_smart_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::SMART_FILL)) {
|
||||
m_tool_type = GLGizmoMmuSegmentation::ToolType::SMART_FILL;
|
||||
for (auto &triangle_selector : m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
|
@ -362,7 +361,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_seed_fill + m_imgui->scaled(0.f));
|
||||
ImGui::SameLine(tool_type_offset + tool_type_radio_brush + tool_type_radio_smart_fill + m_imgui->scaled(0.f));
|
||||
ImGui::PushItemWidth(tool_type_radio_bucket_fill);
|
||||
if (m_imgui->radio_button(m_desc["tool_bucket_fill"], m_tool_type == GLGizmoMmuSegmentation::ToolType::BUCKET_FILL)) {
|
||||
m_tool_type = GLGizmoMmuSegmentation::ToolType::BUCKET_FILL;
|
||||
|
@ -458,14 +457,14 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
|
|||
m_imgui->disabled_end();
|
||||
|
||||
ImGui::Separator();
|
||||
} else if(m_tool_type == ToolType::SEED_FILL) {
|
||||
m_imgui->text(m_desc["seed_fill_angle"] + ":");
|
||||
} else if(m_tool_type == ToolType::SMART_FILL) {
|
||||
m_imgui->text(m_desc["smart_fill_angle"] + ":");
|
||||
ImGui::AlignTextToFramePadding();
|
||||
std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Degree sign to use in the respective slider in MMU gizmo,"
|
||||
"placed after the number with no whitespace in between.");
|
||||
ImGui::SameLine(sliders_width);
|
||||
ImGui::PushItemWidth(window_width - sliders_width);
|
||||
if(m_imgui->slider_float("##seed_fill_angle", &m_seed_fill_angle, SeedFillAngleMin, SeedFillAngleMax, format_str.data()))
|
||||
if(m_imgui->slider_float("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data()))
|
||||
for (auto &triangle_selector : m_triangle_selectors) {
|
||||
triangle_selector->seed_fill_unselect_all_triangles();
|
||||
triangle_selector->request_update_render_data();
|
||||
|
|
|
@ -52,7 +52,7 @@ bool GLGizmoMove3D::on_init()
|
|||
|
||||
std::string GLGizmoMove3D::on_get_name() const
|
||||
{
|
||||
return (_L("Move") + " [M]").ToUTF8().data();
|
||||
return _u8L("Move");
|
||||
}
|
||||
|
||||
bool GLGizmoMove3D::on_is_activable() const
|
||||
|
|
|
@ -282,12 +282,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
: std::min(m_cursor_radius + CursorRadiusStep, CursorRadiusMax);
|
||||
m_parent.set_as_dirty();
|
||||
return true;
|
||||
} else if (m_tool_type == ToolType::SEED_FILL) {
|
||||
m_seed_fill_angle = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_seed_fill_angle - SeedFillAngleStep, SeedFillAngleMin)
|
||||
: std::min(m_seed_fill_angle + SeedFillAngleStep, SeedFillAngleMax);
|
||||
} else if (m_tool_type == ToolType::SMART_FILL) {
|
||||
m_smart_fill_angle = action == SLAGizmoEventType::MouseWheelDown ? std::max(m_smart_fill_angle - SmartFillAngleStep, SmartFillAngleMin)
|
||||
: std::min(m_smart_fill_angle + SmartFillAngleStep, SmartFillAngleMax);
|
||||
m_parent.set_as_dirty();
|
||||
if (m_rr.mesh_id != -1) {
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_smart_fill_angle, true);
|
||||
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
|
||||
m_seed_fill_last_mesh_id = m_rr.mesh_id;
|
||||
}
|
||||
|
@ -379,10 +379,10 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
Vec3f camera_pos = (trafo_matrix.inverse() * camera.get_position()).cast<float>();
|
||||
|
||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||
if (m_tool_type == ToolType::SEED_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
||||
if (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)) {
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_apply_on_triangles(new_state);
|
||||
if (m_tool_type == ToolType::SEED_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle, true);
|
||||
if (m_tool_type == ToolType::SMART_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_smart_fill_angle, true);
|
||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false, true);
|
||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||
|
@ -400,7 +400,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
return true;
|
||||
}
|
||||
|
||||
if (action == SLAGizmoEventType::Moving && (m_tool_type == ToolType::SEED_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER))) {
|
||||
if (action == SLAGizmoEventType::Moving && (m_tool_type == ToolType::SMART_FILL || m_tool_type == ToolType::BUCKET_FILL || (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER))) {
|
||||
if (m_triangle_selectors.empty())
|
||||
return false;
|
||||
|
||||
|
@ -440,8 +440,8 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
seed_fill_unselect_all();
|
||||
|
||||
assert(m_rr.mesh_id < int(m_triangle_selectors.size()));
|
||||
if (m_tool_type == ToolType::SEED_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_seed_fill_angle);
|
||||
if (m_tool_type == ToolType::SMART_FILL)
|
||||
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), m_smart_fill_angle);
|
||||
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
|
||||
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), false);
|
||||
else if (m_tool_type == ToolType::BUCKET_FILL)
|
||||
|
|
|
@ -109,16 +109,16 @@ protected:
|
|||
enum class ToolType {
|
||||
BRUSH,
|
||||
BUCKET_FILL,
|
||||
SEED_FILL
|
||||
SMART_FILL
|
||||
};
|
||||
|
||||
bool m_triangle_splitting_enabled = true;
|
||||
ToolType m_tool_type = ToolType::BRUSH;
|
||||
float m_seed_fill_angle = 30.f;
|
||||
float m_smart_fill_angle = 30.f;
|
||||
|
||||
static constexpr float SeedFillAngleMin = 0.0f;
|
||||
static constexpr float SeedFillAngleMax = 90.f;
|
||||
static constexpr float SeedFillAngleStep = 1.f;
|
||||
static constexpr float SmartFillAngleMin = 0.0f;
|
||||
static constexpr float SmartFillAngleMax = 90.f;
|
||||
static constexpr float SmartFillAngleStep = 1.f;
|
||||
|
||||
// It stores the value of the previous mesh_id to which the seed fill was applied.
|
||||
// It is used to detect when the mouse has moved from one volume to another one.
|
||||
|
|
|
@ -463,7 +463,7 @@ bool GLGizmoRotate3D::on_init()
|
|||
|
||||
std::string GLGizmoRotate3D::on_get_name() const
|
||||
{
|
||||
return (_L("Rotate") + " [R]").ToUTF8().data();
|
||||
return _u8L("Rotate");
|
||||
}
|
||||
|
||||
bool GLGizmoRotate3D::on_is_activable() const
|
||||
|
|
|
@ -76,7 +76,7 @@ bool GLGizmoScale3D::on_init()
|
|||
|
||||
std::string GLGizmoScale3D::on_get_name() const
|
||||
{
|
||||
return (_L("Scale") + " [S]").ToUTF8().data();
|
||||
return _u8L("Scale");
|
||||
}
|
||||
|
||||
bool GLGizmoScale3D::on_is_activable() const
|
||||
|
|
|
@ -48,7 +48,7 @@ bool GLGizmoSeam::on_init()
|
|||
|
||||
std::string GLGizmoSeam::on_get_name() const
|
||||
{
|
||||
return (_L("Seam painting") + " [P]").ToUTF8().data();
|
||||
return _u8L("Seam painting");
|
||||
}
|
||||
|
||||
|
||||
|
@ -79,7 +79,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
|
|||
const float approx_height = m_imgui->scaled(14.0f);
|
||||
y = std::min(y, bottom_limit - approx_height);
|
||||
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
|
||||
const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x,
|
||||
|
|
|
@ -44,7 +44,7 @@ bool GLGizmoSimplify::on_init()
|
|||
|
||||
std::string GLGizmoSimplify::on_get_name() const
|
||||
{
|
||||
return (_L("Simplify")).ToUTF8().data();
|
||||
return _u8L("Simplify");
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::on_render() {}
|
||||
|
@ -55,6 +55,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
create_gui_cfg();
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
int object_idx = selection.get_object_idx();
|
||||
if (!is_selected_object(&object_idx)) return;
|
||||
ModelObject *obj = wxGetApp().plater()->model().objects[object_idx];
|
||||
ModelVolume *act_volume = obj->volumes.front();
|
||||
|
||||
|
@ -96,7 +97,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
|
||||
int flag = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse;
|
||||
m_imgui->begin(on_get_name(), flag);
|
||||
m_imgui->begin(get_name(), flag);
|
||||
|
||||
size_t triangle_count = m_volume->mesh().its.indices.size();
|
||||
// already reduced mesh
|
||||
|
@ -329,9 +330,9 @@ 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) {
|
||||
// object is not selected when it is deleted(cancel and close gizmo)
|
||||
if (m_state != State::settings && is_selected_object()) {
|
||||
GLGizmoBase::m_state = GLGizmoBase::On;
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->push_notification(
|
||||
|
@ -383,4 +384,20 @@ void GLGizmoSimplify::request_rerender() {
|
|||
});
|
||||
}
|
||||
|
||||
bool GLGizmoSimplify::is_selected_object(int *object_idx)
|
||||
{
|
||||
int index = (object_idx != nullptr) ? *object_idx :
|
||||
m_parent.get_selection().get_object_idx();
|
||||
// no selected object --> can appear after delete model
|
||||
if (index < 0) {
|
||||
switch (m_state) {
|
||||
case State::settings: close(); break;
|
||||
case State::canceling: break;
|
||||
default: m_state = State::canceling;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
|
|
|
@ -38,6 +38,7 @@ private:
|
|||
void set_its(indexed_triangle_set &its);
|
||||
void create_gui_cfg();
|
||||
void request_rerender();
|
||||
bool is_selected_object(int *object_idx = nullptr);
|
||||
|
||||
std::atomic_bool m_is_valid_result; // differ what to do in apply
|
||||
std::atomic_bool m_exist_preview; // set when process end
|
||||
|
|
|
@ -625,7 +625,7 @@ RENDER_AGAIN:
|
|||
//ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
|
||||
//ImGui::SetNextWindowSize(ImVec2(window_size));
|
||||
|
||||
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
m_imgui->begin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
|
||||
|
||||
// adjust window position to avoid overlap the view toolbar
|
||||
float win_h = ImGui::GetWindowHeight();
|
||||
|
@ -863,7 +863,7 @@ bool GLGizmoSlaSupports::on_is_selectable() const
|
|||
|
||||
std::string GLGizmoSlaSupports::on_get_name() const
|
||||
{
|
||||
return (_L("SLA Support Points") + " [L]").ToUTF8().data();
|
||||
return _u8L("SLA Support Points");
|
||||
}
|
||||
|
||||
CommonGizmosDataID GLGizmoSlaSupports::on_get_requirements() const
|
||||
|
@ -901,15 +901,6 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
return;
|
||||
|
||||
if (m_state == On && m_old_state != On) { // the gizmo was just turned on
|
||||
if (! m_parent.get_gizmos_manager().is_serializing()) {
|
||||
// Only take the snapshot when the USER opens the gizmo. Common gizmos
|
||||
// data are not yet available, the CallAfter will postpone taking the
|
||||
// snapshot until they are. No, it does not feel right.
|
||||
wxGetApp().CallAfter([]() {
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Entering SLA gizmo"));
|
||||
});
|
||||
}
|
||||
|
||||
// Set default head diameter from config.
|
||||
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config;
|
||||
m_new_point_head_diameter = static_cast<const ConfigOptionFloat*>(cfg.option("support_head_front_diameter"))->value;
|
||||
|
@ -925,8 +916,6 @@ void GLGizmoSlaSupports::on_set_state()
|
|||
else {
|
||||
// we are actually shutting down
|
||||
disable_editing_mode(); // so it is not active next time the gizmo opens
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Leaving SLA gizmo"));
|
||||
m_normal_cache.clear();
|
||||
m_old_mo_id = -1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ public:
|
|||
bool has_backend_supports() const;
|
||||
void reslice_SLA_supports(bool postpone_error_messages = false) const;
|
||||
|
||||
bool wants_enter_leave_snapshots() const override { return true; }
|
||||
|
||||
private:
|
||||
bool on_init() override;
|
||||
void on_update(const UpdateData& data) override;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp"
|
||||
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
|
@ -163,10 +164,8 @@ void GLGizmosManager::refresh_on_off_state()
|
|||
return;
|
||||
|
||||
if (m_current != Undefined
|
||||
&& ! m_gizmos[m_current]->is_activable()) {
|
||||
activate_gizmo(Undefined);
|
||||
&& ! m_gizmos[m_current]->is_activable() && activate_gizmo(Undefined))
|
||||
update_data();
|
||||
}
|
||||
}
|
||||
|
||||
void GLGizmosManager::reset_all_states()
|
||||
|
@ -181,14 +180,28 @@ void GLGizmosManager::reset_all_states()
|
|||
bool GLGizmosManager::open_gizmo(EType type)
|
||||
{
|
||||
int idx = int(type);
|
||||
if (m_gizmos[idx]->is_activable()) {
|
||||
activate_gizmo(m_current == idx ? Undefined : (EType)idx);
|
||||
if (m_gizmos[idx]->is_activable()
|
||||
&& activate_gizmo(m_current == idx ? Undefined : (EType)idx)) {
|
||||
update_data();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool GLGizmosManager::check_gizmos_closed_except(EType type) const
|
||||
{
|
||||
if (get_current_type() != type && get_current_type() != Undefined) {
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
_u8L("ERROR: Please close all manipulators available from "
|
||||
"the left toolbar first"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLGizmosManager::set_hover_id(int id)
|
||||
{
|
||||
if (!m_enabled || m_current == Undefined)
|
||||
|
@ -1193,31 +1206,35 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos)
|
|||
return name;
|
||||
}
|
||||
|
||||
void GLGizmosManager::activate_gizmo(EType type)
|
||||
bool GLGizmosManager::activate_gizmo(EType type)
|
||||
{
|
||||
if (m_gizmos.empty() || m_current == type)
|
||||
return;
|
||||
return true;
|
||||
|
||||
if (m_current != Undefined) {
|
||||
m_gizmos[m_current]->set_state(GLGizmoBase::Off);
|
||||
if (m_gizmos[m_current]->get_state() != GLGizmoBase::Off)
|
||||
return; // gizmo refused to be turned off, do nothing.
|
||||
GLGizmoBase* old_gizmo = m_current == Undefined ? nullptr : m_gizmos[m_current].get();
|
||||
GLGizmoBase* new_gizmo = type == Undefined ? nullptr : m_gizmos[type].get();
|
||||
|
||||
if (old_gizmo) {
|
||||
old_gizmo->set_state(GLGizmoBase::Off);
|
||||
if (old_gizmo->get_state() != GLGizmoBase::Off)
|
||||
return false; // gizmo refused to be turned off, do nothing.
|
||||
|
||||
if (! m_parent.get_gizmos_manager().is_serializing()
|
||||
&& old_gizmo->wants_enter_leave_snapshots())
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
|
||||
Slic3r::format(_utf8("Leaving %1%"), old_gizmo->get_name(false)));
|
||||
}
|
||||
|
||||
if (new_gizmo && ! m_parent.get_gizmos_manager().is_serializing()
|
||||
&& new_gizmo->wants_enter_leave_snapshots())
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(),
|
||||
Slic3r::format(_utf8("Entering %1%"), new_gizmo->get_name(false)));
|
||||
|
||||
m_current = type;
|
||||
|
||||
// Updating common data should be left to the update_data function, which
|
||||
// is always called after this one. activate_gizmo can be called by undo/redo,
|
||||
// when selection is not yet deserialized, so the common data would update
|
||||
// incorrectly (or crash if relying on unempty selection). Undo/redo stack
|
||||
// will also call update_data, after selection is restored.
|
||||
|
||||
//m_common_gizmos_data->update(get_current()
|
||||
// ? get_current()->get_requirements()
|
||||
// : CommonGizmosDataID(0));
|
||||
|
||||
if (type != Undefined)
|
||||
m_gizmos[type]->set_state(GLGizmoBase::On);
|
||||
if (new_gizmo)
|
||||
new_gizmo->set_state(GLGizmoBase::On);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ private:
|
|||
std::vector<size_t> get_selectable_idxs() const;
|
||||
size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const;
|
||||
|
||||
void activate_gizmo(EType type);
|
||||
bool activate_gizmo(EType type);
|
||||
|
||||
struct MouseCapture
|
||||
{
|
||||
|
@ -176,6 +176,7 @@ public:
|
|||
void reset_all_states();
|
||||
bool is_serializing() const { return m_serializing; }
|
||||
bool open_gizmo(EType type);
|
||||
bool check_gizmos_closed_except(EType) const;
|
||||
|
||||
void set_hover_id(int id);
|
||||
void enable_grabber(EType type, unsigned int id, bool enable);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "I18N.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
|
@ -57,10 +58,6 @@ inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, f
|
|||
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);
|
||||
|
@ -77,7 +74,12 @@ void write_used_binary(const std::vector<std::string>& ids)
|
|||
}
|
||||
void read_used_binary(std::vector<std::string>& ids)
|
||||
{
|
||||
boost::filesystem::ifstream file((boost::filesystem::path(data_dir()) / "cache" / "hints.cereal"));
|
||||
boost::filesystem::path path(boost::filesystem::path(data_dir()) / "cache" / "hints.cereal");
|
||||
if (!boost::filesystem::exists(path)) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << path.string();
|
||||
return;
|
||||
}
|
||||
boost::filesystem::ifstream file(path);
|
||||
cereal::BinaryInputArchive archive(file);
|
||||
HintsCerealData cd;
|
||||
try
|
||||
|
@ -245,6 +247,13 @@ HintDatabase::~HintDatabase()
|
|||
write_used_binary(m_used_ids);
|
||||
}
|
||||
}
|
||||
void HintDatabase::uninit()
|
||||
{
|
||||
if (m_initialized) {
|
||||
write_used_binary(m_used_ids);
|
||||
}
|
||||
m_initialized = false;
|
||||
}
|
||||
void HintDatabase::init()
|
||||
{
|
||||
load_hints_from_file(std::move(boost::filesystem::path(resources_dir()) / "data" / "hints.ini"));
|
||||
|
@ -379,7 +388,13 @@ void HintDatabase::load_hints_from_file(const boost::filesystem::path& path)
|
|||
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(); } };
|
||||
wxGetApp().obj_list()->load_shape_object_from_gallery(); }
|
||||
};
|
||||
m_loaded_hints.emplace_back(hint_data);
|
||||
} else if (dict["hypertext_type"] == "menubar") {
|
||||
wxString menu(_L("&" + dict["hypertext_menubar_menu_name"]));
|
||||
wxString item(_L(dict["hypertext_menubar_item_name"]));
|
||||
HintData hint_data{ id_string, text1, weight, was_displayed, hypertext_text, follow_text, disabled_tags, enabled_tags, true, documentation_link, [menu, item]() { wxGetApp().mainframe->open_menubar_item(menu, item); } };
|
||||
m_loaded_hints.emplace_back(hint_data);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -47,6 +47,9 @@ public:
|
|||
return 0;
|
||||
return m_loaded_hints.size();
|
||||
}
|
||||
// resets m_initiailized to false and writes used if was initialized
|
||||
// used when reloading in runtime - like change language
|
||||
void uninit();
|
||||
private:
|
||||
void init();
|
||||
void load_hints_from_file(const boost::filesystem::path& path);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <wx/debug.h>
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
|
@ -141,11 +142,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
default:
|
||||
case GUI_App::EAppMode::Editor:
|
||||
m_taskbar_icon = std::make_unique<PrusaSlicerTaskBarIcon>(wxTBI_DOCK);
|
||||
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer");
|
||||
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-mac_128px.png"), wxBITMAP_TYPE_PNG), "PrusaSlicer");
|
||||
break;
|
||||
case GUI_App::EAppMode::GCodeViewer:
|
||||
m_taskbar_icon = std::make_unique<GCodeViewerTaskBarIcon>(wxTBI_DOCK);
|
||||
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer_128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer");
|
||||
m_taskbar_icon->SetIcon(wxIcon(Slic3r::var("PrusaSlicer-gcodeviewer-mac_128px.png"), wxBITMAP_TYPE_PNG), "G-code Viewer");
|
||||
break;
|
||||
}
|
||||
#endif // __APPLE__
|
||||
|
@ -824,15 +825,26 @@ bool MainFrame::can_start_new_project() const
|
|||
|
||||
bool MainFrame::can_save() const
|
||||
{
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
return (m_plater != nullptr) &&
|
||||
!m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(false) &&
|
||||
!m_plater->get_project_filename().empty() && m_plater->is_project_dirty();
|
||||
#else
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty() &&
|
||||
!m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(false) &&
|
||||
!m_plater->get_project_filename().empty() && m_plater->is_project_dirty();
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
}
|
||||
|
||||
bool MainFrame::can_save_as() const
|
||||
{
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
return (m_plater != nullptr) &&
|
||||
!m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(false);
|
||||
#else
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty() &&
|
||||
!m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(false);
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
}
|
||||
|
||||
void MainFrame::save_project()
|
||||
|
@ -1227,9 +1239,10 @@ void MainFrame::init_menubar_as_editor()
|
|||
append_menu_item(export_menu, wxID_ANY, _L("Export plate as STL &including supports") + dots, _L("Export current plate as STL including supports"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, "export_plater", nullptr,
|
||||
[this](){return can_export_supports(); }, this);
|
||||
append_menu_item(export_menu, wxID_ANY, _L("Export plate as &AMF") + dots, _L("Export current plate as AMF"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater", nullptr,
|
||||
[this](){return can_export_model(); }, this);
|
||||
// Deprecating AMF export. Let's wait for user feedback.
|
||||
// append_menu_item(export_menu, wxID_ANY, _L("Export plate as &AMF") + dots, _L("Export current plate as AMF"),
|
||||
// [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater", nullptr,
|
||||
// [this](){return can_export_model(); }, this);
|
||||
export_menu->AppendSeparator();
|
||||
append_menu_item(export_menu, wxID_ANY, _L("Export &toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"),
|
||||
[this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
|
||||
|
@ -1468,6 +1481,33 @@ void MainFrame::init_menubar_as_editor()
|
|||
update_menubar();
|
||||
}
|
||||
|
||||
void MainFrame::open_menubar_item(const wxString& menu_name,const wxString& item_name)
|
||||
{
|
||||
if (m_menubar == nullptr)
|
||||
return;
|
||||
// Get menu object from menubar
|
||||
int menu_index = m_menubar->FindMenu(menu_name);
|
||||
wxMenu* menu = m_menubar->GetMenu(menu_index);
|
||||
if (menu == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Mainframe open_menubar_item function couldn't find menu: " << menu_name;
|
||||
return;
|
||||
}
|
||||
// Get item id from menu
|
||||
int item_id = menu->FindItem(item_name);
|
||||
if (item_id == wxNOT_FOUND)
|
||||
{
|
||||
// try adding three dots char
|
||||
item_id = menu->FindItem(item_name + dots);
|
||||
}
|
||||
if (item_id == wxNOT_FOUND)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Mainframe open_menubar_item function couldn't find item: " << item_name;
|
||||
return;
|
||||
}
|
||||
// wxEVT_MENU will trigger item
|
||||
wxPostEvent((wxEvtHandler*)menu, wxCommandEvent(wxEVT_MENU, item_id));
|
||||
}
|
||||
|
||||
void MainFrame::init_menubar_as_gcodeviewer()
|
||||
{
|
||||
wxMenu* fileMenu = new wxMenu;
|
||||
|
|
|
@ -158,6 +158,8 @@ public:
|
|||
void init_menubar_as_editor();
|
||||
void init_menubar_as_gcodeviewer();
|
||||
void update_menubar();
|
||||
// Open item in menu by menu and item name (in actual language)
|
||||
void open_menubar_item(const wxString& menu_name,const wxString& item_name);
|
||||
#ifdef _WIN32
|
||||
void show_tabs_menu(bool show);
|
||||
#endif
|
||||
|
|
|
@ -1107,6 +1107,10 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
|
|||
m_evt_handler(evt_handler)
|
||||
{
|
||||
}
|
||||
NotificationManager::~NotificationManager()
|
||||
{
|
||||
HintDatabase::get_instance().uninit();
|
||||
}
|
||||
void NotificationManager::push_notification(const NotificationType type, int timestamp)
|
||||
{
|
||||
auto it = std::find_if(std::begin(basic_notifications), std::end(basic_notifications),
|
||||
|
|
|
@ -121,6 +121,7 @@ public:
|
|||
};
|
||||
|
||||
NotificationManager(wxEvtHandler* evt_handler);
|
||||
~NotificationManager();
|
||||
|
||||
// Push a prefabricated notification from basic_notifications (see the table at the end of this file).
|
||||
void push_notification(const NotificationType type, int timestamp = 0);
|
||||
|
|
|
@ -1361,7 +1361,8 @@ wxDataViewItem ObjectDataViewModel::ReorganizeChildren( const int current_volume
|
|||
if (!node_parent) // happens if item.IsOk()==false
|
||||
return ret_item;
|
||||
|
||||
const size_t shift = node_parent->GetChildren().Item(0)->m_type == itSettings ? 1 : 0;
|
||||
size_t shift;
|
||||
for (shift = 0; shift < node_parent->GetChildCount() && node_parent->GetNthChild(shift)->GetType() != itVolume; shift ++);
|
||||
|
||||
ObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift);
|
||||
node_parent->GetChildren().Remove(deleted_node);
|
||||
|
|
|
@ -1837,7 +1837,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
|||
, main_frame(main_frame)
|
||||
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||
"brim_width", "brim_offset", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
|
||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
|
||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
|
@ -2076,6 +2076,8 @@ Plater::priv::~priv()
|
|||
{
|
||||
if (config != nullptr)
|
||||
delete config;
|
||||
if (notification_manager != nullptr)
|
||||
delete notification_manager;
|
||||
}
|
||||
|
||||
void Plater::priv::update(unsigned int flags)
|
||||
|
@ -2224,7 +2226,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
std::vector<size_t> obj_idxs;
|
||||
|
||||
for (size_t i = 0; i < input_files.size(); ++i) {
|
||||
#ifdef _WIN32
|
||||
auto path = input_files[i];
|
||||
// On Windows, we swap slashes to back slashes, see GH #6803 as read_from_file() does not understand slashes on Windows thus it assignes full path to names of loaded objects.
|
||||
path.make_preferred();
|
||||
#else // _WIN32
|
||||
// Don't make a copy on Posix. Slash is a path separator, back slashes are not accepted as a substitute.
|
||||
const auto &path = input_files[i];
|
||||
#endif // _WIN32
|
||||
const auto filename = path.filename();
|
||||
dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
|
||||
dlg.Fit();
|
||||
|
@ -2339,9 +2348,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
else {
|
||||
model = Slic3r::Model::read_from_file(path.string(), nullptr, nullptr, only_if(load_config, Model::LoadAttribute::CheckVersion));
|
||||
for (auto obj : model.objects)
|
||||
if (obj->name.empty() ||
|
||||
obj->name.find_first_of("/") != std::string::npos) // When file is imported from Fusion360 the path containes "/" instead of "\\" (see https://github.com/prusa3d/PrusaSlicer/issues/6803)
|
||||
// But read_from_file doesn't support that direction separator and as a result object name containes full path
|
||||
if (obj->name.empty())
|
||||
obj->name = fs::path(obj->input_file).filename().string();
|
||||
}
|
||||
} catch (const ConfigurationError &e) {
|
||||
|
@ -2460,7 +2467,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
}
|
||||
|
||||
if (load_model) {
|
||||
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().string());
|
||||
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().make_preferred().string());
|
||||
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
|
||||
statusbar()->set_status_text(_L("Loaded"));
|
||||
}
|
||||
|
@ -3252,6 +3259,9 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const
|
|||
|
||||
void Plater::priv::replace_with_stl()
|
||||
{
|
||||
if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::EType::Undefined))
|
||||
return;
|
||||
|
||||
const Selection& selection = get_selection();
|
||||
|
||||
if (selection.is_wipe_tower() || get_selection().get_volume_idxs().size() != 1)
|
||||
|
@ -3631,14 +3641,8 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* =
|
|||
|
||||
// Do not fix anything when a gizmo is open. There might be issues with updates
|
||||
// and what is worse, the snapshot time would refer to the internal stack.
|
||||
if (q->canvas3D()->get_gizmos_manager().get_current_type() != GLGizmosManager::Undefined) {
|
||||
notification_manager->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
_u8L("ERROR: Please close all manipulators available from "
|
||||
"the left toolbar before fixing the mesh."));
|
||||
if (! q->canvas3D()->get_gizmos_manager().check_gizmos_closed_except(GLGizmosManager::Undefined))
|
||||
return;
|
||||
}
|
||||
|
||||
// size_t snapshot_time = undo_redo_stack().active_snapshot_time();
|
||||
Plater::TakeSnapshot snapshot(q, _L("Fix through NetFabb"));
|
||||
|
@ -4862,7 +4866,7 @@ SLAPrint& Plater::sla_print() { return p->sla_print; }
|
|||
|
||||
void Plater::new_project()
|
||||
{
|
||||
if (!p->save_project_if_dirty())
|
||||
if (p->save_project_if_dirty() == wxID_CANCEL)
|
||||
return;
|
||||
|
||||
p->select_view_3D("3D");
|
||||
|
@ -4875,7 +4879,7 @@ void Plater::new_project()
|
|||
|
||||
void Plater::load_project()
|
||||
{
|
||||
if (!p->save_project_if_dirty())
|
||||
if (p->save_project_if_dirty() == wxID_CANCEL)
|
||||
return;
|
||||
|
||||
// Ask user for a project file name.
|
||||
|
@ -4895,10 +4899,7 @@ void Plater::load_project(const wxString& filename)
|
|||
|
||||
p->reset();
|
||||
|
||||
std::vector<fs::path> input_paths;
|
||||
input_paths.push_back(into_path(filename));
|
||||
|
||||
if (! load_files(input_paths).empty()) {
|
||||
if (! load_files({ into_path(filename) }).empty()) {
|
||||
// At least one file was loaded.
|
||||
p->set_project_filename(filename);
|
||||
reset_project_dirty_initial_presets();
|
||||
|
@ -4915,7 +4916,7 @@ void Plater::add_model(bool imperial_units/* = false*/)
|
|||
|
||||
std::vector<fs::path> paths;
|
||||
for (const auto &file : input_files)
|
||||
paths.push_back(into_path(file));
|
||||
paths.emplace_back(into_path(file));
|
||||
|
||||
wxString snapshot_label;
|
||||
assert(! paths.empty());
|
||||
|
@ -4948,12 +4949,8 @@ void Plater::extract_config_from_project()
|
|||
wxString input_file;
|
||||
wxGetApp().load_project(this, input_file);
|
||||
|
||||
if (input_file.empty())
|
||||
return;
|
||||
|
||||
std::vector<fs::path> input_paths;
|
||||
input_paths.push_back(into_path(input_file));
|
||||
load_files(input_paths, false, true);
|
||||
if (! input_file.empty())
|
||||
load_files({ into_path(input_file) }, false, true);
|
||||
}
|
||||
|
||||
void Plater::load_gcode()
|
||||
|
@ -4982,10 +4979,9 @@ void Plater::load_gcode(const wxString& filename)
|
|||
|
||||
// process gcode
|
||||
GCodeProcessor processor;
|
||||
processor.enable_producers(true);
|
||||
try
|
||||
{
|
||||
processor.process_file(filename.ToUTF8().data(), false);
|
||||
processor.process_file(filename.ToUTF8().data());
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
|
@ -5184,15 +5180,11 @@ bool Plater::load_files(const wxArrayString& filenames)
|
|||
}
|
||||
case LoadType::LoadGeometry: {
|
||||
Plater::TakeSnapshot snapshot(this, _L("Import Object"));
|
||||
std::vector<fs::path> in_paths;
|
||||
in_paths.emplace_back(*it);
|
||||
load_files(in_paths, true, false);
|
||||
load_files({ *it }, true, false);
|
||||
break;
|
||||
}
|
||||
case LoadType::LoadConfig: {
|
||||
std::vector<fs::path> in_paths;
|
||||
in_paths.emplace_back(*it);
|
||||
load_files(in_paths, false, true);
|
||||
load_files({ *it }, false, true);
|
||||
break;
|
||||
}
|
||||
case LoadType::Unknown : {
|
||||
|
@ -5699,8 +5691,16 @@ void Plater::export_amf()
|
|||
|
||||
bool Plater::export_3mf(const boost::filesystem::path& output_path)
|
||||
{
|
||||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
if (p->model.objects.empty()) {
|
||||
MessageDialog dialog(nullptr, _L("The plater is empty.\nConfirm you want to save the project ?"), _L("Save project"), wxYES_NO);
|
||||
if (dialog.ShowModal() != wxID_YES)
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (p->model.objects.empty())
|
||||
return false;
|
||||
#endif // ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
|
||||
wxString path;
|
||||
bool export_config = true;
|
||||
|
@ -6250,16 +6250,6 @@ BoundingBoxf Plater::bed_shape_bb() const
|
|||
return p->bed_shape_bb();
|
||||
}
|
||||
|
||||
void Plater::start_mapping_gcode_window()
|
||||
{
|
||||
p->preview->get_canvas3d()->start_mapping_gcode_window();
|
||||
}
|
||||
|
||||
void Plater::stop_mapping_gcode_window()
|
||||
{
|
||||
p->preview->get_canvas3d()->stop_mapping_gcode_window();
|
||||
}
|
||||
|
||||
void Plater::arrange()
|
||||
{
|
||||
p->m_ui_jobs.arrange();
|
||||
|
@ -6689,6 +6679,11 @@ bool Plater::is_render_statistic_dialog_visible() const
|
|||
return p->show_render_statistic_dialog;
|
||||
}
|
||||
|
||||
|
||||
Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name)
|
||||
: TakeSnapshot(plater, from_u8(snapshot_name)) {}
|
||||
|
||||
|
||||
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
|
||||
bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
|
||||
{
|
||||
|
|
|
@ -282,9 +282,6 @@ public:
|
|||
GLCanvas3D* get_current_canvas3D();
|
||||
BoundingBoxf bed_shape_bb() const;
|
||||
|
||||
void start_mapping_gcode_window();
|
||||
void stop_mapping_gcode_window();
|
||||
|
||||
void arrange();
|
||||
void find_new_position(const ModelInstancePtrs &instances);
|
||||
|
||||
|
@ -383,10 +380,11 @@ public:
|
|||
Plater *m_plater;
|
||||
};
|
||||
|
||||
// ROII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
|
||||
// RAII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
|
||||
class TakeSnapshot
|
||||
{
|
||||
public:
|
||||
TakeSnapshot(Plater *plater, const std::string &snapshot_name);
|
||||
TakeSnapshot(Plater *plater, const wxString &snapshot_name) : m_plater(plater)
|
||||
{
|
||||
m_plater->take_snapshot(snapshot_name);
|
||||
|
|
|
@ -636,8 +636,7 @@ PlaterPresetComboBox::~PlaterPresetComboBox()
|
|||
|
||||
static void run_wizard(ConfigWizard::StartPage sp)
|
||||
{
|
||||
if (wxGetApp().check_and_save_current_preset_changes())
|
||||
wxGetApp().run_wizard(ConfigWizard::RR_USER, sp);
|
||||
wxGetApp().run_wizard(ConfigWizard::RR_USER, sp);
|
||||
}
|
||||
|
||||
void PlaterPresetComboBox::OnSelect(wxCommandEvent &evt)
|
||||
|
|
|
@ -492,17 +492,15 @@ void Tab::OnActivate()
|
|||
activate_selected_page([](){});
|
||||
m_hsizer->Layout();
|
||||
|
||||
// Workaroud for Menu instead of NoteBook
|
||||
#ifdef _MSW_DARK_MODE
|
||||
// if (wxGetApp().tabs_as_menu())
|
||||
{
|
||||
wxSize sz = m_presets_choice->GetSize();
|
||||
wxSize ok_sz = wxSize(35 * m_em_unit, m_presets_choice->GetBestSize().y+1);
|
||||
if (sz != ok_sz) {
|
||||
m_presets_choice->SetMinSize(ok_sz);
|
||||
m_presets_choice->SetSize(ok_sz);
|
||||
GetSizer()->GetItem(size_t(0))->GetSizer()->Layout();
|
||||
}
|
||||
// Because of DarkMode we use our own Notebook (inherited from wxSiplebook) instead of wxNotebook
|
||||
// And it looks like first Layout of the page doesn't update a size of the m_presets_choice
|
||||
// So we have to set correct size explicitely
|
||||
if (wxSize ok_sz = wxSize(35 * m_em_unit, m_presets_choice->GetBestSize().y);
|
||||
ok_sz != m_presets_choice->GetSize()) {
|
||||
m_presets_choice->SetMinSize(ok_sz);
|
||||
m_presets_choice->SetSize(ok_sz);
|
||||
GetSizer()->GetItem(size_t(0))->GetSizer()->Layout();
|
||||
}
|
||||
#endif // _MSW_DARK_MODE
|
||||
Refresh();
|
||||
|
@ -1509,7 +1507,7 @@ void TabPrint::build()
|
|||
optgroup = page->new_optgroup(L("Brim"));
|
||||
optgroup->append_single_option_line("brim_type", category_path + "brim");
|
||||
optgroup->append_single_option_line("brim_width", category_path + "brim");
|
||||
optgroup->append_single_option_line("brim_offset", category_path + "brim");
|
||||
optgroup->append_single_option_line("brim_separation", category_path + "brim");
|
||||
|
||||
page = add_options_page(L("Support material"), "support");
|
||||
category_path = "support-material_1698#";
|
||||
|
@ -1565,12 +1563,14 @@ void TabPrint::build()
|
|||
|
||||
optgroup = page->new_optgroup(L("Modifiers"));
|
||||
optgroup->append_single_option_line("first_layer_speed");
|
||||
optgroup->append_single_option_line("first_layer_speed_over_raft");
|
||||
|
||||
optgroup = page->new_optgroup(L("Acceleration control (advanced)"));
|
||||
optgroup->append_single_option_line("perimeter_acceleration");
|
||||
optgroup->append_single_option_line("infill_acceleration");
|
||||
optgroup->append_single_option_line("bridge_acceleration");
|
||||
optgroup->append_single_option_line("first_layer_acceleration");
|
||||
optgroup->append_single_option_line("first_layer_acceleration_over_raft");
|
||||
optgroup->append_single_option_line("default_acceleration");
|
||||
|
||||
optgroup = page->new_optgroup(L("Autospeed (advanced)"));
|
||||
|
|
|
@ -696,7 +696,12 @@ void DiffViewCtrl::context_menu(wxDataViewEvent& event)
|
|||
auto it = m_items_map.find(item);
|
||||
if (it == m_items_map.end() || !it->second.is_long)
|
||||
return;
|
||||
FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val).ShowModal();
|
||||
|
||||
size_t column_cnt = this->GetColumnCount();
|
||||
const wxString old_value_header = this->GetColumn(column_cnt - 2)->GetTitle();
|
||||
const wxString new_value_header = this->GetColumn(column_cnt - 1)->GetTitle();
|
||||
FullCompareDialog(it->second.opt_name, it->second.old_val, it->second.new_val,
|
||||
old_value_header, new_value_header).ShowModal();
|
||||
|
||||
#ifdef __WXOSX__
|
||||
wxWindow* parent = this->GetParent();
|
||||
|
@ -752,8 +757,8 @@ std::vector<std::string> DiffViewCtrl::selected_options()
|
|||
// UnsavedChangesDialog
|
||||
//------------------------------------------
|
||||
|
||||
UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header)
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, _L("PrusaSlicer is closing: Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
UnsavedChangesDialog::UnsavedChangesDialog(const wxString& header, const wxString& caption/* = wxString()*/)
|
||||
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY, (caption.IsEmpty() ? _L("PrusaSlicer is closing") : caption) + ": " + _L("Unsaved Changes"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
m_app_config_key = "default_action_on_close_application";
|
||||
|
||||
|
@ -1281,7 +1286,8 @@ void UnsavedChangesDialog::on_sys_color_changed()
|
|||
// FullCompareDialog
|
||||
//------------------------------------------
|
||||
|
||||
FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value)
|
||||
FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value,
|
||||
const wxString& old_value_header, const wxString& new_value_header)
|
||||
: wxDialog(nullptr, wxID_ANY, option_name, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
|
||||
{
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
|
@ -1302,8 +1308,8 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
|
|||
grid_sizer->Add(text, 0, wxALL, border);
|
||||
};
|
||||
|
||||
add_header(_L("Old value"));
|
||||
add_header(_L("New value"));
|
||||
add_header(old_value_header);
|
||||
add_header(new_value_header);
|
||||
|
||||
auto get_set_from_val = [](wxString str) {
|
||||
if (str.Find("\n") == wxNOT_FOUND)
|
||||
|
@ -1327,7 +1333,7 @@ FullCompareDialog::FullCompareDialog(const wxString& option_name, const wxString
|
|||
std::set_difference(new_set.begin(), new_set.end(), old_set.begin(), old_set.end(), std::inserter(new_old_diff_set, new_old_diff_set.begin()));
|
||||
|
||||
auto add_value = [grid_sizer, border, this](wxString label, const std::set<wxString>& diff_set, bool is_colored = false) {
|
||||
wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_SIMPLE | wxTE_RICH);
|
||||
wxTextCtrl* text = new wxTextCtrl(this, wxID_ANY, label, wxDefaultPosition, wxSize(400, 400), wxTE_MULTILINE | wxTE_READONLY | wxBORDER_DEFAULT | wxTE_RICH);
|
||||
wxGetApp().UpdateDarkUI(text);
|
||||
text->SetStyle(0, label.Len(), wxTextAttr(is_colored ? wxColour(orange) : wxNullColour, wxNullColour, this->GetFont()));
|
||||
|
||||
|
|
|
@ -263,7 +263,7 @@ class UnsavedChangesDialog : public DPIDialog
|
|||
std::vector<std::pair<std::string, Preset::Type>> names_and_types;
|
||||
|
||||
public:
|
||||
UnsavedChangesDialog(const wxString& header);
|
||||
UnsavedChangesDialog(const wxString& header, const wxString& caption = wxString());
|
||||
UnsavedChangesDialog(Preset::Type type, PresetCollection* dependent_presets, const std::string& new_selected_preset);
|
||||
~UnsavedChangesDialog() {}
|
||||
|
||||
|
@ -299,7 +299,8 @@ protected:
|
|||
class FullCompareDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value);
|
||||
FullCompareDialog(const wxString& option_name, const wxString& old_value, const wxString& new_value,
|
||||
const wxString& old_value_header, const wxString& new_value_header);
|
||||
~FullCompareDialog() {}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue