Export to prusa:

* now the .3mf contains the modified config compatible with purusaslicer
* now the superslicer settings are stored in their own file.
Note: projects saved with this version will transfer only prusa settings back to older superslicer version.
This commit is contained in:
supermerill 2020-11-21 20:54:46 +01:00
parent 8f3e0b6fb4
commit 3799d533cc
4 changed files with 326 additions and 13 deletions

View File

@ -1649,6 +1649,10 @@ protected:
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
// handle_legacy() is called internally by set_deserialize().
virtual void handle_legacy(t_config_option_key&/*opt_key*/, std::string&/*value*/) const {}
// Verify whether the opt_key has to be converted or isn't present int prusaslicer
// Both opt_key and value may be modified by to_prusa().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by to_prusa().
virtual void to_prusa(t_config_option_key&/*opt_key*/, std::string&/*value*/) const {}
public:
using ConfigOptionResolver::option;

View File

@ -48,8 +48,10 @@ const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format
const std::string CONTENT_TYPES_FILE = "[Content_Types].xml";
const std::string RELATIONSHIPS_FILE = "_rels/.rels";
const std::string THUMBNAIL_FILE = "Metadata/thumbnail.png";
const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std::string PRINT_CONFIG_FILE = "Metadata/SuperSlicer.config";
const std::string MODEL_CONFIG_FILE = "Metadata/SuperSlicer_model.config";
const std::string PRINT_PRUSA_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_PRUSA_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
@ -615,6 +617,7 @@ namespace Slic3r {
}
// we then loop again the entries to read other files stored in the archive
bool print_config_parsed = false, model_config_parsed = false;
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
@ -646,6 +649,7 @@ namespace Slic3r {
{
// extract slic3r print config file
_extract_print_config_from_archive(archive, stat, config, filename);
print_config_parsed = true;
}
if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE))
{
@ -653,6 +657,32 @@ namespace Slic3r {
_extract_custom_gcode_per_print_z_from_archive(archive, stat);
}
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE))
{
// extract slic3r model config file
if (!_extract_model_config_from_archive(archive, stat, model))
{
close_zip_reader(&archive);
add_error("Archive does not contain a valid model config");
return false;
}
model_config_parsed = true;
}
}
}
//parsed prusa files if superslicer not found
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
{
std::string name(stat.m_filename);
std::replace(name.begin(), name.end(), '\\', '/');
//TODO use special methods to convert them better?
if (!print_config_parsed && boost::algorithm::iequals(name, PRINT_PRUSA_CONFIG_FILE))
{
// extract slic3r print config file
_extract_print_config_from_archive(archive, stat, config, filename);
} else if (!model_config_parsed && boost::algorithm::iequals(name, MODEL_PRUSA_CONFIG_FILE))
{
// extract slic3r model config file
if (!_extract_model_config_from_archive(archive, stat, model))
@ -2680,18 +2710,44 @@ namespace Slic3r {
}
}
//add prusa print config
out = buffer;
for (std::string key : config.keys())
if (key != "compatible_printers") {
std::string value = config.opt_serialize(key);
config.to_prusa(key, value);
if(!key.empty())
out += "; " + key + " = " + value + "\n";
}
if (!out.empty())
{
if (!mz_zip_writer_add_mem(&archive, PRINT_PRUSA_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
{
add_error("Unable to add prusa print config file to archive");
return false;
}
}
return true;
}
bool _3MF_Exporter::_add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data)
{
std::stringstream stream;
std::stringstream stream_prusa;
// Store mesh transformation in full precision, as the volumes are stored transformed and they need to be transformed back
// when loaded as accurately as possible.
stream << std::setprecision(std::numeric_limits<double>::max_digits10);
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<" << CONFIG_TAG << ">\n";
stream_prusa << std::setprecision(std::numeric_limits<double>::max_digits10);
stream_prusa << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream_prusa << "<" << CONFIG_TAG << ">\n";
for (const IdToObjectDataMap::value_type& obj_metadata : objects_data)
{
const ModelObject* obj = obj_metadata.second.object;
@ -2699,15 +2755,18 @@ namespace Slic3r {
{
// Output of instances count added because of github #3435, currently not used by PrusaSlicer
stream << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
stream_prusa << " <" << OBJECT_TAG << " " << ID_ATTR << "=\"" << obj_metadata.first << "\" " << INSTANCESCOUNT_ATTR << "=\"" << obj->instances.size() << "\">\n";
// stores object's name
if (!obj->name.empty())
if (!obj->name.empty()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
}
// stores object's config data
for (const std::string& key : obj->config.keys())
{
for (const std::string& key : obj->config.keys()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
}
for (const ModelVolume* volume : obj_metadata.second.object->volumes)
@ -2722,20 +2781,30 @@ namespace Slic3r {
stream << " <" << VOLUME_TAG << " ";
stream << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
stream << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\">\n";
stream_prusa << " <" << VOLUME_TAG << " ";
stream_prusa << FIRST_TRIANGLE_ID_ATTR << "=\"" << it->second.first_triangle_id << "\" ";
stream_prusa << LAST_TRIANGLE_ID_ATTR << "=\"" << it->second.last_triangle_id << "\">\n";
// stores volume's name
if (!volume->name.empty())
if (!volume->name.empty()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
}
// stores volume's modifier field (legacy, to support old slicers)
if (volume->is_modifier())
if (volume->is_modifier()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
}
// stores volume's type (overrides the modifier field above)
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's local matrix
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MATRIX_KEY << "\" " << VALUE_ATTR << "=\"";
Transform3d matrix = volume->get_matrix() * volume->source.transform.get_matrix();
for (int r = 0; r < 4; ++r)
{
@ -2744,9 +2813,13 @@ namespace Slic3r {
stream << matrix(r, c);
if ((r != 3) || (c != 3))
stream << " ";
stream_prusa << matrix(r, c);
if ((r != 3) || (c != 3))
stream_prusa << " ";
}
}
stream << "\"/>\n";
stream_prusa << "\"/>\n";
// stores volume's source data
if (!volume->source.input_file.empty())
@ -2758,24 +2831,39 @@ namespace Slic3r {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Y_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(1) << "\"/>\n";
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_Z_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(2) << "\"/>\n";
}
// stores volume's config data
for (const std::string& key : volume->config.keys())
for (std::string key : volume->config.keys())
{
//superslicer config
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
//now the prusa config
std::string value = volume->config.opt_serialize(key);
volume->config.to_prusa(key, value);
if (!key.empty())
stream_prusa << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << value << "\"/>\n";
}
stream << " </" << VOLUME_TAG << ">\n";
stream_prusa << " </" << VOLUME_TAG << ">\n";
}
}
}
stream << " </" << OBJECT_TAG << ">\n";
stream_prusa << " </" << OBJECT_TAG << ">\n";
}
}
stream << "</" << CONFIG_TAG << ">\n";
stream_prusa << "</" << CONFIG_TAG << ">\n";
std::string out = stream.str();
@ -2785,6 +2873,13 @@ namespace Slic3r {
return false;
}
out = stream_prusa.str();
if (!mz_zip_writer_add_mem(&archive, MODEL_PRUSA_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
{
add_error("Unable to add prusa model config file to archive");
return false;
}
return true;
}

View File

@ -4943,6 +4943,214 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
}
}
void PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, const DynamicConfig& all_conf) {
std::unordered_set<std::string> to_remove_keys = {
"thumbnails_color",
"thumbnails_custom_color",
"thumbnails_with_bed",
"thumbnails_with_support",
"allow_empty_layers",
"avoid_crossing_not_first_layer",
"top_fan_speed",
"over_bridge_flow_ratio",
"bridge_overlap",
"bridge_speed_internal",
"brim_inside_holes",
"brim_width_interior",
"brim_ears",
"brim_ears_max_angle",
"brim_ears_pattern",
"brim_offset",
"chamber_temperature",
"complete_objects_one_skirt",
"complete_objects_sort",
"top_fill_pattern",
"solid_fill_pattern",
"enforce_full_fill_volume",
"external_infill_margin",
"bridged_infill_margin",
"external_perimeter_cut_corners",
"external_perimeter_fan_speed",
"external_perimeter_overlap",
"perimeter_overlap",
"perimeter_bonding",
"external_perimeters_vase",
"external_perimeters_nothole",
"external_perimeters_hole",
"perimeter_loop",
"perimeter_loop_seam",
"extra_perimeters_overhangs",
"extra_perimeters_odd_layers",
"only_one_perimeter_top",
"extruder_temperature_offset",
"extruder_fan_offset",
"print_extrusion_multiplier",
"filament_max_speed",
"filament_max_wipe_tower_speed",
"filament_enable_toolchange_temp",
"filament_use_fast_skinnydip",
"filament_enable_toolchange_part_fan",
"filament_toolchange_part_fan_speed",
"filament_use_skinnydip",
"filament_melt_zone_pause",
"filament_cooling_zone_pause",
"filament_dip_insertion_speed",
"filament_dip_extraction_speed",
"filament_toolchange_temp",
"filament_skinnydip_distance",
"filament_shrink",
"fill_angle_increment",
"fill_top_flow_ratio",
"fill_top_flow_ratio",
"first_layer_flow_ratio",
"fill_smooth_width",
"fill_smooth_distribution",
"first_layer_infill_speed",
"gap_fill",
"gap_fill_min_area",
"gap_fill_overlap",
"infill_dense",
"infill_connection",
"infill_dense_algo",
"feature_gcode",
"exact_last_layer_height",
"fan_speedup_time",
"fan_speedup_overhangs",
"fan_kickstart",
"machine_max_acceleration_travel",
"max_speed_reduction",
"min_length",
"min_width_top_surface",
"printhost_apikey",
"printhost_cafile",
"print_host",
"overhangs_speed",
"overhangs_width_speed",
"overhangs_reverse",
"overhangs_reverse_threshold",
"no_perimeter_unsupported_algo",
"support_material_solid_first_layer",
"print_retract_length",
"retract_lift_first_layer",
"retract_lift_top",
"seam_angle_cost",
"seam_travel_cost",
"skirt_extrusion_width",
"small_perimeter_min_length",
"small_perimeter_max_length",
"curve_smoothing_angle_convex",
"curve_smoothing_angle_concave",
"curve_smoothing_precision",
"curve_smoothing_cutoff_dist",
"model_precision",
"support_material_contact_distance_type",
"support_material_contact_distance_bottom",
"support_material_interface_pattern",
"print_temperature",
"print_retract_lift",
"thin_perimeters",
"thin_perimeters_all",
"thin_walls_min_width",
"thin_walls_overlap",
"thin_walls_merge",
"thin_walls_speed",
"time_estimation_compensation",
"tool_name",
"wipe_advanced",
"wipe_advanced_nozzle_melted_volume",
"filament_wipe_advanced_pigment",
"wipe_advanced_multiplier",
"wipe_advanced_algo",
"wipe_tower_brim",
"wipe_extra_perimeter",
"xy_inner_size_compensation",
"hole_size_compensation",
"hole_size_threshold",
"hole_to_polyhole",
"z_step",
"milling_cutter",
"milling_diameter",
"milling_offset",
"milling_z_offset",
"milling_z_lift",
"milling_toolchange_start_gcode",
"milling_toolchange_end_gcode",
"milling_post_process",
"milling_extra_size",
"milling_after_z",
"milling_speed"
};
//looks if it's to be removed, or have to be transformed
if (to_remove_keys.find(opt_key) != to_remove_keys.end()) {
opt_key = "";
value = "";
} else if (opt_key.find("_pattern") != std::string::npos) {
if ("smooth" == value || "smoothtriple" == value || "smoothhilbert" == value || "rectiwithperimeter" == value || "scatteredrectilinear" == value || "rectilineargapfill" == value || "sawtooth" == value) {
value = "rectilinear";
} else if ( "concentricgapfill" == value) {
value = "concentric";
}
} else if ("seam_position" == opt_key) {
if ("seam_travel_cost" == value || "near" == value || "hidden" == value) {
value = "nearest";
}
} else if ("first_layer_size_compensation" == opt_key) {
opt_key = "elefant_foot_compensation";
if (!value.empty()) {
if (value[0] == '-') {
value = value.substr(1);
} else {
value = "0";
}
}
} else if ("elephant_foot_min_width" == opt_key) {
opt_key = "elefant_foot_min_width";
} else if("first_layer_acceleration" == opt_key || "infill_acceleration" == opt_key || "bridge_acceleration" == opt_key || "default_acceleration" == opt_key || "overhangs_speed" == opt_key || "perimeter_acceleration" == opt_key){
if (value.find("%") != std::string::npos)
value = "0";
} else if ("gap_fill_speed" == opt_key && all_conf.has("gap_fill") && !all_conf.option<ConfigOptionBool>("gap_fill")->value) {
value = "0";
} else if ("bridge_flow_ratio" == opt_key && all_conf.has("bridge_flow_ratio")) {
value = boost::lexical_cast<std::string>(all_conf.option<ConfigOptionPercent>("bridge_flow_ratio")->get_abs_value(1));
} else if ("overhangs_width" == opt_key) {
opt_key = "overhangs";
if (value != "0")
value = "1";
} else if ("support_material_contact_distance_top" == opt_key) {
opt_key = "support_material_contact_distance";
//default : get the top value or 0.2 if a %
if (value.find("%") != std::string::npos)
value = "0.2";
try { //avoid most useless cheks and multiple corners cases with this try catch
SupportZDistanceType dist_type = all_conf.option<ConfigOptionEnum<SupportZDistanceType>>("support_material_contact_distance_type")->value;
if (SupportZDistanceType::zdNone == dist_type) {
value = "0";
} else {
double val = all_conf.option<ConfigOptionFloatOrPercent>("support_material_contact_distance_top")->get_abs_value(all_conf.option<ConfigOptionFloats>("nozzle_diameter")->values.front());
if (SupportZDistanceType::zdFilament == dist_type) { // not exact but good enough effort
val += all_conf.option<ConfigOptionFloats>("nozzle_diameter")->values.front();
val -= all_conf.get_abs_value("layer_height");
}
value = boost::lexical_cast<std::string>(val);
}
}
catch (...) {
}
} else if ("gcode_flavor" == opt_key) {
if ("reprap" == value)
value = "reprapfirmware";
else if ("sprinter" == value)
value = "reprap";
else if ("lerdge" == value)
value = "marlin";
else if ("klipper" == value)
value = "reprap";
}
}
const PrintConfigDef print_config_def;
DynamicPrintConfig DynamicPrintConfig::full_print_config()

View File

@ -365,6 +365,7 @@ public:
PrintConfigDef();
static void handle_legacy(t_config_option_key& opt_key, std::string& value);
static void to_prusa(t_config_option_key& opt_key, std::string& value, const DynamicConfig& all_conf);
// Array options growing with the number of extruders
const std::vector<std::string>& extruder_option_keys() const { return m_extruder_option_keys; }
@ -435,6 +436,9 @@ public:
// handle_legacy() is called internally by set_deserialize().
void handle_legacy(t_config_option_key &opt_key, std::string &value) const override
{ PrintConfigDef::handle_legacy(opt_key, value); }
void to_prusa(t_config_option_key& opt_key, std::string& value) const override
{ PrintConfigDef::to_prusa(opt_key, value, *this); }
};
class StaticPrintConfig : public StaticConfig
@ -1860,6 +1864,8 @@ public:
// Not thread safe! Should not be called from other than the main thread!
void touch() { m_timestamp = ++ s_last_timestamp; }
void to_prusa(t_config_option_key& opt_key, std::string& value) const
{ m_data.to_prusa(opt_key, value); }
private:
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) { ar(m_timestamp); ar(m_data); }