mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-12 19:47:43 +00:00
Only serialize selected published settings. Minor cleanup
This commit is contained in:
@@ -5943,6 +5943,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
bool m_save_gcode { false }; // whether to save gcode for normal save
|
||||
bool m_skip_model { false }; // skip model when exporting .gcode.3mf
|
||||
bool m_skip_auxiliary { false }; // skip normal axuiliary files
|
||||
bool m_minimal_published { false }; // published 3MF: omit embedded preset files
|
||||
bool m_use_loaded_id { false }; // whether to use loaded id for identify_id
|
||||
bool m_share_mesh { false }; // whether to share mesh between objects
|
||||
std::string m_thumbnail_middle = PRINTER_THUMBNAIL_MIDDLE_FILE;
|
||||
@@ -6042,6 +6043,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
m_skip_auxiliary = store_params.strategy & SaveStrategy::SkipAuxiliary;
|
||||
m_share_mesh = store_params.strategy & SaveStrategy::ShareMesh;
|
||||
m_from_backup_save = store_params.strategy & SaveStrategy::Backup;
|
||||
m_minimal_published = store_params.strategy & SaveStrategy::MinimalPublished;
|
||||
|
||||
m_use_loaded_id = store_params.strategy & SaveStrategy::UseLoadedId;
|
||||
|
||||
@@ -6464,8 +6466,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
if (cb_cancel) return false;
|
||||
}
|
||||
|
||||
// BBS: add project config
|
||||
if (project_presets.size() > 0) {
|
||||
// BBS: add project config (omitted for minimal published 3MF)
|
||||
if (!m_minimal_published && project_presets.size() > 0) {
|
||||
// BBS: add project embedded preset files
|
||||
_add_project_embedded_presets_to_archive(archive, model, project_presets);
|
||||
|
||||
|
||||
@@ -145,6 +145,7 @@ enum class SaveStrategy
|
||||
SkipAuxiliary = 1 << 9,
|
||||
UseLoadedId = 1 << 10,
|
||||
ShareMesh = 1 << 11,
|
||||
MinimalPublished = 1 << 12,
|
||||
|
||||
SplitModel = 0x1000 | ProductionExt,
|
||||
Encrypted = SecureContentExt | SplitModel,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "PresetBundle.hpp"
|
||||
#include "Preset.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -102,4 +103,60 @@ std::vector<std::string> collect_dirty_settings_keys(const PresetBundle& bundle)
|
||||
return keys;
|
||||
}
|
||||
|
||||
DynamicPrintConfig filter_published_config(
|
||||
const DynamicPrintConfig &full_config,
|
||||
const std::vector<std::string> &published_keys,
|
||||
const std::vector<PublishedMaterialEntry> &material_keys)
|
||||
{
|
||||
DynamicPrintConfig filtered;
|
||||
|
||||
std::set<std::string> base_keys_to_include;
|
||||
|
||||
// 1. Mandatory material identity & slot count keys for 3MF validation/normalization
|
||||
static const std::vector<std::string> s_material_identity_keys = {
|
||||
"filament_colour",
|
||||
"filament_type",
|
||||
"filament_vendor",
|
||||
"filament_ids",
|
||||
"filament_diameter",
|
||||
"filament_self_index",
|
||||
"filament_extruder_variant"
|
||||
};
|
||||
for (const std::string &key : s_material_identity_keys)
|
||||
base_keys_to_include.insert(key);
|
||||
|
||||
// 2. Published plate / bed geometry keys (wipe tower positioning)
|
||||
static const std::vector<std::string> s_plate_geometry_keys = {
|
||||
"wipe_tower_x",
|
||||
"wipe_tower_y",
|
||||
"wipe_tower_rotation_angle"
|
||||
};
|
||||
for (const std::string &key : s_plate_geometry_keys)
|
||||
base_keys_to_include.insert(key);
|
||||
|
||||
// 3. Process and printer published keys
|
||||
for (const std::string &key : published_keys) {
|
||||
const std::string base_key = key.substr(0, key.find('#'));
|
||||
if (!base_key.empty())
|
||||
base_keys_to_include.insert(base_key);
|
||||
}
|
||||
|
||||
// 4. Material-specific published keys
|
||||
for (const PublishedMaterialEntry &entry : material_keys) {
|
||||
for (const std::string &key : entry.keys) {
|
||||
const std::string base_key = key.substr(0, key.find('#'));
|
||||
if (!base_key.empty())
|
||||
base_keys_to_include.insert(base_key);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy selected options from full_config into filtered config
|
||||
for (const std::string &key : base_keys_to_include) {
|
||||
if (const ConfigOption *opt = full_config.option(key))
|
||||
filtered.set_key_value(key, opt->clone());
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -53,4 +53,12 @@ struct PublishedMaterialEntry {
|
||||
int slot{-1};
|
||||
std::vector<std::string> keys;
|
||||
};
|
||||
|
||||
// Constructs a minimal DynamicPrintConfig for a published 3MF export containing only the
|
||||
// author-selected published keys, material keys, material identity fields, and plate geometry keys.
|
||||
class DynamicPrintConfig;
|
||||
DynamicPrintConfig filter_published_config(
|
||||
const DynamicPrintConfig &full_config,
|
||||
const std::vector<std::string> &published_keys,
|
||||
const std::vector<PublishedMaterialEntry> &material_keys);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig&
|
||||
std::string temp_str = opt_key;
|
||||
if (pos > 0) {
|
||||
boost::erase_head(temp_str, pos + 1);
|
||||
orig_opt_idx = static_cast<size_t>(atoi(temp_str.c_str()));
|
||||
orig_opt_idx = std::atoi(temp_str.c_str());
|
||||
}
|
||||
opt_idx = orig_opt_idx >= 0 ? orig_opt_idx : 0;
|
||||
const std::string pure_key = get_pure_opt_key(opt_key);
|
||||
@@ -83,8 +83,8 @@ wxString get_string_value(const std::string& opt_key, const DynamicPrintConfig&
|
||||
}
|
||||
auto opt_vector = dynamic_cast<const ConfigOptionVectorBase *>(option);
|
||||
|
||||
if (option->is_scalar() && config.option(pure_key)->is_nil() ||
|
||||
option->is_vector() && opt_vector && opt_idx >= 0 && opt_idx < opt_vector->size() && opt_vector->is_nil(opt_idx))
|
||||
if ((option->is_scalar() && option->is_nil()) ||
|
||||
(option->is_vector() && opt_vector && opt_idx >= 0 && opt_idx < opt_vector->size() && opt_vector->is_nil(opt_idx)))
|
||||
return _L("N/A");
|
||||
|
||||
wxString out;
|
||||
|
||||
@@ -7324,7 +7324,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
||||
}
|
||||
|
||||
auto choise = wxGetApp().app_config->get("no_warn_when_modified_gcodes");
|
||||
if (choise.empty() || choise != "true") {
|
||||
if (!published_config.published && (choise.empty() || choise != "true")) {
|
||||
// BBS: first validate the printer
|
||||
// validate the system profiles
|
||||
std::set<std::string> modified_gcodes;
|
||||
@@ -16204,16 +16204,21 @@ int Plater::export_published_3mf(const std::vector<std::string>& published_keys,
|
||||
model.model_info->metadata_items["published_keys"] = j.dump();
|
||||
model.model_info->metadata_items["published_material_keys"] = jm.dump();
|
||||
|
||||
// Same file layout save_project() uses for its project files, plus SaveStrategy::Silence:
|
||||
// Minimal published export: filter full_config to only the published keys, material keys,
|
||||
// identity fields, and plate geometry keys, and omit project-embedded preset dumps.
|
||||
DynamicPrintConfig full_cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||
DynamicPrintConfig filtered_cfg = filter_published_config(full_cfg, published_keys, material_keys);
|
||||
|
||||
// Same file layout save_project() uses for its project files, plus SaveStrategy::Silence and SaveStrategy::MinimalPublished:
|
||||
// without it export_3mf() calls set_project_filename() on success, which would make this
|
||||
// pure export the current project file. Silence keeps the project state untouched, exactly
|
||||
// like export_core_3mf().
|
||||
auto save_strategy = SaveStrategy::SplitModel | SaveStrategy::ShareMesh | SaveStrategy::Silence;
|
||||
auto save_strategy = SaveStrategy::SplitModel | SaveStrategy::ShareMesh | SaveStrategy::Silence | SaveStrategy::MinimalPublished;
|
||||
bool full_pathnames = wxGetApp().app_config->get_bool("export_sources_full_pathnames");
|
||||
if (full_pathnames)
|
||||
save_strategy = save_strategy | SaveStrategy::FullPathSources;
|
||||
|
||||
const int ret = export_3mf(into_path(path), save_strategy);
|
||||
const int ret = export_3mf(into_path(path), save_strategy, -1, nullptr, &filtered_cfg);
|
||||
|
||||
// Restore the previous metadata state (both on success and on failure).
|
||||
if (!had_model_info) {
|
||||
@@ -16737,7 +16742,7 @@ void publish(Model &model, SaveStrategy strategy) {
|
||||
}
|
||||
|
||||
// BBS: backup
|
||||
int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy strategy, int export_plate_idx, Export3mfProgressFn proFn)
|
||||
int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy strategy, int export_plate_idx, Export3mfProgressFn proFn, const DynamicPrintConfig* override_config)
|
||||
{
|
||||
int ret = 0;
|
||||
//if (p->model.objects.empty()) {
|
||||
@@ -16759,7 +16764,7 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy
|
||||
// modify model
|
||||
publish(p->model, strategy);
|
||||
|
||||
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
|
||||
DynamicPrintConfig cfg = override_config ? *override_config : wxGetApp().preset_bundle->full_config_secure();
|
||||
const std::string path_u8 = into_u8(path);
|
||||
wxBusyCursor wait;
|
||||
|
||||
|
||||
@@ -505,7 +505,7 @@ public:
|
||||
//void export_amf();
|
||||
//BBS add extra param for exporting 3mf silence
|
||||
// BBS: backup
|
||||
int export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path(), SaveStrategy strategy = SaveStrategy::Default, int export_plate_idx = -1, Export3mfProgressFn proFn = nullptr);
|
||||
int export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path(), SaveStrategy strategy = SaveStrategy::Default, int export_plate_idx = -1, Export3mfProgressFn proFn = nullptr, const DynamicPrintConfig* override_config = nullptr);
|
||||
|
||||
//BBS
|
||||
void publish_project();
|
||||
|
||||
@@ -321,8 +321,8 @@ void PublishSettingsDialog::build_option_model()
|
||||
cat.filament_id = identity.id;
|
||||
cat.filament_slot = slot;
|
||||
if (!icon_name.empty()) {
|
||||
ScalableBitmap icon_bmp(m_scroll, icon_name, 18);
|
||||
cat.icon = new wxStaticBitmap(m_scroll, wxID_ANY, icon_bmp.bmp());
|
||||
cat.icon_bmp = ScalableBitmap(m_scroll, icon_name, 18);
|
||||
cat.icon = new wxStaticBitmap(m_scroll, wxID_ANY, cat.icon_bmp.bmp());
|
||||
}
|
||||
if (section == Section::Material) {
|
||||
// Material header: [master (title)][slim tri-state select-all].
|
||||
@@ -667,8 +667,8 @@ size_t PublishSettingsDialog::section_group_for(Section kind)
|
||||
}
|
||||
|
||||
if (!section.icon_name.empty()) {
|
||||
ScalableBitmap icon_bmp(m_scroll, section.icon_name, 18);
|
||||
section.icon = new wxStaticBitmap(m_scroll, wxID_ANY, icon_bmp.bmp());
|
||||
section.icon_bmp = ScalableBitmap(m_scroll, section.icon_name, 18);
|
||||
section.icon = new wxStaticBitmap(m_scroll, wxID_ANY, section.icon_bmp.bmp());
|
||||
}
|
||||
|
||||
section.chevron = create_chevron(m_scroll, wxEVT_LEFT_DOWN, [this, new_index] { toggle_section(new_index); });
|
||||
@@ -1062,12 +1062,32 @@ std::vector<Slic3r::PublishedMaterialEntry> PublishSettingsDialog::GetPublishedM
|
||||
|
||||
void PublishSettingsDialog::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
// The remaining bitmap icons are rescaled; the collapse chevrons are
|
||||
// vector-drawn and repaint themselves.
|
||||
// Rescale toolbar bitmaps and icons; collapse chevrons are vector-drawn and repaint themselves.
|
||||
m_search.msw_rescale();
|
||||
m_menu.msw_rescale();
|
||||
m_filter_box->SetIcon(m_search.bmp());
|
||||
m_menu_button->SetBitmap(m_menu.bmp());
|
||||
|
||||
for (SectionGroup& section : m_sections) {
|
||||
if (section.icon != nullptr && section.icon_bmp.bmp().IsOk()) {
|
||||
section.icon_bmp.msw_rescale();
|
||||
section.icon->SetBitmap(section.icon_bmp.bmp());
|
||||
}
|
||||
if (section.header_line != nullptr)
|
||||
section.header_line->Rescale();
|
||||
}
|
||||
|
||||
for (Category& cat : m_categories) {
|
||||
if (cat.icon != nullptr && cat.icon_bmp.bmp().IsOk()) {
|
||||
cat.icon_bmp.msw_rescale();
|
||||
cat.icon->SetBitmap(cat.icon_bmp.bmp());
|
||||
}
|
||||
for (Subcategory& sub : cat.subs) {
|
||||
if (sub.header != nullptr)
|
||||
sub.header->Rescale();
|
||||
}
|
||||
}
|
||||
|
||||
SetMinSize(FromDIP(wxSize(600, 500)));
|
||||
m_scroll->FitInside();
|
||||
m_list_sizer->Layout();
|
||||
|
||||
@@ -91,6 +91,7 @@ private:
|
||||
Section section{Section::Print};
|
||||
size_t group{0}; // index into m_sections
|
||||
std::string icon_name; // bitmap name; empty = no icon
|
||||
ScalableBitmap icon_bmp; // scalable bitmap for DPI changes
|
||||
wxStaticBitmap* icon{nullptr}; // 18px category icon (null when icon_name empty)
|
||||
wxCheckBox* header{nullptr}; // select-all tri-state
|
||||
// Material opt-in: the master checkbox carries the material title and
|
||||
@@ -117,6 +118,7 @@ private:
|
||||
wxString title; // _L("Printer") / _L("Filament") / _L("Process")
|
||||
Section kind{Section::Print}; // maps 1:1 to the display group
|
||||
std::string icon_name; // "printer" / "filament" / "process"
|
||||
ScalableBitmap icon_bmp; // scalable bitmap for DPI changes
|
||||
wxStaticBitmap* icon{nullptr}; // 18px, like Category::icon
|
||||
wxCheckBox* header{nullptr}; // tri-state select-all; nullptr for the Filament group
|
||||
::StaticLine* header_line{nullptr}; // Filament group's clickable title
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "libslic3r/PublishSettings.hpp"
|
||||
|
||||
#include "test_utils.hpp"
|
||||
|
||||
@@ -674,3 +675,79 @@ SCENARIO("Published 3MF round-trips the published_material_keys metadata", "[3mf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Minimal published 3MF serialization filters config and omits embedded presets", "[3mf]") {
|
||||
GIVEN("a full print configuration and published keys") {
|
||||
Model model;
|
||||
std::string src_file = std::string(TEST_DATA_DIR) + "/test_3mf/Prusa.stl";
|
||||
REQUIRE(load_stl(src_file.c_str(), &model));
|
||||
model.add_default_instances();
|
||||
|
||||
DynamicPrintConfig full_cfg = DynamicPrintConfig::full_print_config();
|
||||
full_cfg.set_key_value("layer_height", new ConfigOptionFloat(0.24));
|
||||
full_cfg.set_key_value("retraction_length", new ConfigOptionFloats({ 1.2 }));
|
||||
|
||||
const std::vector<std::string> published_keys = { "layer_height", "retraction_length" };
|
||||
const std::vector<PublishedMaterialEntry> material_keys = {
|
||||
{ "PLA", "Generic", "GFL99", 0, { "filament_retraction_length" } }
|
||||
};
|
||||
|
||||
DynamicPrintConfig filtered_cfg = filter_published_config(full_cfg, published_keys, material_keys);
|
||||
|
||||
// Filtered config must contain the published keys and identity keys
|
||||
REQUIRE(filtered_cfg.option("layer_height") != nullptr);
|
||||
REQUIRE(filtered_cfg.option("retraction_length") != nullptr);
|
||||
REQUIRE(filtered_cfg.option("filament_colour") != nullptr);
|
||||
REQUIRE(filtered_cfg.option("filament_type") != nullptr);
|
||||
REQUIRE(filtered_cfg.option("wipe_tower_x") != nullptr);
|
||||
|
||||
// Non-published settings should NOT be in filtered_cfg
|
||||
REQUIRE(filtered_cfg.option("sparse_infill_density") == nullptr);
|
||||
REQUIRE(filtered_cfg.option("machine_start_gcode") == nullptr);
|
||||
|
||||
model.model_info = std::make_shared<ModelInfo>();
|
||||
model.model_info->metadata_items["published"] = "1";
|
||||
model.model_info->metadata_items["published_keys"] = R"(["layer_height","retraction_length"])";
|
||||
|
||||
ScopedTemporaryDir backup_dir("orca_min_pub");
|
||||
model.set_backup_path(backup_dir.string());
|
||||
|
||||
WHEN("stored using SaveStrategy::MinimalPublished") {
|
||||
ScopedTemporaryFile temp(".3mf");
|
||||
const std::string test_file = temp.string();
|
||||
|
||||
// Create a fake project preset to verify it gets omitted with MinimalPublished
|
||||
Preset preset(Preset::TYPE_PRINT, "TestPrintPreset");
|
||||
preset.config = full_cfg;
|
||||
std::vector<Preset*> project_presets = { &preset };
|
||||
|
||||
StoreParams store_params;
|
||||
store_params.path = test_file.c_str();
|
||||
store_params.model = &model;
|
||||
store_params.config = &filtered_cfg;
|
||||
store_params.project_presets = project_presets;
|
||||
store_params.strategy = SaveStrategy::Zip64 | SaveStrategy::Silence | SaveStrategy::MinimalPublished;
|
||||
REQUIRE(store_bbs_3mf(store_params));
|
||||
|
||||
Model dst_model;
|
||||
DynamicPrintConfig dst_config;
|
||||
ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Enable };
|
||||
PlateDataPtrs dst_plates;
|
||||
std::vector<Preset*> loaded_presets;
|
||||
bool is_bbl_3mf = false, is_orca_3mf = false;
|
||||
Semver file_version;
|
||||
bool loaded = load_bbs_3mf(test_file.c_str(), &dst_config, &ctxt, &dst_model, &dst_plates,
|
||||
&loaded_presets, &is_bbl_3mf, &is_orca_3mf, &file_version, nullptr,
|
||||
LoadStrategy::LoadModel | LoadStrategy::LoadConfig);
|
||||
THEN("the 3MF loads successfully without project embedded presets") {
|
||||
REQUIRE(loaded);
|
||||
REQUIRE(loaded_presets.empty());
|
||||
REQUIRE(dst_config.option("layer_height") != nullptr);
|
||||
REQUIRE(dst_config.opt_float("layer_height") == 0.24);
|
||||
REQUIRE(dst_config.option("sparse_infill_density") == nullptr);
|
||||
}
|
||||
release_PlateData_list(dst_plates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user