From 19d02e6d749b844c2bcd5b8c158f923720380b99 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 17 Mar 2023 09:29:28 +0100 Subject: [PATCH 01/19] Fix: ../src/slic3r/GUI/IconManager.cpp:174:22: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast] ../src/slic3r/GUI/SurfaceDrag.cpp:104:25: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector::size_type' {aka 'long unsigned int'} [-Wsign-compare] ../src/slic3r/GUI/SurfaceDrag.cpp:57:30: warning: 'std::optional Slic3r::GUI::calc_scale(const Matrix3d&, const Matrix3d&, const Vec3d&)' defined but not used [-Wunused-function] ../src/slic3r/Utils/RaycastManager.cpp:14:56: warning: suggest parentheses around '&&' within '||' [-Wparentheses] ../src/slic3r/Utils/RaycastManager.cpp:316:32: warning: comparison of integer expressions of different signedness: 'int' and 'std::vector::size_type' {aka 'long unsigned int'} [-Wsign-compare] --- src/slic3r/GUI/IconManager.cpp | 2 +- src/slic3r/GUI/SurfaceDrag.cpp | 16 +++++++++------- src/slic3r/Utils/RaycastManager.cpp | 9 ++++++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/slic3r/GUI/IconManager.cpp b/src/slic3r/GUI/IconManager.cpp index 1974e917cb..45c76887c3 100644 --- a/src/slic3r/GUI/IconManager.cpp +++ b/src/slic3r/GUI/IconManager.cpp @@ -171,7 +171,7 @@ void draw(const IconManager::Icon &icon, const ImVec2 &size, const ImVec4 &tint_ return; } - ImTextureID id = (void *) icon.tex_id; + ImTextureID id = (void *)static_cast(icon.tex_id); const ImVec2 &s = (size.x < 1 || size.y < 1) ? icon.size : size; ImGui::Image(id, s, icon.tl, icon.br, tint_col, border_col); } diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index f32113698b..4a48ced29f 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -53,8 +53,8 @@ static Vec2d calc_screen_offset_to_volume_center(const Vec2d &screen_coor, const return nearest_offset; } - // Calculate scale in world -static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) + // Calculate scale in world for check in debug +[[maybe_unused]] static std::optional calc_scale(const Matrix3d &from, const Matrix3d &to, const Vec3d &dir) { Vec3d from_dir = from * dir; Vec3d to_dir = to * dir; @@ -98,11 +98,13 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, return false; // is selected volume closest hovered? - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - int hovered_idx = canvas.get_first_hover_volume_idx(); - if (hovered_idx < 0 || - hovered_idx >= gl_volumes.size() || - gl_volumes[hovered_idx] != gl_volume) + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + if (int hovered_idx = canvas.get_first_hover_volume_idx(); + hovered_idx < 0) + return false; + else if (auto hovered_idx_ = static_cast(hovered_idx); + hovered_idx_ >= gl_volumes.size() || + gl_volumes[hovered_idx_] != gl_volume) return false; const ModelObject *object = get_model_object(*gl_volume, canvas.get_model()->objects); diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 63cb580dbe..80a7167554 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -11,7 +11,7 @@ static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelIn return std::make_pair(instance.id().id, volume.id().id); } static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { - return k1.first < k2.first || k1.first == k2.first && k1.second < k2.second; } + return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); } static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; } @@ -313,9 +313,12 @@ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::A RaycastManager::Meshes meshes; for (const std::shared_ptr &caster : casters) { int index = SceneRaycaster::decode_id(type, caster->get_id()); - if (index < 0 || index >= gl_volumes.size()) + if (index < 0) continue; - const GLVolume *gl_volume = gl_volumes[index]; + auto index_ = static_cast(index); + if(index_ >= gl_volumes.size()) + continue; + const GLVolume *gl_volume = gl_volumes[index_]; const ModelVolume *volume = get_model_volume(*gl_volume, objects); size_t id = volume->id().id; if (condition.skip(id)) From d5f229a249efd1c30378d1fd670dbb19bdf7dafe Mon Sep 17 00:00:00 2001 From: David Kocik Date: Thu, 9 Mar 2023 17:14:36 +0100 Subject: [PATCH 02/19] Refactoring of miniz extract calls. Use file_index instead of filename as identifier. --- src/libslic3r/Format/3mf.cpp | 18 +++++++++--------- src/libslic3r/Format/AMF.cpp | 2 +- src/libslic3r/Format/ZipperArchiveImport.cpp | 4 ++-- src/slic3r/Utils/PresetUpdater.cpp | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 260e82e81c..34594240f5 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -957,7 +957,7 @@ namespace Slic3r { try { - res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { + res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { CallbackData* data = (CallbackData*)pOpaque; if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.parse_error()) { char error_buf[1024]; @@ -991,7 +991,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading cut information data to buffer"); return; @@ -1053,7 +1053,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading config data to buffer"); return; @@ -1073,7 +1073,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading layer heights profile data to buffer"); return; @@ -1135,7 +1135,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading layer config ranges data to buffer"); return; @@ -1192,7 +1192,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading sla support points data to buffer"); return; @@ -1274,7 +1274,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer(size_t(stat.m_uncomp_size), 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading sla support points data to buffer"); return; @@ -1379,7 +1379,7 @@ namespace Slic3r { return false; } - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, parser_buffer, (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading config data to buffer"); return false; @@ -1399,7 +1399,7 @@ namespace Slic3r { { if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { add_error("Error while reading custom Gcodes per height data to buffer"); return; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 7acec17716..a38960324c 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -965,7 +965,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi try { - res = mz_zip_reader_extract_file_to_callback(&archive, stat.m_filename, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { + res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { CallbackData* data = (CallbackData*)pOpaque; if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->ctx.error()) { diff --git a/src/libslic3r/Format/ZipperArchiveImport.cpp b/src/libslic3r/Format/ZipperArchiveImport.cpp index 5b0ecff6e4..2bd5f555b5 100644 --- a/src/libslic3r/Format/ZipperArchiveImport.cpp +++ b/src/libslic3r/Format/ZipperArchiveImport.cpp @@ -18,7 +18,7 @@ boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, { std::string buf(size_t(entry.m_uncomp_size), '\0'); - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + if (!mz_zip_reader_extract_to_mem(&zip.arch, entry.m_file_index, buf.data(), buf.size(), 0)) throw Slic3r::FileIOError(zip.get_errorstr()); @@ -35,7 +35,7 @@ EntryBuffer read_entry(const mz_zip_archive_file_stat &entry, { std::vector buf(entry.m_uncomp_size); - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + if (!mz_zip_reader_extract_to_mem(&zip.arch, entry.m_file_index, buf.data(), buf.size(), 0)) throw Slic3r::FileIOError(zip.get_errorstr()); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index e7bea48199..028c7ce0a5 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -399,7 +399,7 @@ void PresetUpdater::priv::sync_config(const VendorMap vendors, const std::string std::string name(stat.m_filename); if (stat.m_uncomp_size > 0) { std::string buffer((size_t)stat.m_uncomp_size, 0); - mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0); if (res == 0) { BOOST_LOG_TRIVIAL(error) << "Failed to unzip " << stat.m_filename; continue; From b61a6f293d0dd638e17acafeb86e67d037e46c92 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Mar 2023 12:56:25 +0100 Subject: [PATCH 03/19] fix problem with missing lslice Links - detection could occasionally fail, issue 9744 --- src/libslic3r/Layer.cpp | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 7962f0376b..2d11322831 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -1,11 +1,14 @@ #include "Layer.hpp" #include "ClipperZUtils.hpp" #include "ClipperUtils.hpp" +#include "Point.hpp" +#include "Polygon.hpp" #include "Print.hpp" #include "Fill/Fill.hpp" #include "ShortestPath.hpp" #include "SVG.hpp" #include "BoundingBox.hpp" +#include "clipper/clipper.hpp" #include @@ -180,6 +183,31 @@ static void connect_layer_slices( break; } } + if (!found) { + // The check above might sometimes fail when the polygons overlap only on points, which causes the clipper to detect no intersection. + // The problem happens rarely, mostly on simple polygons (in terms of number of points), but regardless of size! + // example of failing link on two layers, each with single polygon without holes. + // layer A = Polygon{(-24931238,-11153865),(-22504249,-8726874),(-22504249,11477151),(-23261469,12235585),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)} + // layer B = Polygon{(-24877897,-11100524),(-22504249,-8726874),(-22504249,11477151),(-23244827,12218916),(-23752371,12727276),(-25002495,12727276),(-27502745,10227026),(-27502745,-12727274),(-26504645,-12727274)} + // note that first point is not identical, and the check above picks (-24877897,-11100524) as the first contour point (polynode.Contour.front()). + // that point is sadly slightly outisde of the layer A, so no link is detected, eventhough they are overlaping "completely" + Polygon contour_poly; + for (const auto& p : polynode.Contour) { + contour_poly.points.emplace_back(p.x(), p.y()); + } + BoundingBox contour_aabb{contour_poly.points}; + for (int l = int(m_above.lslices_ex.size()) - 1; l >= 0; --l) { + LayerSlice &lslice = m_above.lslices_ex[l]; + // it is potentially slow, but should be executed rarely + if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_above.lslices[l]).empty()) { + found = true; + j = l; + assert(i >= 0 && i < m_below.lslices_ex.size()); + assert(j >= 0 && j < m_above.lslices_ex.size()); + break; + } + } + } } else { // Index of an island above. Look-it up in the island below. assert(j < m_offset_end); @@ -194,6 +222,23 @@ static void connect_layer_slices( break; } } + if (!found) { // Explanation for aditional check is above. + Polygon contour_poly; + for (const auto &p : polynode.Contour) { + contour_poly.points.emplace_back(p.x(), p.y()); + } + BoundingBox contour_aabb{contour_poly.points}; + for (int l = int(m_below.lslices_ex.size()) - 1; l >= 0; --l) { + LayerSlice &lslice = m_below.lslices_ex[l]; + if (contour_aabb.overlap(lslice.bbox) && !intersection(Polygons{contour_poly}, m_below.lslices[l]).empty()) { + found = true; + i = l; + assert(i >= 0 && i < m_below.lslices_ex.size()); + assert(j >= 0 && j < m_above.lslices_ex.size()); + break; + } + } + } } } else { assert(assert_intersection_valid(i, j)); From 520148a8aaf163c30331ddd6d46872797b98b60d Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:48:06 +0100 Subject: [PATCH 04/19] Added new AnkerMake resources --- resources/profiles/Anker/M5-texture_v2.svg | 1 + resources/profiles/Anker/M5_thumbnail_v2.png | Bin 0 -> 26339 bytes 2 files changed, 1 insertion(+) create mode 100644 resources/profiles/Anker/M5-texture_v2.svg create mode 100644 resources/profiles/Anker/M5_thumbnail_v2.png diff --git a/resources/profiles/Anker/M5-texture_v2.svg b/resources/profiles/Anker/M5-texture_v2.svg new file mode 100644 index 0000000000..4fcf959f66 --- /dev/null +++ b/resources/profiles/Anker/M5-texture_v2.svg @@ -0,0 +1 @@ + diff --git a/resources/profiles/Anker/M5_thumbnail_v2.png b/resources/profiles/Anker/M5_thumbnail_v2.png new file mode 100644 index 0000000000000000000000000000000000000000..a03a7bc08d9de13d5f6699f5e846db293180bdb4 GIT binary patch literal 26339 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 literal 0 HcmV?d00001 From d4d463bc289a129d679ac0e2a7b47cd1bb894b62 Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:48:36 +0100 Subject: [PATCH 05/19] Official AnkerMake profiles. --- resources/profiles/Anker.idx | 6 +- resources/profiles/Anker.ini | 189 +++++++++++++++-------------------- 2 files changed, 84 insertions(+), 111 deletions(-) diff --git a/resources/profiles/Anker.idx b/resources/profiles/Anker.idx index 49e15b3d03..f96cee2476 100644 --- a/resources/profiles/Anker.idx +++ b/resources/profiles/Anker.idx @@ -1,3 +1,3 @@ -min_slic3r_version = 2.6.0-alpha1 -1.0.1 Disabled thick bridges. -1.0.0 Initial Version +min_slic3r_version = 2.6.0-alpha4 +1.1.1 Initial official version +1.0.1 Initial Version diff --git a/resources/profiles/Anker.ini b/resources/profiles/Anker.ini index 34592e1e55..70cb4dcf14 100644 --- a/resources/profiles/Anker.ini +++ b/resources/profiles/Anker.ini @@ -1,14 +1,14 @@ # Print profiles for the AnkerMake printers. -# https://github.com/prusa3d/PrusaSlicer/pull/9075 by @just-trey [vendor] # Vendor name will be shown by the Config Wizard. -name = Anker +name = AnkerMake # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.0.1 +config_version = 1.1.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anker/ +# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% # The printer models will be shown by the Configuration Wizard in this order, # also the first model installed & the first nozzle installed will be activated after install. @@ -20,8 +20,9 @@ variants = 0.4 technology = FFF family = AnkerMake bed_model = M5-bed.stl -bed_texture = M5-texture.svg -default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER +bed_texture = M5-texture_v2.svg +thumbnail = M5_thumbnail_v2.png +default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER; # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -31,8 +32,8 @@ default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; avoid_crossing_perimeters = 0 bridge_acceleration = 2500 bridge_angle = 0 -bridge_flow_ratio = 0.95 -bridge_speed = 150 +bridge_flow_ratio = 1 +bridge_speed = 50 brim_separation = 0.1 brim_type = outer_only brim_width = 0 @@ -40,32 +41,36 @@ clip_multipart_objects = 1 complete_objects = 0 default_acceleration = 2500 dont_support_bridges = 1 -elefant_foot_compensation = 0.1 +elefant_foot_compensation = 0.2 ensure_vertical_shell_thickness = 1 external_perimeter_speed = 150 external_perimeters_first = 0 extra_perimeters = 0 extruder_clearance_height = 30 extruder_clearance_radius = 45 +extrusion_width = 0.4 +external_perimeter_extrusion_width = 0.44 + fill_angle = 45 -fill_density = 15% -fill_pattern = cubic +fill_density = 10% +fill_pattern = grid first_layer_acceleration = 2500 first_layer_acceleration_over_raft = 0 -first_layer_extrusion_width = 200% +first_layer_extrusion_width = 0.4 first_layer_speed = 50% first_layer_speed_over_raft = 30 gap_fill_enabled = 1 gap_fill_speed = 150 gcode_comments = 0 infill_acceleration = 2500 -infill_anchor = 600% -infill_anchor_max = 50 +infill_anchor = 2.5 +infill_anchor_max = 12 infill_every_layers = 1 infill_extruder = 1 infill_first = 0 +infill_extrusion_width = 0.4 infill_only_where_needed = 0 -infill_overlap = 23% +infill_overlap = 10% infill_speed = 250 interface_shells = 0 max_print_speed = 250 @@ -76,18 +81,18 @@ min_skirt_length = 4 notes = only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{digits(layer_height,1,2)}mm_{filament_type[0]}_{printer_model}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}.gcode overhangs = 1 perimeter_acceleration = 2500 perimeter_extruder = 1 -perimeter_extrusion_width = 0 -perimeter_generator = arachne +perimeter_extrusion_width = 0.4 +perimeter_generator = classic perimeter_speed = 250 perimeters = 3 post_process = print_settings_id = raft_layers = 0 -resolution = 0 +resolution = 0.01 seam_position = aligned single_extruder_multi_material_priming = 0 skirt_distance = 3 @@ -97,32 +102,37 @@ small_perimeter_speed = 150 solid_infill_below_area = 0 solid_infill_every_layers = 0 solid_infill_extruder = 1 -solid_infill_speed = 175 +solid_infill_extrusion_width = 0.4 +solid_infill_speed = 250 spiral_vase = 0 standby_temperature_delta = -5 +support_material_auto = 0 support_material = 0 support_material_angle = 0 support_material_buildplate_only = 0 -support_material_contact_distance = 0.15 +support_material_contact_distance = 0.1 support_material_enforce_layers = 0 support_material_extruder = 0 support_material_interface_contact_loops = 0 support_material_interface_extruder = 0 support_material_interface_layers = 2 support_material_interface_spacing = 0.2 -support_material_interface_speed = 100% +support_material_interface_speed = 80% support_material_pattern = rectilinear support_material_spacing = 2 -support_material_speed = 125 +support_material_speed = 150 support_material_synchronize_layers = 0 -support_material_threshold = 40 +support_material_threshold = 55 support_material_with_sheath = 0 -support_material_xy_spacing = 60% +support_material_xy_spacing = 50% thick_bridges = 0 thin_walls = 0 top_solid_infill_speed = 150 -travel_speed = 300 -travel_speed_z = 10 +top_infill_extrusion_width = 0.4 +top_fill_pattern = rectilinear +bottom_fill_pattern = rectilinear +travel_speed = 250 +travel_speed_z = 0 wipe_tower = 0 wipe_tower_bridging = 10 wipe_tower_rotation_angle = 0 @@ -131,86 +141,49 @@ wipe_tower_x = 170 wipe_tower_y = 140 xy_size_compensation = 0 -[print:*0.08mm*] -inherits = *common* -layer_height = 0.08 -first_layer_height = 0.12 -bottom_solid_layers = 9 -top_solid_layers = 11 -bridge_flow_ratio = 0.70 - [print:*0.10mm*] inherits = *common* layer_height = 0.10 -first_layer_height = 0.14 +first_layer_height = 0.10 bottom_solid_layers = 7 top_solid_layers = 9 -bridge_flow_ratio = 0.70 - -[print:*0.12mm*] -inherits = *common* -layer_height = 0.12 -first_layer_height = 0.16 -bottom_solid_layers = 6 -top_solid_layers = 7 -bridge_flow_ratio = 0.70 - -[print:*0.16mm*] -inherits = *common* -layer_height = 0.16 -first_layer_height = 0.20 -bottom_solid_layers = 5 -top_solid_layers = 7 -bridge_flow_ratio = 0.85 +bridge_flow_ratio = 1 [print:*0.20mm*] inherits = *common* layer_height = 0.20 -first_layer_height = 0.24 +first_layer_height = 0.14 bottom_solid_layers = 4 top_solid_layers = 5 -[print:*0.24mm*] +[print:*0.30mm*] inherits = *common* -layer_height = 0.24 -first_layer_height = 0.28 +layer_height = 0.30 +first_layer_height = 0.21 bottom_solid_layers = 3 top_solid_layers = 4 -[print:*0.28mm*] -inherits = *common* -layer_height = 0.28 -first_layer_height = 0.28 -bottom_solid_layers = 3 -top_solid_layers = 4 - -[print:0.08 mm SUPERDETAIL (0.4 mm nozzle) @ANKER] -inherits = *0.08mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 [print:0.10 mm HIGHDETAIL (0.4 mm nozzle) @ANKER] inherits = *0.10mm* compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 -[print:0.12 mm DETAIL (0.4 mm nozzle) @ANKER] -inherits = *0.12mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 - -[print:0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER] -inherits = *0.16mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 - [print:0.20 mm NORMAL (0.4 mm nozzle) @ANKER] inherits = *0.20mm* compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 -[print:0.24 mm DRAFT (0.4 mm nozzle) @ANKER] -inherits = *0.24mm* + +[print:0.30 mm SUPERDRAFT (0.4 mm nozzle) @ANKER] +inherits = *0.30mm* compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 -[print:0.28 mm SUPERDRAFT (0.4 mm nozzle) @ANKER] -inherits = *0.28mm* -compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4 +# When submitting new filaments please print the following temperature tower at 0.1mm layer height: +# https://www.thingiverse.com/thing:2615842 +# Pay particular attention to bridging, overhangs and retractions. +# Also print the following bed adhesion test at 0.1 layer height as well: +# https://www.prusaprinters.org/prints/4634-bed-adhesion-warp-test +# At least for PLA, please keep bed temp at 60, as many Creality printers do not have any ABL +# So having some leeway to get good bed adhesion is not a luxury for many users [filament:*common*] cooling = 0 @@ -235,47 +208,47 @@ filament_type = PLA filament_density = 1.24 filament_cost = 20 first_layer_bed_temperature = 60 -first_layer_temperature = 210 +first_layer_temperature = 230 fan_always_on = 1 max_fan_speed = 100 min_fan_speed = 100 bridge_fan_speed = 100 -disable_fan_first_layers = 2 -temperature = 205 +disable_fan_first_layers = 1 +temperature = 200 [filament:*PLA+*] inherits = *common* bed_temperature = 60 fan_below_layer_time = 100 filament_colour = #DDDDDD -filament_type = PLA +filament_type = PLA+ filament_density = 1.24 filament_cost = 20 first_layer_bed_temperature = 60 -first_layer_temperature = 220 +first_layer_temperature = 230 fan_always_on = 1 max_fan_speed = 100 min_fan_speed = 100 bridge_fan_speed = 100 -disable_fan_first_layers = 2 -temperature = 210 +disable_fan_first_layers = 1 +temperature = 200 [filament:*PET*] inherits = *common* -bed_temperature = 70 +bed_temperature = 80 disable_fan_first_layers = 2 fan_below_layer_time = 20 filament_colour = #DDDDDD filament_type = PETG filament_density = 1.27 filament_cost = 30 -first_layer_bed_temperature = 70 -first_layer_temperature = 240 +first_layer_bed_temperature = 80 +first_layer_temperature = 260 fan_always_on = 1 max_fan_speed = 50 min_fan_speed = 50 bridge_fan_speed = 100 -temperature = 240 +temperature = 260 [filament:*ABS*] inherits = *common* @@ -286,14 +259,14 @@ filament_colour = #DDDDDD filament_type = ABS filament_density = 1.04 filament_cost = 20 -first_layer_bed_temperature = 100 -first_layer_temperature = 245 +first_layer_bed_temperature = 90 +first_layer_temperature = 260 fan_always_on = 0 max_fan_speed = 0 min_fan_speed = 0 bridge_fan_speed = 30 top_fan_speed = 0 -temperature = 245 +temperature = 260 [filament:Generic PLA @ANKER] inherits = *PLA* @@ -324,8 +297,8 @@ deretract_speed = 60 extruder_colour = #FCE94F extruder_offset = 0x0 gcode_flavor = marlin -silent_mode = 0 -remaining_times = 0 +silent_mode = 1 +remaining_times = 1 machine_max_acceleration_e = 2500 machine_max_acceleration_extruding = 2500 machine_max_acceleration_retracting = 2500 @@ -338,8 +311,8 @@ machine_max_feedrate_x = 300 machine_max_feedrate_y = 300 machine_max_feedrate_z = 20 machine_max_jerk_e = 3 -machine_max_jerk_x = 30 -machine_max_jerk_y = 30 +machine_max_jerk_x = 15 +machine_max_jerk_y = 15 machine_max_jerk_z = 0.3 machine_min_extruding_rate = 0 machine_min_travel_rate = 0 @@ -347,10 +320,10 @@ layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z} max_print_height = 250 printer_notes = printer_settings_id = -retract_before_travel = 2 -retract_before_wipe = 70% +retract_before_travel = 3 +retract_before_wipe = 0 retract_layer_change = 1 -retract_length_toolchange = 1 +retract_length_toolchange = 4 retract_lift = 0 retract_lift_above = 0 retract_lift_below = 0 @@ -365,10 +338,10 @@ use_firmware_retraction = 0 use_relative_e_distances = 0 use_volumetric_e = 0 variable_layer_height = 1 -wipe = 1 +wipe = 0 z_offset = 0 -default_filament_profile = "Generic PLA+ @ANKER" -start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for nozzle temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime) +default_filament_profile = Generic PLA+ @ANKER +start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nM420 S1; restore saved Auto Bed Leveling data\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime) end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84 [printer:*M5*] @@ -376,12 +349,12 @@ inherits = *common* bed_shape = 0x0,235-0,235x235,0x235 max_print_height = 250 printer_model = M5 -retract_length = 1.25 +retract_length = 3 retract_speed = 60 deretract_speed = 60 -retract_before_travel = 1 +retract_before_travel = 3 retract_before_wipe = 0% -printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANKERMAKE\nPRINTER_MODEL_M5 +printer_notes = Don not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANKERMAKE\nPRINTER_MODEL_M5 [printer:AnkerMake M5 (0.4 mm nozzle)] inherits = *M5* @@ -389,5 +362,5 @@ nozzle_diameter = 0.4 printer_variant = 0.4 min_layer_height = 0.08 max_layer_height = 0.32 -retract_lift_above = 0.2 -default_print_profile = "0.16 mm OPTIMAL (0.4 mm nozzle) @ANKER" \ No newline at end of file +retract_lift_above = 0 +default_print_profile = 0.2 mm OPTIMAL (0.4 mm nozzle) @ANKER From 16024b12c523e194f32c9ebf438442ceaeb9375c Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Fri, 17 Mar 2023 15:51:51 +0100 Subject: [PATCH 06/19] Update Anker.idx --- resources/profiles/Anker.idx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/profiles/Anker.idx b/resources/profiles/Anker.idx index f96cee2476..0cf915db16 100644 --- a/resources/profiles/Anker.idx +++ b/resources/profiles/Anker.idx @@ -1,3 +1,5 @@ min_slic3r_version = 2.6.0-alpha4 1.1.1 Initial official version 1.0.1 Initial Version +min_slic3r_version = 2.6.0-alpha1 +1.0.0 Initial Version From 79adb72a2529a56dd9cca1628298b4b6bcea1427 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 17 Mar 2023 17:13:57 +0100 Subject: [PATCH 07/19] Fix int overflow, reduces amount of bridging substantially, improve code description, extend bridge anchors when anchoring to solid surfaces on lower layers --- src/libslic3r/PrintObject.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b72a2bacb7..86cb16e2d0 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1618,7 +1618,8 @@ void PrintObject::bridge_over_infill() if (layer->lower_layer == nullptr) { continue; } - auto spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); + double spacing = layer->regions().front()->flow(frSolidInfill).scaled_spacing(); + // unsupported area will serve as a filter for polygons worth bridging. Polygons unsupported_area; Polygons lower_layer_solids; bool contains_only_lightning = true; @@ -1627,6 +1628,7 @@ void PrintObject::bridge_over_infill() contains_only_lightning = false; } Polygons fill_polys = to_polygons(region->fill_expolygons()); + // initially consider the whole layer unsupported, but also gather solid layers to later cut off supported parts unsupported_area.insert(unsupported_area.end(), fill_polys.begin(), fill_polys.end()); for (const Surface &surface : region->fill_surfaces()) { if (surface.surface_type != stInternal || region->region().config().fill_density.value == 100) { @@ -1636,25 +1638,30 @@ void PrintObject::bridge_over_infill() } } unsupported_area = closing(unsupported_area, SCALED_EPSILON); - - lower_layer_solids = expand(lower_layer_solids, 4 * spacing); - unsupported_area = shrink(unsupported_area, 4 * spacing); + // By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids + // NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole + lower_layer_solids = expand(lower_layer_solids, 3 * spacing); + // By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters. + unsupported_area = shrink(unsupported_area, 3 * spacing); unsupported_area = diff(unsupported_area, lower_layer_solids); for (const LayerRegion *region : layer->regions()) { SurfacesPtr region_internal_solids = region->fill_surfaces().filter_by_type(stInternalSolid); for (const Surface *s : region_internal_solids) { Polygons unsupported = intersection(to_polygons(s->expolygon), unsupported_area); + // The following flag marks those surfaces, which overlap with unuspported area, but at least part of them is supported. + // These regions can be filtered by area, because they for sure are touching solids on lower layers, and it does not make sense to bridge their tiny overhangs bool partially_supported = area(unsupported) < area(to_polygons(s->expolygon)) - EPSILON; - if (!unsupported.empty() && (!partially_supported || area(unsupported) > 5 * 5 * spacing * spacing)) { - Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing)); + if (!unsupported.empty() && (!partially_supported || area(unsupported) > 3 * 3 * spacing * spacing)) { + Polygons worth_bridging = intersection(to_polygons(s->expolygon), expand(unsupported, 4 * spacing)); + // after we extracted the part worth briding, we go over the leftovers and merge the tiny ones back, to not brake the surface too much for (const Polygon& p : diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))) { - auto area = p.area(); + double area = p.area(); if (area < spacing * scale_(12.0) && area > spacing * spacing) { worth_bridging.push_back(p); } } - worth_bridging = intersection(closing(worth_bridging, 2 * spacing), s->expolygon); + worth_bridging = intersection(closing(worth_bridging, SCALED_EPSILON), s->expolygon); candidate_surfaces.push_back(CandidateSurface(s, lidx, worth_bridging, region, 0, contains_only_lightning)); #ifdef DEBUG_BRIDGE_OVER_INFILL @@ -1663,7 +1670,7 @@ void PrintObject::bridge_over_infill() to_lines(unsupported_area)); #endif #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(s->expolygon)), + debug_draw(std::to_string(lidx) + "_candidate_processing_" + std::to_string(area(unsupported)), to_lines(unsupported), to_lines(intersection(to_polygons(s->expolygon), expand(unsupported, 5 * spacing))), to_lines(diff(to_polygons(s->expolygon), expand(worth_bridging, spacing))), to_lines(unsupported_area)); @@ -1728,15 +1735,15 @@ void PrintObject::bridge_over_infill() layer_area_covered_by_candidates[pair.first] = {}; } + // prepare inflated filter for each candidate on each layer. layers will be put into single thread cluster if they are close to each other (z-axis-wise) + // and if the inflated AABB polygons overlap somewhere tbb::parallel_for(tbb::blocked_range(0, layers_with_candidates.size()), [&layers_with_candidates, &surfaces_by_layer, &layer_area_covered_by_candidates]( tbb::blocked_range r) { for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) { size_t lidx = layers_with_candidates[job_idx]; for (const auto &candidate : surfaces_by_layer.at(lidx)) { - Polygon candiate_inflated_aabb = get_extents(candidate.new_polys) - .inflated(candidate.region->flow(frSolidInfill, true).scaled_spacing() * 5) - .polygon(); + Polygon candiate_inflated_aabb = get_extents(candidate.new_polys).inflated(scale_(7)).polygon(); layer_area_covered_by_candidates.at(lidx) = union_(layer_area_covered_by_candidates.at(lidx), Polygons{candiate_inflated_aabb}); } @@ -2088,13 +2095,13 @@ void PrintObject::bridge_over_infill() } } - deep_infill_area = expand(deep_infill_area, spacing); + deep_infill_area = expand(deep_infill_area, spacing * 1.5); // Now gather expansion polygons - internal infill on current layer, from which we can cut off anchors Polygons expansion_area; Polygons total_fill_area; for (const LayerRegion *region : layer->regions()) { - Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_type(stInternal)); + Polygons internal_polys = to_polygons(region->fill_surfaces().filter_by_types({stInternal, stInternalSolid})); expansion_area.insert(expansion_area.end(), internal_polys.begin(), internal_polys.end()); Polygons fill_polys = to_polygons(region->fill_expolygons()); total_fill_area.insert(total_fill_area.end(), fill_polys.begin(), fill_polys.end()); From 39bca4420c88ae8afd4221acd266c42284d401b3 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 17 Mar 2023 17:13:29 +0100 Subject: [PATCH 08/19] Fix for #10072 - Split to objects is acting funky --- src/slic3r/GUI/Plater.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e89dd26240..a64de49eec 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3160,6 +3160,8 @@ void Plater::priv::split_object() // causing original positions not to be kept std::vector idxs = load_model_objects(new_objects); + // clear previosli selection + get_selection().clear(); // select newly added objects for (size_t idx : idxs) { From b9d8fe7118d03a607bff5acae3e5acb25ed666e8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 07:48:26 +0100 Subject: [PATCH 09/19] WIP: PlaceholderParser support for writable output variables. --- src/libslic3r/PlaceholderParser.cpp | 122 +++++++++++++++++++- src/libslic3r/PlaceholderParser.hpp | 6 +- tests/libslic3r/test_placeholder_parser.cpp | 13 +++ 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 5235fd72ed..c26197d27a 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -171,7 +171,8 @@ namespace client struct OptWithPos { OptWithPos() {} OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range) : opt(opt), it_range(it_range) {} - ConfigOptionConstPtr opt = nullptr; + ConfigOptionConstPtr opt { nullptr }; + bool writable { false }; boost::iterator_range it_range; }; @@ -688,6 +689,7 @@ namespace client const DynamicConfig *external_config = nullptr; const DynamicConfig *config = nullptr; const DynamicConfig *config_override = nullptr; + mutable DynamicConfig *config_outputs = nullptr; size_t current_extruder_id = 0; PlaceholderParser::ContextData *context_data = nullptr; // If false, the macro_processor will evaluate a full macro. @@ -713,6 +715,7 @@ namespace client } const ConfigOption* resolve_symbol(const std::string &opt_key) const { return this->optptr(opt_key); } + ConfigOption* resolve_output_symbol(const std::string &opt_key) const { return this->config_outputs ? this->config_outputs->optptr(opt_key, false) : nullptr; } template static void legacy_variable_expansion( @@ -788,8 +791,12 @@ namespace client OptWithPos &output) { const ConfigOption *opt = ctx->resolve_symbol(std::string(opt_key.begin(), opt_key.end())); - if (opt == nullptr) - ctx->throw_exception("Not a variable name", opt_key); + if (opt == nullptr) { + opt = ctx->resolve_output_symbol(std::string(opt_key.begin(), opt_key.end())); + if (opt == nullptr) + ctx->throw_exception("Not a variable name", opt_key); + output.writable = true; + } output.opt = opt; output.it_range = opt_key; } @@ -914,6 +921,98 @@ namespace client output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); } + // Decoding a scalar variable symbol "opt", assigning it a value of "param". + template + static void scalar_variable_assign( + const MyContext *ctx, + OptWithPos &opt, + expr ¶m, + // Not used, just clear it. + std::string &out) + { + if (! opt.writable) + ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + if (opt.opt->is_vector()) + ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); + ConfigOption *wropt = const_cast(opt.opt); + switch (wropt->type()) { + case coFloat: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coInt: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_i(); + break; + case coString: + static_cast(wropt)->value = param.to_string(); + break; + case coPercent: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coBool: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(wropt)->value = param.b(); + break; + default: + ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); + } + out.clear(); + } + + template + static void vector_variable_assign( + const MyContext *ctx, + OptWithPos &opt, + int &index, + expr ¶m, + // Not used, just clear it. + std::string &out) + { + if (! opt.writable) + ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); + if (opt.opt->is_scalar()) + ctx->throw_exception("Referencing an output scalar variable when vector is expected", opt.it_range); + ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + if (index < 0 || index >= int(vec->size())) + ctx->throw_exception("Index out of range", opt.it_range); + switch (opt.opt->type()) { + case coFloats: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[index] = param.as_d(); + break; + case coInts: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[index] = param.as_i(); + break; + case coStrings: + static_cast(vec)->values[index] = param.to_string(); + break; + case coPercents: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[index] = param.as_d(); + break; + case coBools: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(vec)->values[index] = param.b(); + break; + default: + ctx->throw_exception("Unsupported output vector variable type", opt.it_range); + } + out.clear(); + } + // Verify that the expression returns an integer, which may be used // to address a vector. template @@ -1165,7 +1264,9 @@ namespace client macro = (kw["if"] > if_else_output(_r1) [_val = _1]) // | (kw["switch"] > switch_output(_r1) [_val = _1]) - | additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]; + | (assignment_statement(_r1) [_val = _1]) + | (additive_expression(_r1) [ px::bind(&expr::to_string2, _1, _val) ]) + ; macro.name("macro"); // An if expression enclosed in {} (the outmost {} are already parsed by the caller). @@ -1257,6 +1358,15 @@ namespace client ); multiplicative_expression.name("multiplicative_expression"); + assignment_statement = + variable_reference(_r1)[_a = _1] >> + ( + ('[' >> additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] >> ']' >> '=' >> additive_expression(_r1)) + [px::bind(&MyContext::vector_variable_assign, _r1, _a, _b, _2, _val)] + | ('=' >> additive_expression(_r1)) + [px::bind(&MyContext::scalar_variable_assign, _r1, _a, _1, _val)] + ); + struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) { out.it_range = boost::iterator_range(start_pos, start_pos); } @@ -1430,6 +1540,7 @@ namespace client qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> is_nil_test; qi::rule, spirit_encoding::space_type> if_else_output; + qi::rule, int>, spirit_encoding::space_type> assignment_statement; // qi::rule, bool, std::string>, spirit_encoding::space_type> switch_output; qi::symbols keywords; @@ -1461,12 +1572,13 @@ static std::string process_macro(const std::string &templ, client::MyContext &co return output; } -std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, ContextData *context_data) const +std::string PlaceholderParser::process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context_data) const { client::MyContext context; context.external_config = this->external_config(); context.config = &this->config(); context.config_override = config_override; + context.config_outputs = config_outputs; context.current_extruder_id = current_extruder_id; context.context_data = context_data; return process_macro(templ, context); diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index fc184be774..a3f0515586 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -55,8 +55,10 @@ public: // Fill in the template using a macro processing language. // Throws Slic3r::PlaceholderParserError on syntax or runtime error. - std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const; - + std::string process(const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override, DynamicConfig *config_outputs, ContextData *context) const; + std::string process(const std::string &templ, unsigned int current_extruder_id = 0, const DynamicConfig *config_override = nullptr, ContextData *context = nullptr) const + { return this->process(templ, current_extruder_id, config_override, nullptr /* config_outputs */, context); } + // Evaluate a boolean expression using the full expressive power of the PlaceholderParser boolean expression syntax. // Throws Slic3r::PlaceholderParserError on syntax or runtime error. static bool evaluate_boolean_expression(const std::string &templ, const DynamicConfig &config, const DynamicConfig *config_override = nullptr); diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 8ad6b243ff..5248e089a8 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -117,4 +117,17 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); } SECTION("enum expression") { REQUIRE(boolean_expression("gcode_flavor == \"marlin\"")); } + + SECTION("write to a scalar variable") { + DynamicConfig config_outputs; + config_outputs.set_key_value("writable_string", new ConfigOptionString()); + parser.process("{writable_string = \"Written\"}", 0, nullptr, &config_outputs, nullptr); + REQUIRE(parser.process("{writable_string}", 0, nullptr, &config_outputs, nullptr) == "Written"); + } + SECTION("write to a vector variable") { + DynamicConfig config_outputs; + config_outputs.set_key_value("writable_floats", new ConfigOptionFloats({ 0., 0., 0. })); + parser.process("{writable_floats[1] = 33}", 0, nullptr, &config_outputs, nullptr); + REQUIRE(config_outputs.opt_float("writable_floats", 1) == Approx(33.)); + } } From d152b67ce5512c598d6632ddba255a4c93c351a2 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 10:25:52 +0100 Subject: [PATCH 10/19] PlaceholderParser: Simplified the parser after introducing the writable variables. --- src/libslic3r/PlaceholderParser.cpp | 395 +++++++++++++--------------- 1 file changed, 187 insertions(+), 208 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index c26197d27a..cb0760d4df 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -173,7 +173,11 @@ namespace client OptWithPos(ConfigOptionConstPtr opt, boost::iterator_range it_range) : opt(opt), it_range(it_range) {} ConfigOptionConstPtr opt { nullptr }; bool writable { false }; + // -1 means it is a scalar variable, or it is a vector variable and index was not assigned yet or the whole vector is considered. + int index { -1 }; boost::iterator_range it_range; + + bool has_index() const { return index != -1; } }; template @@ -802,128 +806,127 @@ namespace client } template - static void scalar_variable_reference( + static void store_variable_index( const MyContext *ctx, - OptWithPos &opt, - expr &output) + OptWithPos &opt, + int index, + Iterator it_end, + OptWithPos &output) { - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); - switch (opt.opt->type()) { - case coFloat: output.set_d(opt.opt->getFloat()); break; - case coInt: output.set_i(opt.opt->getInt()); break; - case coString: output.set_s(static_cast(opt.opt)->value); break; - case coPercent: output.set_d(opt.opt->getFloat()); break; - case coEnum: - case coPoint: output.set_s(opt.opt->serialize()); break; - case coBool: output.set_b(opt.opt->getBool()); break; - case coFloatOrPercent: - { - std::string opt_key(opt.it_range.begin(), opt.it_range.end()); - if (boost::ends_with(opt_key, "extrusion_width")) { - // Extrusion width supports defaults and a complex graph of dependencies. - output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); - } else if (! static_cast(opt.opt)->percent) { - // Not a percent, just return the value. - output.set_d(opt.opt->getFloat()); - } else { - // Resolve dependencies using the "ratio_over" link to a parent value. - const ConfigOptionDef *opt_def = print_config_def.get(opt_key); - assert(opt_def != nullptr); - double v = opt.opt->getFloat() * 0.01; // percent to ratio - for (;;) { - const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); - if (opt_parent == nullptr) - ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); - if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { - // Extrusion width supports defaults and a complex graph of dependencies. - assert(opt_parent->type() == coFloatOrPercent); - v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); - break; - } - if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { - v *= opt_parent->getFloat(); - if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) - break; - v *= 0.01; // percent to ratio - } - // Continue one level up in the "ratio_over" hierarchy. - opt_def = print_config_def.get(opt_def->ratio_over); - assert(opt_def != nullptr); - } - output.set_d(v); - } - break; - } - default: - ctx->throw_exception("Unknown scalar variable type", opt.it_range); - } - output.it_range = opt.it_range; + if (! opt.opt->is_vector()) + ctx->throw_exception("Cannot index a scalar variable", opt.it_range); + if (index < 0) + ctx->throw_exception("Referencing a vector variable with a negative index", opt.it_range); + output = opt; + output.index = index; + output.it_range.end() = it_end; } template - static void vector_variable_reference( + static void variable_value( const MyContext *ctx, OptWithPos &opt, - int &index, - Iterator it_end, expr &output) { - if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); - const ConfigOptionVectorBase *vec = static_cast(opt.opt); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); - switch (opt.opt->type()) { - case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; - case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; - case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; - case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; - case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; - //case coEnums: output.set_s(opt.opt->vserialize()[idx]); break; - default: - ctx->throw_exception("Unknown vector variable type", opt.it_range); + if (opt.opt->is_vector()) { + if (! opt.has_index()) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + const ConfigOptionVectorBase *vec = static_cast(opt.opt); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + size_t idx = (opt.index < 0) ? 0 : (opt.index >= int(vec->size())) ? 0 : size_t(opt.index); + switch (opt.opt->type()) { + case coFloats: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coInts: output.set_i(static_cast(opt.opt)->values[idx]); break; + case coStrings: output.set_s(static_cast(opt.opt)->values[idx]); break; + case coPercents: output.set_d(static_cast(opt.opt)->values[idx]); break; + case coPoints: output.set_s(to_string(static_cast(opt.opt)->values[idx])); break; + case coBools: output.set_b(static_cast(opt.opt)->values[idx] != 0); break; + //case coEnums: output.set_s(opt.opt->vserialize()[idx]); break; + default: + ctx->throw_exception("Unknown vector variable type", opt.it_range); + } + } else { + assert(opt.opt->is_scalar()); + switch (opt.opt->type()) { + case coFloat: output.set_d(opt.opt->getFloat()); break; + case coInt: output.set_i(opt.opt->getInt()); break; + case coString: output.set_s(static_cast(opt.opt)->value); break; + case coPercent: output.set_d(opt.opt->getFloat()); break; + case coEnum: + case coPoint: output.set_s(opt.opt->serialize()); break; + case coBool: output.set_b(opt.opt->getBool()); break; + case coFloatOrPercent: + { + std::string opt_key(opt.it_range.begin(), opt.it_range.end()); + if (boost::ends_with(opt_key, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); + } else if (! static_cast(opt.opt)->percent) { + // Not a percent, just return the value. + output.set_d(opt.opt->getFloat()); + } else { + // Resolve dependencies using the "ratio_over" link to a parent value. + const ConfigOptionDef *opt_def = print_config_def.get(opt_key); + assert(opt_def != nullptr); + double v = opt.opt->getFloat() * 0.01; // percent to ratio + for (;;) { + const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); + if (opt_parent == nullptr) + ctx->throw_exception("FloatOrPercent variable failed to resolve the \"ratio_over\" dependencies", opt.it_range); + if (boost::ends_with(opt_def->ratio_over, "extrusion_width")) { + // Extrusion width supports defaults and a complex graph of dependencies. + assert(opt_parent->type() == coFloatOrPercent); + v *= Flow::extrusion_width(opt_def->ratio_over, static_cast(opt_parent), *ctx, static_cast(ctx->current_extruder_id)); + break; + } + if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { + v *= opt_parent->getFloat(); + if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) + break; + v *= 0.01; // percent to ratio + } + // Continue one level up in the "ratio_over" hierarchy. + opt_def = print_config_def.get(opt_def->ratio_over); + assert(opt_def != nullptr); + } + output.set_d(v); + } + break; + } + default: + ctx->throw_exception("Unknown scalar variable type", opt.it_range); + } } - output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); + + output.it_range = opt.it_range; } // Return a boolean value, true if the scalar variable referenced by "opt" is nullable and it has a nil value. - template - static void is_nil_test_scalar( - const MyContext *ctx, - OptWithPos &opt, - expr &output) - { - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); - output.set_b(opt.opt->is_nil()); - output.it_range = opt.it_range; - } - // Return a boolean value, true if an element of a vector variable referenced by "opt[index]" is nullable and it has a nil value. template - static void is_nil_test_vector( + static void is_nil_test( const MyContext *ctx, OptWithPos &opt, - int &index, - Iterator it_end, expr &output) { - if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing a scalar variable when vector is expected", opt.it_range); - const ConfigOptionVectorBase *vec = static_cast(opt.opt); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - size_t idx = (index < 0) ? 0 : (index >= int(vec->size())) ? 0 : size_t(index); - output.set_b(static_cast(opt.opt)->is_nil(idx)); - output.it_range = boost::iterator_range(opt.it_range.begin(), it_end); + if (opt.opt->is_vector()) { + if (! opt.has_index()) + ctx->throw_exception("Referencing a vector variable when scalar is expected", opt.it_range); + const ConfigOptionVectorBase *vec = static_cast(opt.opt); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + output.set_b(static_cast(opt.opt)->is_nil(opt.index >= int(vec->size()) ? 0 : size_t(opt.index))); + } else { + assert(opt.opt->is_scalar()); + output.set_b(opt.opt->is_nil()); + } + output.it_range = opt.it_range; } // Decoding a scalar variable symbol "opt", assigning it a value of "param". template - static void scalar_variable_assign( + static void variable_assign( const MyContext *ctx, OptWithPos &opt, expr ¶m, @@ -932,83 +935,71 @@ namespace client { if (! opt.writable) ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); - if (opt.opt->is_vector()) - ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); - ConfigOption *wropt = const_cast(opt.opt); - switch (wropt->type()) { - case coFloat: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(wropt)->value = param.as_d(); - break; - case coInt: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(wropt)->value = param.as_i(); - break; - case coString: - static_cast(wropt)->value = param.to_string(); - break; - case coPercent: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(wropt)->value = param.as_d(); - break; - case coBool: - if (param.type() != expr::TYPE_BOOL) - ctx->throw_exception("Right side is not a boolean expression", param.it_range); - static_cast(wropt)->value = param.b(); - break; - default: - ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); - } - out.clear(); - } - - template - static void vector_variable_assign( - const MyContext *ctx, - OptWithPos &opt, - int &index, - expr ¶m, - // Not used, just clear it. - std::string &out) - { - if (! opt.writable) - ctx->throw_exception("Cannot modify a read-only variable", opt.it_range); - if (opt.opt->is_scalar()) - ctx->throw_exception("Referencing an output scalar variable when vector is expected", opt.it_range); - ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); - if (vec->empty()) - ctx->throw_exception("Indexing an empty vector variable", opt.it_range); - if (index < 0 || index >= int(vec->size())) - ctx->throw_exception("Index out of range", opt.it_range); - switch (opt.opt->type()) { - case coFloats: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(vec)->values[index] = param.as_d(); - break; - case coInts: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(vec)->values[index] = param.as_i(); - break; - case coStrings: - static_cast(vec)->values[index] = param.to_string(); - break; - case coPercents: - if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) - ctx->throw_exception("Right side is not a numeric expression", param.it_range); - static_cast(vec)->values[index] = param.as_d(); - break; - case coBools: - if (param.type() != expr::TYPE_BOOL) - ctx->throw_exception("Right side is not a boolean expression", param.it_range); - static_cast(vec)->values[index] = param.b(); - break; - default: - ctx->throw_exception("Unsupported output vector variable type", opt.it_range); + if (opt.opt->is_vector()) { + if (! opt.has_index()) + ctx->throw_exception("Referencing an output vector variable when scalar is expected", opt.it_range); + ConfigOptionVectorBase *vec = const_cast(static_cast(opt.opt)); + if (vec->empty()) + ctx->throw_exception("Indexing an empty vector variable", opt.it_range); + if (opt.index >= int(vec->size())) + ctx->throw_exception("Index out of range", opt.it_range); + switch (opt.opt->type()) { + case coFloats: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[opt.index] = param.as_d(); + break; + case coInts: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[opt.index] = param.as_i(); + break; + case coStrings: + static_cast(vec)->values[opt.index] = param.to_string(); + break; + case coPercents: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(vec)->values[opt.index] = param.as_d(); + break; + case coBools: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(vec)->values[opt.index] = param.b(); + break; + default: + ctx->throw_exception("Unsupported output vector variable type", opt.it_range); + } + } else { + assert(opt.opt->is_scalar()); + ConfigOption *wropt = const_cast(opt.opt); + switch (wropt->type()) { + case coFloat: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coInt: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_i(); + break; + case coString: + static_cast(wropt)->value = param.to_string(); + break; + case coPercent: + if (param.type() != expr::TYPE_INT && param.type() != expr::TYPE_DOUBLE) + ctx->throw_exception("Right side is not a numeric expression", param.it_range); + static_cast(wropt)->value = param.as_d(); + break; + case coBool: + if (param.type() != expr::TYPE_BOOL) + ctx->throw_exception("Right side is not a boolean expression", param.it_range); + static_cast(wropt)->value = param.b(); + break; + default: + ctx->throw_exception("Unsupported output scalar variable type", opt.it_range); + } } out.clear(); } @@ -1110,9 +1101,9 @@ namespace client { "multiplicative_expression", "Expecting an expression." }, { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, - { "scalar_variable_reference", "Expecting a scalar variable reference."}, - { "is_nil_test", "Expecting a scalar variable reference."}, { "variable_reference", "Expecting a variable reference."}, + { "is_nil_test", "Expecting a scalar variable reference."}, + { "variable", "Expecting a variable name."}, { "regular_expression", "Expecting a regular expression."} }; @@ -1359,13 +1350,8 @@ namespace client multiplicative_expression.name("multiplicative_expression"); assignment_statement = - variable_reference(_r1)[_a = _1] >> - ( - ('[' >> additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] >> ']' >> '=' >> additive_expression(_r1)) - [px::bind(&MyContext::vector_variable_assign, _r1, _a, _b, _2, _val)] - | ('=' >> additive_expression(_r1)) - [px::bind(&MyContext::scalar_variable_assign, _r1, _a, _1, _val)] - ); + (variable_reference(_r1) >> '=' > additive_expression(_r1)) + [px::bind(&MyContext::variable_assign, _r1, _1, _2, _val)]; struct FactorActions { static void set_start_pos(Iterator &start_pos, expr &out) @@ -1392,7 +1378,7 @@ namespace client static void noexpr(expr &out) { out.reset(); } }; unary_expression = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( - scalar_variable_reference(_r1) [ _val = _1 ] + variable_reference(_r1) [px::bind(&MyContext::variable_value, _r1, _1, _val)] | (lit('(') > conditional_expression(_r1) > ')' > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] | (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ] | (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ] @@ -1424,27 +1410,20 @@ namespace client ); optional_parameter.name("optional_parameter"); - scalar_variable_reference = - variable_reference(_r1)[_a=_1] >> - ( - ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > - iter_pos[px::bind(&MyContext::vector_variable_reference, _r1, _a, _b, _1, _val)]) - | eps[px::bind(&MyContext::scalar_variable_reference, _r1, _a, _val)] - ); - scalar_variable_reference.name("scalar variable reference"); + is_nil_test = variable_reference(_r1)[px::bind(&MyContext::is_nil_test, _r1, _1, _val)]; + is_nil_test.name("is_nil test"); - variable_reference = identifier - [ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + variable_reference = + variable(_r1)[_a=_1] >> + ( + ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > iter_pos) + [px::bind(&MyContext::store_variable_index, _r1, _a, _b, _2, _val)] + | eps[_val=_a] + ); variable_reference.name("variable reference"); - is_nil_test = - variable_reference(_r1)[_a=_1] >> - ( - ('[' > additive_expression(_r1)[px::bind(&MyContext::evaluate_index, _1, _b)] > ']' > - iter_pos[px::bind(&MyContext::is_nil_test_vector, _r1, _a, _b, _1, _val)]) - | eps[px::bind(&MyContext::is_nil_test_scalar, _r1, _a, _val)] - ); - is_nil_test.name("is_nil test"); + variable = identifier[ px::bind(&MyContext::resolve_variable, _r1, _1, _val) ]; + variable.name("variable reference"); regular_expression = raw[lexeme['/' > *((utf8char - char_('\\') - char_('/')) | ('\\' > char_)) > '/']]; regular_expression.name("regular_expression"); @@ -1488,8 +1467,8 @@ namespace client debug(multiplicative_expression); debug(unary_expression); debug(optional_parameter); - debug(scalar_variable_reference); debug(variable_reference); + debug(variable); debug(is_nil_test); debug(regular_expression); } @@ -1533,11 +1512,11 @@ namespace client // Evaluate boolean expression into bool. qi::rule bool_expr_eval; // Reference of a scalar variable, or reference to a field of a vector variable. - qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> scalar_variable_reference; + qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> variable_reference; // Rule to translate an identifier to a ConfigOption, or to fail. - qi::rule(const MyContext*), spirit_encoding::space_type> variable_reference; + qi::rule(const MyContext*), spirit_encoding::space_type> variable; // Evaluating whether a nullable variable is nil. - qi::rule(const MyContext*), qi::locals, int>, spirit_encoding::space_type> is_nil_test; + qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; From 5ab9532e394238b9a580b8c10fe52abcec7b439e Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 13:45:41 +0100 Subject: [PATCH 11/19] Fix bad actualization without mesh source sorting --- src/slic3r/Utils/RaycastManager.cpp | 211 +++++++++++++++------------- 1 file changed, 110 insertions(+), 101 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 80a7167554..62722b0769 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -1,25 +1,31 @@ #include "RaycastManager.hpp" #include +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/Camera.hpp" +#include "slic3r/GUI/CameraUtils.hpp" + using namespace Slic3r::GUI; -namespace priv { +namespace{ using namespace Slic3r; -static void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); -static const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); -static RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ +void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes *input = nullptr); +const AABBMesh * get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id); +RaycastManager::TrKey create_key(const ModelVolume& volume, const ModelInstance& instance){ return std::make_pair(instance.id().id, volume.id().id); } -static RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); -static bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { +RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key); +bool is_lower_key(const RaycastManager::TrKey &k1, const RaycastManager::TrKey &k2) { return k1.first < k2.first || (k1.first == k2.first && k1.second < k2.second); } -static bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { +bool is_lower(const RaycastManager::TrItem &i1, const RaycastManager::TrItem &i2) { return is_lower_key(i1.first, i2.first); }; +template inline void erase(std::vector &vec, const std::vector &flags); } void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Meshes *meshes) { // actualize MeshRaycaster - priv::actualize(m_meshes, object.volumes, skip, meshes); + ::actualize(m_meshes, object.volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -32,8 +38,8 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes for (const ModelInstance *instance : object.instances) { const Transform3d &instrance_tr = instance->get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(*volume, *instance); - auto item = priv::find(m_transformations, key); + TrKey key = ::create_key(*volume, *instance); + auto item = ::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -41,19 +47,17 @@ void RaycastManager::actualize(const ModelObject &object, const ISkip *skip, Mes removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(key, transformation)); + m_transformations.emplace_back(key, transformation); need_sort = true; } } } // clean other transformation - for (int i = removed_transf.size() - 1; i >= 0; --i) - if (removed_transf[i]) - m_transformations.erase(m_transformations.begin() + i); + ::erase(m_transformations, removed_transf); if (need_sort) - std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); + std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower); } void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, Meshes *meshes) @@ -61,7 +65,7 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, const ModelVolumePtrs &volumes = instance.get_object()->volumes; // actualize MeshRaycaster - priv::actualize(m_meshes, volumes, skip, meshes); + ::actualize(m_meshes, volumes, skip, meshes); // check if inscance was removed std::vector removed_transf(m_transformations.size(), {true}); @@ -74,8 +78,8 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, const Transform3d &volume_tr = volume->get_matrix(); const Transform3d &instrance_tr = instance.get_matrix(); Transform3d transformation = instrance_tr * volume_tr; - TrKey key = priv::create_key(*volume, instance); - auto item = priv::find(m_transformations, key); + TrKey key = ::create_key(*volume, instance); + auto item = ::find(m_transformations, key); if (item != m_transformations.end()) { // actualize transformation all the time item->second = transformation; @@ -83,40 +87,35 @@ void RaycastManager::actualize(const ModelInstance &instance, const ISkip *skip, removed_transf[index] = false; } else { // add new transformation - m_transformations.emplace_back(std::make_pair(key, transformation)); + m_transformations.emplace_back(key, transformation); need_sort = true; } } // clean other transformation - for (int i = removed_transf.size() - 1; i >= 0; --i) - if (removed_transf[i]) - m_transformations.erase(m_transformations.begin() + i); + ::erase(m_transformations, removed_transf); if (need_sort) - std::sort(m_transformations.begin(), m_transformations.end(), priv::is_lower); + std::sort(m_transformations.begin(), m_transformations.end(), ::is_lower); } std::optional RaycastManager::first_hit(const Vec3d& point, const Vec3d& direction, const ISkip *skip) const { // Improve: it is not neccessaru to use AABBMesh and calc normal for every hit - struct Result - { - const AABBMesh *mesh = nullptr; - double squared_distance; - int face; - Vec3d hit_world; - const Transform3d *tramsformation; - const TrKey *key; - }result; + + // Results + const AABBMesh *hit_mesh = nullptr; + double hit_squared_distance = 0.; + int hit_face = -1; + Vec3d hit_world; + const Transform3d *hit_tramsformation = nullptr; + const TrKey *hit_key = nullptr; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation]: m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; - const Transform3d &transformation = item.second; Transform3d inv = transformation.inverse(); // transform input into mesh world @@ -129,46 +128,44 @@ std::optional RaycastManager::first_hit(const Vec3d& point, const AABBMesh::hit_result &hit = hits.front(); // convert to world - Vec3d hit_world = transformation * hit.position(); - double squared_distance = (point - hit_world).squaredNorm(); - if (result.mesh != nullptr && - result.squared_distance < squared_distance) + Vec3d world = transformation * hit.position(); + double squared_distance = (point - world).squaredNorm(); + if (hit_mesh != nullptr && + hit_squared_distance < squared_distance) continue; // exist closer one - result.mesh = mesh; - result.squared_distance = squared_distance; - result.face = hit.face(); - result.hit_world = hit_world; - result.tramsformation = &transformation; - result.key = &key; + hit_mesh = mesh; + hit_squared_distance = squared_distance; + hit_face = hit.face(); + hit_world = world; + hit_tramsformation = &transformation; + hit_key = &key; } - if (result.mesh == nullptr) + if (hit_mesh == nullptr) return {}; // Calculate normal from transformed triangle // NOTE: Anisotropic transformation of normal is not perpendiculat to triangle - const Vec3i tri = result.mesh->indices(result.face); - Vec3d pts[3]; - auto tr = result.tramsformation->linear(); + const Vec3i tri = hit_mesh->indices(hit_face); + std::array pts; + auto tr = hit_tramsformation->linear(); for (int i = 0; i < 3; ++i) - pts[i] = tr * result.mesh->vertices(tri[i]).cast(); + pts[i] = tr * hit_mesh->vertices(tri[i]).cast(); Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); normal_world.normalize(); - SurfacePoint point_world{result.hit_world, normal_world}; - return RaycastManager::Hit{point_world, *result.key, result.squared_distance}; + SurfacePoint point_world{hit_world, normal_world}; + return RaycastManager::Hit{point_world, *hit_key, hit_squared_distance}; } std::optional RaycastManager::closest_hit(const Vec3d &point, const Vec3d &direction, const ISkip *skip) const { std::optional closest; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation] : m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const Transform3d &transformation = item.second; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; @@ -196,14 +193,12 @@ std::optional RaycastManager::closest_hit(const Vec3d &poin std::optional RaycastManager::closest(const Vec3d &point, const ISkip *skip) const { std::optional closest; - for (const auto &item : m_transformations) { - const TrKey &key = item.first; + for (const auto &[key, transformation] : m_transformations) { size_t volume_id = key.second; if (skip != nullptr && skip->skip(volume_id)) continue; - const AABBMesh *mesh = priv::get_mesh(m_meshes, volume_id); + const AABBMesh *mesh = ::get_mesh(m_meshes, volume_id); if (mesh == nullptr) continue; - const Transform3d &transformation = item.second; Transform3d tr_inv = transformation.inverse(); Vec3d mesh_point = tr_inv * point; @@ -222,17 +217,15 @@ std::optional RaycastManager::closest(const Vec3d &p } Slic3r::Transform3d RaycastManager::get_transformation(const TrKey &tr_key) const { - // TODO: transformations are sorted use lower bound - auto item = std::find_if(m_transformations.begin(), - m_transformations.end(), - [&tr_key](const TrItem &it) -> bool { - return it.first == tr_key; - }); - if (item == m_transformations.end()) return Transform3d::Identity(); - return item->second; + auto tr = ::find(m_transformations, tr_key); + if (tr == m_transformations.end()) + return Transform3d::Identity(); + return tr->second; } -void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) + +namespace { +void actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volumes, const RaycastManager::ISkip *skip, RaycastManager::Meshes* inputs) { // check if volume was removed std::vector removed_meshes(meshes.size(), {true}); @@ -242,33 +235,33 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu size_t oid = volume->id().id; if (skip != nullptr && skip->skip(oid)) continue; - auto item = std::find_if(meshes.begin(), meshes.end(), [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); - if (item == meshes.end()) { - // exist AABB in inputs ? - if (inputs != nullptr) { - auto input = std::find_if(inputs->begin(), inputs->end(), - [oid](const RaycastManager::Mesh &it) -> bool { return oid == it.first; }); - if (input != inputs->end()) { - meshes.emplace_back(std::move(*input)); - continue; - } - } - - // add new raycaster - bool calculate_epsilon = true; - auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); - meshes.emplace_back(std::make_pair(oid, std::move(mesh))); - need_sort = true; - } else { + auto is_oid = [oid](const RaycastManager::Mesh &it) { return oid == it.first; }; + if (auto item = std::find_if(meshes.begin(), meshes.end(), is_oid); + item != meshes.end()) { size_t index = item - meshes.begin(); removed_meshes[index] = false; + continue; } + + // exist AABB in inputs ? + if (inputs != nullptr) { + auto input = std::find_if(inputs->begin(), inputs->end(), is_oid); + if (input != inputs->end()) { + meshes.emplace_back(std::move(*input)); + need_sort = true; + continue; + } + } + + // add new raycaster + bool calculate_epsilon = true; + auto mesh = std::make_unique(volume->mesh(), calculate_epsilon); + meshes.emplace_back(std::make_pair(oid, std::move(mesh))); + need_sort = true; } // clean other raycasters - for (int i = removed_meshes.size() - 1; i >= 0; --i) - if (removed_meshes[i]) - meshes.erase(meshes.begin() + i); + erase(meshes, removed_meshes); // All the time meshes must be sorted by volume id - for faster search if (need_sort) { @@ -277,28 +270,44 @@ void priv::actualize(RaycastManager::Meshes &meshes, const ModelVolumePtrs &volu } } -const Slic3r::AABBMesh *priv::get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) +const Slic3r::AABBMesh *get_mesh(const RaycastManager::Meshes &meshes, size_t volume_id) { - auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) -> bool { return m.first < i; }; + auto is_lower_index = [](const RaycastManager::Mesh &m, size_t i) { return m.first < i; }; auto it = std::lower_bound(meshes.begin(), meshes.end(), volume_id, is_lower_index); if (it == meshes.end() || it->first != volume_id) return nullptr; return &(*(it->second)); } -RaycastManager::TrItems::iterator priv::find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { - auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &key)->bool { - return priv::is_lower_key(it.first, key); - }; +RaycastManager::TrItems::iterator find(RaycastManager::TrItems &items, const RaycastManager::TrKey &key) { + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); }; auto it = std::lower_bound(items.begin(), items.end(), key, fnc); - if (it == items.end() || it->first != key) + if (it != items.end() && it->first != key) return items.end(); return it; } -#include "slic3r/GUI/GLCanvas3D.hpp" -#include "slic3r/GUI/Camera.hpp" -#include "slic3r/GUI/CameraUtils.hpp" +RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &items, const RaycastManager::TrKey &key) +{ + auto fnc = [](const RaycastManager::TrItem &it, const RaycastManager::TrKey &l_key) { return is_lower_key(it.first, l_key); }; + auto it = std::lower_bound(items.begin(), items.end(), key, fnc); + if (it != items.end() && it->first != key) + return items.end(); + return it; +} + +template inline void erase(std::vector &vec, const std::vector &flags) +{ + assert(vec.size() == flags.size()); + if (flags.empty()) return; + + // reverse iteration over flags to erase indices from back to front. + for (int i = static_cast(flags.size()) - 1; i >= 0; --i) + if (flags[i]) + vec.erase(vec.begin() + i); +} + +} // namespace namespace Slic3r::GUI{ From 201e9d493f1607c8e8162ab541f52fc2f207e797 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:32:21 +0100 Subject: [PATCH 12/19] Remove side effects from setter for enable picking. --- src/slic3r/GUI/GLCanvas3D.cpp | 1 - src/slic3r/GUI/GUI_Preview.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f95d748ba0..e369e0f400 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1407,7 +1407,6 @@ void GLCanvas3D::enable_legend_texture(bool enable) void GLCanvas3D::enable_picking(bool enable) { m_picking_enabled = enable; - m_selection.set_mode(Selection::Instance); } void GLCanvas3D::enable_moving(bool enable) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index d9dfc7db22..656bc0b538 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -65,6 +65,7 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig m_canvas->allow_multisample(OpenGLManager::can_multisample()); m_canvas->enable_picking(true); + m_canvas->get_selection().set_mode(Selection::Instance); m_canvas->enable_moving(true); // XXX: more config from 3D.pm m_canvas->set_model(model); From d24472675c938986d0dc31ccc7edb6fa77e47cb0 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 15:33:00 +0100 Subject: [PATCH 13/19] Keep color of object during drag over surface. --- src/slic3r/GUI/SurfaceDrag.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/SurfaceDrag.cpp b/src/slic3r/GUI/SurfaceDrag.cpp index 4a48ced29f..ecee0a4e06 100644 --- a/src/slic3r/GUI/SurfaceDrag.cpp +++ b/src/slic3r/GUI/SurfaceDrag.cpp @@ -80,6 +80,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // allow moving with object again canvas.enable_moving(true); + canvas.enable_picking(true); surface_drag.reset(); // only left up is correct @@ -167,6 +168,7 @@ bool on_mouse_surface_drag(const wxMouseEvent &mouse_event, // disable moving with object by mouse canvas.enable_moving(false); + canvas.enable_picking(false); return true; } From 7afabcde959640b90e376752a334495ce5d76dc6 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Mon, 20 Mar 2023 15:55:18 +0100 Subject: [PATCH 14/19] PlaceholderParser: Implemented one_of() matching function: 1st parameter is the text to match against, the rest of the parameters are pattern to be matched: either strings, then the match is exact, or regex enclosed in // or regex string starting with ~ For example one_of("a", "a", "b") finds a in "a", "b" one_of("abc", /.*a.*/) matches "abc" using regular expression /.*a.*/ --- src/libslic3r/PlaceholderParser.cpp | 62 +++++++++++++++++++-- tests/libslic3r/test_placeholder_parser.cpp | 12 ++++ 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index cb0760d4df..5f8624ad30 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -587,7 +587,7 @@ namespace client param1.set_s(buf); } - static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) + static void regex_op(const expr &lhs, boost::iterator_range &rhs, char op, expr &out) { const std::string *subject = nullptr; if (lhs.type() == TYPE_STRING) { @@ -601,7 +601,7 @@ namespace client bool result = SLIC3R_REGEX_NAMESPACE::regex_match(*subject, SLIC3R_REGEX_NAMESPACE::regex(pattern)); if (op == '!') result = ! result; - lhs.set_b(result); + out.set_b(result); } catch (SLIC3R_REGEX_NAMESPACE::regex_error &ex) { // Syntax error in the regular expression boost::throw_exception(qi::expectation_failure( @@ -609,8 +609,37 @@ namespace client } } - static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '='); } - static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!'); } + static void regex_matches (expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '=', lhs); } + static void regex_doesnt_match(expr &lhs, boost::iterator_range &rhs) { return regex_op(lhs, rhs, '!', lhs); } + + static void one_of_test_init(expr &out) { + out.set_b(false); + } + template + static void one_of_test(const expr &match, const expr &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + if (pattern.type() != TYPE_STRING) + match.throw_exception("one_of(): Pattern has to be a string value"); + if (RegEx) { + try { + out.set_b(SLIC3R_REGEX_NAMESPACE::regex_match(match.s(), SLIC3R_REGEX_NAMESPACE::regex(pattern.s()))); + } catch (SLIC3R_REGEX_NAMESPACE::regex_error &) { + // Syntax error in the regular expression + pattern.throw_exception("Regular expression compilation failed"); + } + } else + out.set_b(match.s() == pattern.s()); + } + } + static void one_of_test_regex(const expr &match, boost::iterator_range &pattern, expr &out) { + if (! out.b()) { + if (match.type() != TYPE_STRING) + match.throw_exception("one_of(): First parameter (the string to match against) has to be a string value"); + regex_op(match, pattern, '=', out); + } + } static void logical_op(expr &lhs, expr &rhs, char op) { @@ -1101,6 +1130,7 @@ namespace client { "multiplicative_expression", "Expecting an expression." }, { "unary_expression", "Expecting an expression." }, { "optional_parameter", "Expecting a closing brace or an optional parameter." }, + { "one_of_list", "Expecting a list of string patterns (simple text or rexep)" }, { "variable_reference", "Expecting a variable reference."}, { "is_nil_test", "Expecting a scalar variable reference."}, { "variable", "Expecting a variable name."}, @@ -1221,6 +1251,7 @@ namespace client qi::_a_type _a; qi::_b_type _b; qi::_r1_type _r1; + qi::_r2_type _r2; // Starting symbol of the grammer. // The leading eps is required by the "expectation point" operator ">". @@ -1395,7 +1426,8 @@ namespace client [ px::bind(&expr::template digits, _val, _2, _3) ] | (kw["int"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::to_int, _1, _val) ] | (kw["round"] > '(' > conditional_expression(_r1) > ')') [ px::bind(&FactorActions::round, _1, _val) ] - | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [_val = _1] + | (kw["is_nil"] > '(' > is_nil_test(_r1) > ')') [ _val = _1 ] + | (kw["one_of"] > '(' > one_of(_r1) > ')') [ _val = _1 ] | (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ] | (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ] | (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ] @@ -1404,6 +1436,20 @@ namespace client ); unary_expression.name("unary_expression"); + one_of = (unary_expression(_r1)[_a = _1] > one_of_list(_r1, _a))[_val = _2]; + one_of.name("one_of"); + one_of_list = + eps[px::bind(&expr::one_of_test_init, _val)] > + ( ',' > *( + ( + unary_expression(_r1)[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | (lit('~') > unary_expression(_r1))[px::bind(&expr::template one_of_test, _r2, _1, _val)] + | regular_expression[px::bind(&expr::one_of_test_regex, _r2, _1, _val)] + ) >> -lit(',')) + | eps + ); + one_of_list.name("one_of_list"); + optional_parameter = iter_pos[px::bind(&FactorActions::set_start_pos, _1, _val)] >> ( lit(')') [ px::bind(&FactorActions::noexpr, _val) ] | (lit(',') > conditional_expression(_r1) > ')') [ _val = _1 ] @@ -1445,6 +1491,7 @@ namespace client ("random") ("round") ("not") + ("one_of") ("or") ("true"); @@ -1466,6 +1513,8 @@ namespace client debug(additive_expression); debug(multiplicative_expression); debug(unary_expression); + debug(one_of); + debug(one_of_list); debug(optional_parameter); debug(variable_reference); debug(variable); @@ -1517,6 +1566,9 @@ namespace client qi::rule(const MyContext*), spirit_encoding::space_type> variable; // Evaluating whether a nullable variable is nil. qi::rule(const MyContext*), spirit_encoding::space_type> is_nil_test; + // Evaluating "one of" list of patterns. + qi::rule(const MyContext*), qi::locals>, spirit_encoding::space_type> one_of; + qi::rule(const MyContext*, const expr ¶m), spirit_encoding::space_type> one_of_list; qi::rule, spirit_encoding::space_type> if_else_output; qi::rule, int>, spirit_encoding::space_type> assignment_statement; diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index 5248e089a8..e40657d160 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -113,6 +113,18 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("boolean expression parser: greater than or equal - false") { REQUIRE(! boolean_expression("12 >= 22")); } SECTION("boolean expression parser: lower than or equal (same values) - true") { REQUIRE(boolean_expression("12 <= 12")); } SECTION("boolean expression parser: greater than or equal (same values) - true") { REQUIRE(boolean_expression("12 >= 12")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"b\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"c\", \"a\", \"b\", \"c\")") { REQUIRE(boolean_expression("one_of(\"c\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"d\", \"a\", \"b\", \"c\")") { REQUIRE(! boolean_expression("one_of(\"d\", \"a\", \"b\", \"c\")")); } + SECTION("boolean expression parser: one_of(\"a\")") { REQUIRE(! boolean_expression("one_of(\"a\")")); } + SECTION("boolean expression parser: one_of(\"a\", \"a\")") { REQUIRE(boolean_expression("one_of(\"a\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"b\", \"a\")") { REQUIRE(! boolean_expression("one_of(\"b\", \"a\")")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", /.*f.*/, /.*c.*/)") { REQUIRE(boolean_expression("one_of(\"abcdef\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(boolean_expression("one_of(\"abcdef\", ~\".*f.*\", ~\".*c.*\")")); } + SECTION("boolean expression parser: one_of(\"ghij\", /.*f.*/, /.*c.*/)") { REQUIRE(! boolean_expression("one_of(\"ghij\", /.*f.*/, /.*c.*/)")); } + SECTION("boolean expression parser: one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")") { REQUIRE(! boolean_expression("one_of(\"ghij\", ~\".*f.*\", ~\".*c.*\")")); } SECTION("complex expression") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_MK2.*/ and nozzle_diameter[0]==0.6 and num_extruders>1")); } SECTION("complex expression2") { REQUIRE(boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.6 and num_extruders>1)")); } SECTION("complex expression3") { REQUIRE(! boolean_expression("printer_notes=~/.*PRINTER_VEwerfNDOR_PRUSA3D.*/ or printer_notes=~/.*PRINTertER_MODEL_MK2.*/ or (nozzle_diameter[0]==0.3 and num_extruders>1)")); } From d653cc6ab5d6ec8c1c209444fef21c73e6d7bed7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 16:10:13 +0100 Subject: [PATCH 15/19] Fix crash made on MMU printers --- src/slic3r/Utils/RaycastManager.cpp | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index 62722b0769..f8f4442411 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -313,11 +313,14 @@ namespace Slic3r::GUI{ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::AllowVolumes &condition) { - SceneRaycaster::EType type = SceneRaycaster::EType::Volume; - auto scene_casters = canvas.get_raycasters_for_picking(type); - const std::vector> &casters = *scene_casters; - const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; - const ModelObjectPtrs &objects = canvas.get_model()->objects; + SceneRaycaster::EType type = SceneRaycaster::EType::Volume; + auto scene_casters = canvas.get_raycasters_for_picking(type); + if (scene_casters == nullptr) + return {}; + const std::vector> &casters = *scene_casters; + + const GLVolumePtrs &gl_volumes = canvas.get_volumes().volumes; + const ModelObjectPtrs &objects = canvas.get_model()->objects; RaycastManager::Meshes meshes; for (const std::shared_ptr &caster : casters) { @@ -327,9 +330,13 @@ RaycastManager::Meshes create_meshes(GLCanvas3D &canvas, const RaycastManager::A auto index_ = static_cast(index); if(index_ >= gl_volumes.size()) continue; - const GLVolume *gl_volume = gl_volumes[index_]; - const ModelVolume *volume = get_model_volume(*gl_volume, objects); - size_t id = volume->id().id; + const GLVolume *gl_volume = gl_volumes[index_]; + if (gl_volume == nullptr) + continue; + const ModelVolume *volume = get_model_volume(*gl_volume, objects); + if (volume == nullptr) + continue; + size_t id = volume->id().id; if (condition.skip(id)) continue; auto mesh = std::make_unique(caster->get_raycaster()->get_aabb_mesh()); From 9c70ed01e5f3345f7468c64add674dbf7ba93590 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 20 Mar 2023 17:41:51 +0100 Subject: [PATCH 16/19] Fix add emboss volume on reflected --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 7 ++----- src/slic3r/Utils/RaycastManager.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index bc0b5e8110..695399ab20 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -3462,15 +3462,12 @@ bool priv::start_create_volume_on_surface_job( if (!hit.has_value()) return false; - // priv::reset_skew(hit_to_world); - Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); - // Create result volume transformation Transform3d surface_trmat = create_transformation_onto_surface(hit->position, hit->normal, priv::up_limit); const FontProp &font_prop = emboss_data.text_configuration.style.prop; apply_transformation(font_prop, surface_trmat); - // new transformation in world coor is surface_trmat - Transform3d volume_trmat = instance.inverse() * surface_trmat; + Transform3d instance = gl_volume->get_instance_transformation().get_matrix(); + Transform3d volume_trmat = instance.inverse() * surface_trmat; start_create_volume_job(obj, volume_trmat, emboss_data, volume_type); return true; } diff --git a/src/slic3r/Utils/RaycastManager.cpp b/src/slic3r/Utils/RaycastManager.cpp index f8f4442411..b2b7588555 100644 --- a/src/slic3r/Utils/RaycastManager.cpp +++ b/src/slic3r/Utils/RaycastManager.cpp @@ -153,6 +153,8 @@ std::optional RaycastManager::first_hit(const Vec3d& point, for (int i = 0; i < 3; ++i) pts[i] = tr * hit_mesh->vertices(tri[i]).cast(); Vec3d normal_world = (pts[1] - pts[0]).cross(pts[2] - pts[1]); + if (has_reflection(*hit_tramsformation)) + normal_world *= -1; normal_world.normalize(); SurfacePoint point_world{hit_world, normal_world}; @@ -298,8 +300,8 @@ RaycastManager::TrItems::const_iterator find(const RaycastManager::TrItems &item template inline void erase(std::vector &vec, const std::vector &flags) { - assert(vec.size() == flags.size()); - if (flags.empty()) return; + if (vec.size() < flags.size() || flags.empty()) + return; // reverse iteration over flags to erase indices from back to front. for (int i = static_cast(flags.size()) - 1; i >= 0; --i) From ff5767b6f58e1464f5426536e1e4ce872fe5dc6e Mon Sep 17 00:00:00 2001 From: Pavel Mikus Date: Mon, 20 Mar 2023 20:01:37 +0100 Subject: [PATCH 17/19] enable AABB tree over 3D lines --- src/libslic3r/AABBTreeLines.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/AABBTreeLines.hpp b/src/libslic3r/AABBTreeLines.hpp index 21678bfcdf..2136e8edbd 100644 --- a/src/libslic3r/AABBTreeLines.hpp +++ b/src/libslic3r/AABBTreeLines.hpp @@ -165,9 +165,9 @@ inline std::vector> get_intersections_with_line(si // Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies // during tree traversal. template -inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector &lines) +inline AABBTreeIndirect::Tree build_aabb_tree_over_indexed_lines(const std::vector &lines) { - using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>; + using TreeType = AABBTreeIndirect::Tree; // using CoordType = typename TreeType::CoordType; using VectorType = typename TreeType::VectorType; using BoundingBox = typename TreeType::BoundingBox; From ae016aceb9aee77749e7aa6d74525e7e47ec0ee7 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 21 Mar 2023 12:11:09 +0100 Subject: [PATCH 18/19] Fix for selection of unknodn font --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 224 ++++++++++-------------- src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp | 3 +- 2 files changed, 91 insertions(+), 136 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 695399ab20..dd18cb878a 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -53,7 +53,6 @@ #define SHOW_IMGUI_ATLAS #define SHOW_ICONS_TEXTURE #define SHOW_FINE_POSITION // draw convex hull around volume -#define SHOW_WX_WEIGHT_INPUT #define DRAW_PLACE_TO_ADD_TEXT // Interactive draw of window position #define ALLOW_OPEN_NEAR_VOLUME #define EXECUTE_PROCESS_ON_MAIN_THREAD // debug execution on main thread @@ -531,6 +530,12 @@ void GLGizmoEmboss::on_render_input_window(float x, float y, float bottom_limit) return; } + // Not known situation when could happend this is only for sure + if (!m_is_unknown_font && !m_style_manager.is_active_font()) + create_notification_not_valid_font("No active font in style. Select correct one."); + else if (!m_is_unknown_font && !m_style_manager.get_wx_font().IsOk()) + create_notification_not_valid_font("WxFont is not loaded properly."); + // Configuration creation double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); float main_toolbar_height = m_parent.get_main_toolbar_height(); @@ -1192,21 +1197,24 @@ void GLGizmoEmboss::draw_window() if (ImGui::Button("add svg")) choose_svg_file(); #endif // ALLOW_DEBUG_MODE - bool is_active_font = m_style_manager.is_active_font(); - if (!is_active_font) - m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Warning: No font is selected. Select correct one.")); - + // Setter of indent must be befor disable !!! + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); + ScopeGuard indent_sc([](){ ImGui::PopStyleVar(/*ImGuiStyleVar_IndentSpacing*/); }); + // Disable all except selection of font, when open text from 3mf with unknown font m_imgui->disabled_begin(m_is_unknown_font); - ScopeGuard unknown_font_sc([&]() { - m_imgui->disabled_end(); - }); - draw_text_input(); - m_imgui->disabled_begin(!is_active_font); + ScopeGuard unknown_font_sc([imgui = m_imgui]() { imgui->disabled_end(/*m_is_unknown_font*/); }); + + draw_text_input(); - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, m_gui_cfg->indent); ImGui::Indent(); - draw_style_edit(); + // When unknown font is inside .3mf only font selection is allowed + m_imgui->disabled_end(/*m_is_unknown_font*/); + draw_font_list_line(); + m_imgui->disabled_begin(m_is_unknown_font); + bool use_inch = wxGetApp().app_config->get_bool("use_inches"); + draw_height(use_inch); + draw_depth(use_inch); ImGui::Unindent(); // close advanced style property when unknown font is selected @@ -1223,8 +1231,6 @@ void GLGizmoEmboss::draw_window() } else if (m_is_advanced_edit_style) set_minimal_window_size(false); - ImGui::PopStyleVar(); // ImGuiStyleVar_IndentSpacing - ImGui::Separator(); draw_style_list(); @@ -1234,8 +1240,6 @@ void GLGizmoEmboss::draw_window() ImGui::Separator(); draw_model_type(); } - - m_imgui->disabled_end(); // !is_active_font #ifdef SHOW_WX_FONT_DESCRIPTOR if (is_selected_style) @@ -1728,6 +1732,59 @@ bool GLGizmoEmboss::select_facename(const wxString &facename) return true; } +void GLGizmoEmboss::draw_font_list_line() +{ + bool exist_stored_style = m_style_manager.exist_stored_style(); + bool exist_change_in_font = m_style_manager.is_font_changed(); + const std::string& font_text = m_gui_cfg->translations.font; + if (exist_change_in_font || !exist_stored_style) + ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, font_text); + else + ImGuiWrapper::text(font_text); + + ImGui::SameLine(m_gui_cfg->input_offset); + + draw_font_list(); + + bool exist_change = false; + if (!m_is_unknown_font) { + ImGui::SameLine(); + if (draw_italic_button()) + exist_change = true; + ImGui::SameLine(); + if (draw_bold_button()) + exist_change = true; + } else { + // when exist unknown font add confirmation button + ImGui::SameLine(); + // Apply for actual selected font + if (ImGui::Button(_u8L("Apply").c_str())) + exist_change = true; + } + + EmbossStyle &style = m_style_manager.get_style(); + if (exist_change_in_font) { + ImGui::SameLine(ImGui::GetStyle().FramePadding.x); + if (draw_button(m_icons, IconType::undo)) { + const EmbossStyle *stored_style = m_style_manager.get_stored_style(); + + style.path = stored_style->path; + style.prop.boldness = stored_style->prop.boldness; + style.prop.skew = stored_style->prop.skew; + + wxFont new_wx_font = WxFontUtils::load_wxFont(style.path); + if (new_wx_font.IsOk() && m_style_manager.set_wx_font(new_wx_font)) + exist_change = true; + } else if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str()); + } + + if (exist_change) { + m_style_manager.clear_glyphs_cache(); + process(); + } +} + void GLGizmoEmboss::draw_font_list() { // Set partial @@ -1745,16 +1802,6 @@ void GLGizmoEmboss::draw_font_list() // When deletation of font appear this variable is set std::optional del_index; - // When is unknown font is inside .3mf only font selection is allowed - // Stop Imgui disable + Guard again start disabling - ScopeGuard unknown_font_sc; - if (m_is_unknown_font) { - m_imgui->disabled_end(); - unknown_font_sc.closure = [&]() { - m_imgui->disabled_begin(true); - }; - } - // Code const char *popup_id = "##font_list_popup"; const char *input_id = "##font_list_input"; @@ -1881,13 +1928,6 @@ void GLGizmoEmboss::draw_font_list() store(m_face_names); } - if (m_is_unknown_font) { - ImGui::SameLine(); - // Apply for actual selected font - if (ImGui::Button(_u8L("Apply").c_str())) - process(); - } - #ifdef ALLOW_ADD_FONT_BY_FILE ImGui::SameLine(); // select font file by file browser @@ -2580,89 +2620,6 @@ bool GLGizmoEmboss::rev_checkbox(const std::string &name, undo_offset, draw_offseted_input); } -void GLGizmoEmboss::draw_style_edit() -{ - { - // Check correct WxFont - const wxFont &wx_font = m_style_manager.get_wx_font(); - assert(wx_font.IsOk()); - if (!wx_font.IsOk()) { - ImGui::TextColored(ImGuiWrapper::COL_ORANGE_DARK, "%s", _u8L("WxFont is not loaded properly.").c_str()); - return; - } - } - - bool exist_stored_style = m_style_manager.exist_stored_style(); - bool exist_change_in_font = m_style_manager.is_font_changed(); - const GuiCfg::Translations &tr = m_gui_cfg->translations; - if (exist_change_in_font || !exist_stored_style) - ImGuiWrapper::text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, tr.font); - else - ImGuiWrapper::text(tr.font); - ImGui::SameLine(m_gui_cfg->input_offset); - draw_font_list(); - bool exist_change = false; - if (!m_is_unknown_font) { - ImGui::SameLine(); - if (draw_italic_button()) - exist_change = true; - ImGui::SameLine(); - if (draw_bold_button()) - exist_change = true; - } - EmbossStyle &style = m_style_manager.get_style(); - if (exist_change_in_font) { - ImGui::SameLine(ImGui::GetStyle().FramePadding.x); - if (draw_button(m_icons, IconType::undo)) { - const EmbossStyle *stored_style = m_style_manager.get_stored_style(); - style.path = stored_style->path; - style.prop.boldness = stored_style->prop.boldness; - style.prop.skew = stored_style->prop.skew; - - wxFont new_wx_font = WxFontUtils::load_wxFont(style.path); - if (new_wx_font.IsOk() && - m_style_manager.set_wx_font(new_wx_font)) - exist_change = true; - } else if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("Revert font changes.").c_str()); - } - - if (exist_change) { - m_style_manager.clear_glyphs_cache(); - process(); - } - - bool use_inch = wxGetApp().app_config->get_bool("use_inches"); - draw_height(use_inch); - draw_depth(use_inch); - -#ifdef SHOW_WX_WEIGHT_INPUT - if (wx_font.has_value()) { - ImGui::Text("%s", "weight"); - ImGui::SameLine(m_gui_cfg->input_offset); - ImGui::SetNextItemWidth(m_gui_cfg->input_width); - int weight = wx_font->GetNumericWeight(); - int min_weight = 1, max_weight = 1000; - if (ImGui::SliderInt("##weight", &weight, min_weight, max_weight)) { - wx_font->SetNumericWeight(weight); - m_style_manager.wx_font_changed(); - process(); - } - - wxFont f = wx_font->Bold(); - bool disable = f == *wx_font; - ImGui::SameLine(); - if (draw_button(IconType::bold, disable)) { - *wx_font = f; - m_style_manager.wx_font_changed(); - process(); - } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", _u8L("wx Make bold").c_str()); - } -#endif // SHOW_WX_WEIGHT_INPUT -} - bool GLGizmoEmboss::set_height() { float &value = m_style_manager.get_style().prop.size_in_mm; @@ -3254,39 +3211,36 @@ bool GLGizmoEmboss::choose_svg_file() void GLGizmoEmboss::create_notification_not_valid_font( const TextConfiguration &tc) { - // not neccessary, but for sure that old notification doesnt exist - if (m_is_unknown_font) remove_notification_not_valid_font(); - m_is_unknown_font = true; - - auto type = NotificationType::UnknownFont; - auto level = - NotificationManager::NotificationLevel::WarningNotificationLevel; - const EmbossStyle &es = m_style_manager.get_style(); const auto &face_name_opt = es.prop.face_name; - const auto &face_name_3mf_opt = tc.style.prop.face_name; + const std::string &face_name_3mf = tc.style.prop.face_name.value_or(tc.style.path); - const std::string &face_name_3mf = face_name_3mf_opt.has_value() ? - *face_name_3mf_opt : - tc.style.path; - - std::string face_name_by_wx; + std::optional face_name_by_wx; if (!face_name_opt.has_value()) { const wxFont& wx_font = m_style_manager.get_wx_font(); if (wx_font.IsOk()) { wxString wx_face_name = wx_font.GetFaceName(); - face_name_by_wx = std::string((const char *) wx_face_name.ToUTF8()); + if (!wx_face_name.empty()) + face_name_by_wx = std::string(wx_face_name.ToUTF8().data()); } } - - const std::string &face_name = face_name_opt.has_value() ? *face_name_opt : - (!face_name_by_wx.empty() ? face_name_by_wx : es.path); - + const std::string &face_name = face_name_opt.value_or(face_name_by_wx.value_or(es.path)); std::string text = GUI::format(_L("Can't load exactly same font(\"%1%\"), " "Aplication selected a similar one(\"%2%\"). " "You have to specify font for enable edit text."), face_name_3mf, face_name); + create_notification_not_valid_font(text); +} + +void GLGizmoEmboss::create_notification_not_valid_font(const std::string &text) { + // not neccessary, but for sure that old notification doesnt exist + if (m_is_unknown_font) + remove_notification_not_valid_font(); + m_is_unknown_font = true; + + auto type = NotificationType::UnknownFont; + auto level = NotificationManager::NotificationLevel::WarningNotificationLevel; auto notification_manager = wxGetApp().plater()->get_notification_manager(); notification_manager->push_notification(type, level, text); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp index 730b7354ca..ebbdf616cc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp @@ -102,8 +102,8 @@ private: void init_font_name_texture(); struct FaceName; void draw_font_preview(FaceName &face, bool is_visible); + void draw_font_list_line(); void draw_font_list(); - void draw_style_edit(); void draw_height(bool use_inch); void draw_depth(bool use_inch); @@ -154,6 +154,7 @@ private: // When open text loaded from .3mf it could be written with unknown font bool m_is_unknown_font; void create_notification_not_valid_font(const TextConfiguration& tc); + void create_notification_not_valid_font(const std::string& text); void remove_notification_not_valid_font(); // This configs holds GUI layout size given by translated texts. From 4326929960e4fab5d6516e8fd9a0a73f26b4580d Mon Sep 17 00:00:00 2001 From: rtyr <36745189+rtyr@users.noreply.github.com> Date: Tue, 21 Mar 2023 12:36:23 +0100 Subject: [PATCH 19/19] Updated output filename format. --- resources/profiles/PrusaResearch.idx | 1 + resources/profiles/PrusaResearch.ini | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index 05970389cf..77fdf5a83b 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,4 +1,5 @@ min_slic3r_version = 2.6.0-alpha5 +1.9.0-alpha0 Updated output filename format. 1.7.0-alpha2 Updated compatibility condition in some filament profiles (Prusa XL). 1.7.0-alpha1 Added profiles for Original Prusa XL. Added filament profile for Prusament PETG Tungsten 75%. min_slic3r_version = 2.6.0-alpha1 diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 3fb3aee3d9..f57a8110d7 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.7.0-alpha2 +config_version = 1.9.0-alpha0 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/PrusaResearch/ changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1% @@ -192,7 +192,7 @@ notes = overhangs = 1 only_retract_when_crossing_perimeters = 0 ooze_prevention = 0 -output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode perimeters = 2 perimeter_extruder = 1 perimeter_extrusion_width = 0.45 @@ -390,7 +390,7 @@ support_material_xy_spacing = 80% support_material_interface_spacing = 0.2 support_material_spacing = 2.2 raft_first_layer_expansion = 2 -output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{initial_filament_type}_{printer_model}_{print_time}.gcode +output_filename_format = {input_filename_base}_{nozzle_diameter[0]}n_{layer_height}mm_{printing_filament_types}_{printer_model}_{print_time}.gcode infill_anchor = 2 infill_anchor_max = 15 thick_bridges = 0