Merge branch 'lm_wipe_tower_model'

This commit is contained in:
Lukas Matena 2024-10-17 12:17:56 +02:00
commit 1f3be0e0bb
20 changed files with 246 additions and 131 deletions

View File

@ -90,6 +90,7 @@ const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt";
const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml";
const std::string WIPE_TOWER_INFORMATION_FILE = "Metadata/Prusa_Slicer_wipe_tower_information.xml";
const std::string CUT_INFORMATION_FILE = "Metadata/Prusa_Slicer_cut_information.xml";
static constexpr const char *RELATIONSHIP_TAG = "Relationship";
@ -554,6 +555,8 @@ namespace Slic3r {
void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_wipe_tower_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
void _extract_wipe_tower_information_from_archive_legacy(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, Model& model);
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
@ -766,6 +769,9 @@ namespace Slic3r {
}
}
// Initialize the wipe tower position (see the end of this function):
model.wipe_tower.position.x() = std::numeric_limits<double>::max();
// Read root model file
if (start_part_stat.m_file_index < num_entries) {
try {
@ -822,6 +828,10 @@ namespace Slic3r {
// extract slic3r layer config ranges file
_extract_custom_gcode_per_print_z_from_archive(archive, stat);
}
else if (boost::algorithm::iequals(name, WIPE_TOWER_INFORMATION_FILE)) {
// extract wipe tower information file
_extract_wipe_tower_information_from_archive(archive, stat, model);
}
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE)) {
// extract slic3r model config file
if (!_extract_model_config_from_archive(archive, stat, model)) {
@ -836,6 +846,28 @@ namespace Slic3r {
}
}
if (model.wipe_tower.position.x() == std::numeric_limits<double>::max()) {
// This is apparently an old project from before PS 2.9.0, which saved wipe tower pos and rotation
// into config, not into Model. Try to load it from the config file.
// First set default in case we do not find it (these were the default values of the config options).
model.wipe_tower.position.x() = 180;
model.wipe_tower.position.y() = 140;
model.wipe_tower.rotation = 0.;
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(), '\\', '/');
if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE)) {
_extract_wipe_tower_information_from_archive_legacy(archive, stat, model);
break;
}
}
}
}
close_zip_reader(&archive);
if (m_version == 0) {
@ -1615,6 +1647,77 @@ namespace Slic3r {
}
}
void _3MF_Importer::_extract_wipe_tower_information_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, Model& model)
{
if (stat.m_uncomp_size > 0) {
std::string buffer((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 wipe tower information data to buffer");
return;
}
std::istringstream iss(buffer); // wrap returned xml to istringstream
pt::ptree main_tree;
pt::read_xml(iss, main_tree);
try {
auto& node = main_tree.get_child("wipe_tower_information");
double pos_x = node.get<double>("<xmlattr>.position_x");
double pos_y = node.get<double>("<xmlattr>.position_y");
double rot_deg = node.get<double>("<xmlattr>.rotation_deg");
model.wipe_tower.position = Vec2d(pos_x, pos_y);
model.wipe_tower.rotation = rot_deg;
} catch (const boost::property_tree::ptree_bad_path&) {
// Handles missing node or attribute.
add_error("Error while reading wipe tower information.");
return;
}
}
}
void _3MF_Importer::_extract_wipe_tower_information_from_archive_legacy(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat, Model& model)
{
if (stat.m_uncomp_size > 0) {
std::string buffer((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;
}
// Try to find wipe tower data in the config, where pre-2.9.0 slicers saved them.
// Do not load the config as usual, it no longer knows those values.
std::istringstream iss(buffer);
std::string line;
while (iss) {
std::getline(iss, line);
boost::algorithm::trim_left_if(line, [](char ch) { return std::isspace(ch) || ch == ';'; });
if (boost::starts_with(line, "wipe_tower_x") || boost::starts_with(line, "wipe_tower_y") || boost::starts_with(line, "wipe_tower_rotation_angle")) {
std::string value_str;
try {
value_str = line.substr(line.find("=") + 1, std::string::npos);
} catch (const std::out_of_range&) {
continue;
}
double val = 0.;
std::istringstream value_ss(value_str);
value_ss >> val;
if (! value_ss.fail()) {
if (boost::starts_with(line, "wipe_tower_x"))
model.wipe_tower.position.x() = val;
else if (boost::starts_with(line, "wipe_tower_y"))
model.wipe_tower.position.y() = val;
else
model.wipe_tower.rotation = val;
}
}
}
}
}
void XMLCALL _3MF_Importer::_handle_start_relationships_element(void *userData, const char *name, const char **attributes)
{
_3MF_Importer *importer = (_3MF_Importer *) userData;
@ -2645,9 +2748,10 @@ namespace Slic3r {
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config, const Model& model);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
bool _add_wipe_tower_information_file_to_archive( mz_zip_archive& archive, Model& model);
};
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64)
@ -2754,10 +2858,19 @@ namespace Slic3r {
return false;
}
// Adds wipe tower information ("Metadata/Prusa_Slicer_wipe_tower_information.xml").
if (!_add_wipe_tower_information_file_to_archive(archive, model)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
// Adds slic3r print config file ("Metadata/Slic3r_PE.config").
// This file contains the content of FullPrintConfing / SLAFullPrintConfig.
if (config != nullptr) {
if (!_add_print_config_file_to_archive(archive, *config)) {
if (!_add_print_config_file_to_archive(archive, *config, model)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
@ -3456,16 +3569,39 @@ namespace Slic3r {
return true;
}
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config, const Model& model)
{
assert(is_decimal_separator_point());
char buffer[1024];
sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
std::string out = buffer;
for (const std::string &key : config.keys())
if (key != "compatible_printers")
out += "; " + key + " = " + config.opt_serialize(key) + "\n";
t_config_option_keys keys = config.keys();
// Wipe tower values were historically stored in the config, but they were moved into
// Model in PS 2.9.0. Keep saving the old values to maintain forward compatibility.
for (const std::string s : {"wipe_tower_x", "wipe_tower_y", "wipe_tower_rotation_angle"})
if (! config.has(s))
keys.emplace_back(s);
sort_remove_duplicates(keys);
for (const std::string& key : keys) {
if (key == "compatible_printers")
continue;
std::string opt_serialized;
if (key == "wipe_tower_x")
opt_serialized = float_to_string_decimal_point(model.wipe_tower.position.x());
else if (key == "wipe_tower_y")
opt_serialized = float_to_string_decimal_point(model.wipe_tower.position.y());
else if (key == "wipe_tower_rotation_angle")
opt_serialized = float_to_string_decimal_point(model.wipe_tower.rotation);
else
opt_serialized = config.opt_serialize(key);
out += "; " + key + " = " + opt_serialized + "\n";
}
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
@ -3663,6 +3799,34 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
return true;
}
bool _3MF_Exporter::_add_wipe_tower_information_file_to_archive( mz_zip_archive& archive, Model& model)
{
std::string out = "";
pt::ptree tree;
pt::ptree& main_tree = tree.add("wipe_tower_information", "");
main_tree.put("<xmlattr>.position_x", model.wipe_tower.position.x());
main_tree.put("<xmlattr>.position_y", model.wipe_tower.position.y());
main_tree.put("<xmlattr>.rotation_deg", model.wipe_tower.rotation);
std::ostringstream oss;
boost::property_tree::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string
boost::replace_all(out, "><", ">\n<");
if (!out.empty()) {
if (!mz_zip_writer_add_mem(&archive, WIPE_TOWER_INFORMATION_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
add_error("Unable to add wipe tower information file to archive");
return false;
}
}
return true;
}
// Perform conversions based on the config values available.
static void handle_legacy_project_loaded(unsigned int version_project_file, DynamicPrintConfig& config, const boost::optional<Semver>& prusaslicer_generator_version)
{

View File

@ -1312,7 +1312,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
std::vector<std::pair<coordf_t, ObjectsLayerToPrint>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower = std::make_unique<GCode::WipeTowerIntegration>(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get());
m_wipe_tower = std::make_unique<GCode::WipeTowerIntegration>(print.model().wipe_tower.position.cast<float>(), print.model().wipe_tower.rotation, print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get());
// Set position for wipe tower generation.
Vec3d new_position = this->writer().get_position();

View File

@ -151,8 +151,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
// Wipe tower extrusions are saved as if the tower was at the origin with no rotation
// We need to get position and angle of the wipe tower to transform them to actual position.
Transform2d trafo =
Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) *
Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value));
Eigen::Translation2d(print.model().wipe_tower.position.x(), print.model().wipe_tower.position.y()) *
Eigen::Rotation2Dd(Geometry::deg2rad(print.model().wipe_tower.rotation));
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {

View File

@ -541,11 +541,11 @@ WipeTower::ToolChangeResult WipeTower::construct_tcr(WipeTowerWriter& writer,
WipeTower::WipeTower(const PrintConfig& config, const PrintRegionConfig& default_region_config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
WipeTower::WipeTower(const Vec2f& pos, double rotation_deg, const PrintConfig& config, const PrintRegionConfig& default_region_config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) :
m_semm(config.single_extruder_multi_material.value),
m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y),
m_wipe_tower_pos(pos),
m_wipe_tower_width(float(config.wipe_tower_width)),
m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)),
m_wipe_tower_rotation_angle(rotation_deg),
m_wipe_tower_brim_width(float(config.wipe_tower_brim_width)),
m_wipe_tower_cone_angle(float(config.wipe_tower_cone_angle)),
m_extra_flow(float(config.wipe_tower_extra_flow/100.)),

View File

@ -139,7 +139,9 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTower(const PrintConfig& config,
WipeTower(const Vec2f& position,
double rotation_deg,
const PrintConfig& config,
const PrintRegionConfig& default_region_config,
const std::vector<std::vector<float>>& wiping_matrix,
size_t initial_tool);

View File

@ -20,14 +20,16 @@ namespace GCode {
class WipeTowerIntegration {
public:
WipeTowerIntegration(
Vec2f pos,
double rotation,
const PrintConfig &print_config,
const std::vector<WipeTower::ToolChangeResult> &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const WipeTower::ToolChangeResult &final_purge) :
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)),
m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)),
m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)),
m_left( 0.f),
m_right(float(print_config.wipe_tower_width.value)),
m_wipe_tower_pos(pos),
m_wipe_tower_rotation(rotation),
m_extruder_offsets(print_config.extruder_offset.values),
m_priming(priming),
m_tool_changes(tool_changes),

View File

@ -69,6 +69,7 @@ Model& Model::assign_copy(const Model &rhs)
// copy custom code per height
this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
this->wipe_tower = rhs.wipe_tower;
return *this;
}
@ -90,6 +91,7 @@ Model& Model::assign_copy(Model &&rhs)
// copy custom code per height
this->custom_gcode_per_print_z = std::move(rhs.custom_gcode_per_print_z);
this->wipe_tower = rhs.wipe_tower;
return *this;
}

View File

@ -1190,11 +1190,20 @@ private:
};
// Note: The following class does not have to inherit from ObjectID, it is currently
// only used for arrangement. It might be good to refactor this in future.
class ModelWipeTower final : public ObjectBase
{
public:
Vec2d position;
double rotation;
Vec2d position = Vec2d(180., 140.);
double rotation = 0.;
bool operator==(const ModelWipeTower& other) const { return position == other.position && rotation == other.rotation; }
bool operator!=(const ModelWipeTower& other) const { return !((*this) == other); }
// Assignment operator does not touch the ID!
ModelWipeTower& operator=(const ModelWipeTower& rhs) { position = rhs.position; rotation = rhs.rotation; return *this; }
private:
friend class cereal::access;
@ -1210,9 +1219,8 @@ private:
// Copy constructor copies the ID.
explicit ModelWipeTower(const ModelWipeTower &cfg) = default;
// Disabled methods.
ModelWipeTower(ModelWipeTower &&rhs) = delete;
ModelWipeTower& operator=(const ModelWipeTower &rhs) = delete;
// Disabled methods.
ModelWipeTower(ModelWipeTower &&rhs) = delete;
ModelWipeTower& operator=(ModelWipeTower &&rhs) = delete;
// For serialization / deserialization of ModelWipeTower composed into another class into the Undo / Redo stack as a separate object.

View File

@ -485,8 +485,8 @@ static std::vector<std::string> s_Preset_print_options {
"perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width",
"top_infill_extrusion_width", "support_material_extrusion_width", "infill_overlap", "infill_anchor", "infill_anchor_max", "bridge_flow_ratio",
"elefant_foot_compensation", "xy_size_compensation", "resolution", "gcode_resolution", "arc_fitting",
"wipe_tower", "wipe_tower_x", "wipe_tower_y",
"wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
"wipe_tower",
"wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width",
"mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_flow", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits",
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width",

View File

@ -199,10 +199,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "draft_shield"
|| opt_key == "skirt_distance"
|| opt_key == "min_skirt_length"
|| opt_key == "ooze_prevention"
|| opt_key == "wipe_tower_x"
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_rotation_angle") {
|| opt_key == "ooze_prevention") {
steps.emplace_back(psSkirtBrim);
} else if (
opt_key == "first_layer_height"
@ -1049,8 +1046,8 @@ void Print::process()
if (this->has_wipe_tower()) {
// These values have to be updated here, not during wipe tower generation.
// When the wipe tower is moved/rotated, it is not regenerated.
m_wipe_tower_data.position = { m_config.wipe_tower_x, m_config.wipe_tower_y };
m_wipe_tower_data.rotation_angle = m_config.wipe_tower_rotation_angle;
m_wipe_tower_data.position = model().wipe_tower.position;
m_wipe_tower_data.rotation_angle = model().wipe_tower.rotation;
}
auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(objects(), m_wipe_tower_data);
@ -1279,8 +1276,8 @@ Points Print::first_layer_wipe_tower_corners() const
pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha)));
for (Vec2d& pt : pts) {
pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt;
pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value);
pt = Eigen::Rotation2Dd(Geometry::deg2rad(model().wipe_tower.rotation)) * pt;
pt += model().wipe_tower.position;
pts_scaled.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
}
}
@ -1554,7 +1551,7 @@ void Print::_make_wipe_tower()
this->throw_if_canceled();
// Initialize the wipe tower.
WipeTower wipe_tower(m_config, m_default_region_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder());
WipeTower wipe_tower(model().wipe_tower.position.cast<float>(), model().wipe_tower.rotation, m_config, m_default_region_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder());
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < m_config.nozzle_diameter.size(); ++ i)

View File

@ -1066,7 +1066,12 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
num_extruders_changed = true;
}
}
// Check the position and rotation of the wipe tower.
if (model.wipe_tower != m_model.wipe_tower)
update_apply_status(this->invalidate_step(psSkirtBrim));
m_model.wipe_tower = model.wipe_tower;
ModelObjectStatusDB model_object_status_db;
// 1) Synchronize model objects.

View File

@ -3547,20 +3547,6 @@ void PrintConfigDef::init_fff_params()
def->tooltip = "";
def->set_default_value(new ConfigOptionBool{ false });
def = this->add("wipe_tower_x", coFloat);
def->label = L("Position X");
def->tooltip = L("X coordinate of the left front corner of a wipe tower");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(180.));
def = this->add("wipe_tower_y", coFloat);
def->label = L("Position Y");
def->tooltip = L("Y coordinate of the left front corner of a wipe tower");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(140.));
def = this->add("wipe_tower_width", coFloat);
def->label = L("Width");
def->tooltip = L("Width of a wipe tower");
@ -3568,13 +3554,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(60.));
def = this->add("wipe_tower_rotation_angle", coFloat);
def->label = L("Wipe tower rotation angle");
def->tooltip = L("Wipe tower rotation angle with respect to x-axis.");
def->sidetext = L("°");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("wipe_tower_brim_width", coFloat);
def->label = L("Wipe tower brim width");
def->tooltip = L("Wipe tower brim width");
@ -4821,7 +4800,8 @@ static std::set<std::string> PrintConfigDef_ignore = {
// Disabled in 2.6.0-alpha6, this option is problematic
"infill_only_where_needed",
"gcode_binary", // Introduced in 2.7.0-alpha1, removed in 2.7.1 (replaced by binary_gcode).
"wiping_volumes_extruders" // Removed in 2.7.3-alpha1.
"wiping_volumes_extruders", // Removed in 2.7.3-alpha1.
"wipe_tower_x", "wipe_tower_y", "wipe_tower_rotation_angle" // Removed in 2.9.0
};
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)

View File

@ -965,11 +965,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionBools, wipe))
((ConfigOptionBool, wipe_tower))
((ConfigOptionFloat, wipe_tower_acceleration))
((ConfigOptionFloat, wipe_tower_x))
((ConfigOptionFloat, wipe_tower_y))
((ConfigOptionFloat, wipe_tower_width))
((ConfigOptionFloat, wipe_tower_per_color_wipe))
((ConfigOptionFloat, wipe_tower_rotation_angle))
((ConfigOptionFloat, wipe_tower_brim_width))
((ConfigOptionFloat, wipe_tower_cone_angle))
((ConfigOptionPercent, wipe_tower_extra_spacing))

View File

@ -324,7 +324,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("standby_temperature_delta", have_ooze_prevention);
bool have_wipe_tower = config->opt_bool("wipe_tower");
for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle",
for (auto el : { "wipe_tower_width", "wipe_tower_brim_width", "wipe_tower_cone_angle",
"wipe_tower_extra_spacing", "wipe_tower_extra_flow", "wipe_tower_bridging", "wipe_tower_no_sparse_layers", "single_extruder_multi_material_priming" })
toggle_field(el, have_wipe_tower);

View File

@ -1690,8 +1690,8 @@ void GCodeViewer::load_wipetower_shell(const Print& print)
const std::vector<std::pair<float, float>> z_and_depth_pairs = print.wipe_tower_data(extruders_count).z_and_depth_pairs;
const float brim_width = wipe_tower_data.brim_width;
if (depth != 0.) {
m_shells.volumes.load_wipe_tower_preview(config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, z_and_depth_pairs,
max_z, config.wipe_tower_cone_angle, config.wipe_tower_rotation_angle, false, brim_width);
m_shells.volumes.load_wipe_tower_preview(wxGetApp().plater()->model().wipe_tower.position.x(), wxGetApp().plater()->model().wipe_tower.position.y(), config.wipe_tower_width, depth, z_and_depth_pairs,
max_z, config.wipe_tower_cone_angle, wxGetApp().plater()->model().wipe_tower.rotation, false, brim_width);
GLVolume* volume = m_shells.volumes.volumes.back();
volume->color.a(0.25f);
volume->force_native_color = true;

View File

@ -1011,8 +1011,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_RESET_SKEW, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MIRRORED, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_TOUCHED, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event<bool>);
wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>);
wxDEFINE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED, SimpleEvent);
@ -2392,10 +2391,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (extruders_count > 1 && wt && !co) {
const float x = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_x"))->value;
const float y = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_y"))->value;
const float x = m_model->wipe_tower.position.x();
const float y = m_model->wipe_tower.position.y();
const float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
const float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
const float a = m_model->wipe_tower.rotation;
const float bw = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_brim_width"))->value;
const float ca = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_cone_angle"))->value;
@ -3954,8 +3953,10 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
if (object_moved)
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
if (wipe_tower_origin != Vec3d::Zero())
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin)));
if (wipe_tower_origin != Vec3d::Zero()) {
m_model->wipe_tower.position = Vec2d(wipe_tower_origin[0], wipe_tower_origin[1]);
post_event(SimpleEvent(EVT_GLCANVAS_WIPETOWER_TOUCHED));
}
if (current_printer_technology() == ptFFF && fff_print()->config().complete_objects) {
update_sequential_clearance(true);
@ -3999,7 +4000,8 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
const Vec3d offset = v->get_volume_offset();
Vec3d rot_unit_x = v->get_volume_transformation().get_matrix().linear() * Vec3d::UnitX();
double z_rot = std::atan2(rot_unit_x.y(), rot_unit_x.x());
post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3d(offset.x(), offset.y(), z_rot)));
m_model->wipe_tower.position = Vec2d(offset.x(), offset.y());
m_model->wipe_tower.rotation = (180./M_PI) * z_rot;
}
const int object_idx = v->object_idx();
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
@ -4316,9 +4318,9 @@ GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const
for (const GLVolume* vol : m_volumes.volumes) {
if (vol->is_wipe_tower) {
wti.m_pos = Vec2d(m_config->opt_float("wipe_tower_x"),
m_config->opt_float("wipe_tower_y"));
wti.m_rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle");
wti.m_pos = Vec2d(m_model->wipe_tower.position.x(),
m_model->wipe_tower.position.y());
wti.m_rotation = (M_PI/180.) * m_model->wipe_tower.rotation;
const BoundingBoxf3& bb = vol->bounding_box();
wti.m_bb = BoundingBoxf{to_2d(bb.min), to_2d(bb.max)};
break;
@ -6885,11 +6887,8 @@ const SLAPrint* GLCanvas3D::sla_print() const
void GLCanvas3D::WipeTowerInfo::apply_wipe_tower(Vec2d pos, double rot)
{
DynamicPrintConfig cfg;
cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = pos.x();
cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = pos.y();
cfg.opt<ConfigOptionFloat>("wipe_tower_rotation_angle", true)->value = (180./M_PI) * rot;
wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg);
wxGetApp().plater()->model().wipe_tower.position = pos;
wxGetApp().plater()->model().wipe_tower.rotation = (180./M_PI) * rot;
}
void GLCanvas3D::RenderTimer::Notify()

View File

@ -163,12 +163,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event<int>); // data: +1 => increase, -1 => decrease
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_FORCE_UPDATE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_TOUCHED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_SKEW, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MIRRORED, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_ROTATED, Vec3dEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event<bool>);
wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>);
wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED, SimpleEvent);
@ -919,8 +918,6 @@ public:
inline double rotation() const { return m_rotation; }
inline const Vec2d bb_size() const { return m_bb.size(); }
inline const BoundingBoxf& bounding_box() const { return m_bb; }
void apply_wipe_tower() const { apply_wipe_tower(m_pos, m_rotation); }
static void apply_wipe_tower(Vec2d pos, double rot);
};

View File

@ -427,8 +427,8 @@ public:
if (wipe_tower_data.final_purge)
m_final.emplace_back(*wipe_tower_data.final_purge.get());
m_angle = config.wipe_tower_rotation_angle.value / 180.0f * PI;
m_position = Slic3r::Vec2f(config.wipe_tower_x.value, config.wipe_tower_y.value);
m_angle = print.model().wipe_tower.rotation / 180.0f * PI;
m_position = print.model().wipe_tower.position.cast<float>();
m_layers_count = wipe_tower_data.tool_changes.size() + (m_priming.empty() ? 0 : 1);
}

View File

@ -614,7 +614,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extra_flow", "wipe_tower_extruder",
"wipe_tower", "wipe_tower_width", "wipe_tower_brim_width", "wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extra_flow", "wipe_tower_extruder",
"extruder_colour", "filament_colour", "material_colour", "max_print_height", "printer_model", "printer_notes", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
@ -701,8 +701,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
{ if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); });
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); });
view3D_canvas->Bind(EVT_GLCANVAS_FORCE_UPDATE, [this](SimpleEvent&) { update(); });
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this);
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this);
view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_TOUCHED,[this](SimpleEvent&) { update(); });
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); });
view3D_canvas->Bind(EVT_GLCANVAS_RESET_SKEW, [this](SimpleEvent&) { update(); });
view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_SCALED, [this](SimpleEvent&) { update(); });
@ -1301,8 +1300,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (! config_substitutions.empty())
show_substitutions_info(config_substitutions.substitutions, filename.string());
if (load_config)
if (load_config) {
this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
this->model.wipe_tower = model.wipe_tower;
}
}
if (load_config) {
@ -3234,19 +3235,13 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
void Plater::priv::on_wipetower_moved(Vec3dEvent &evt)
{
DynamicPrintConfig cfg;
cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = evt.data(0);
cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = evt.data(1);
wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg);
model.wipe_tower.position = Vec2d(evt.data[0], evt.data[1]);
}
void Plater::priv::on_wipetower_rotated(Vec3dEvent& evt)
{
DynamicPrintConfig cfg;
cfg.opt<ConfigOptionFloat>("wipe_tower_x", true)->value = evt.data(0);
cfg.opt<ConfigOptionFloat>("wipe_tower_y", true)->value = evt.data(1);
cfg.opt<ConfigOptionFloat>("wipe_tower_rotation_angle", true)->value = Geometry::rad2deg(evt.data(2));
wxGetApp().get_tab(Preset::TYPE_PRINT)->load_config(cfg);
model.wipe_tower.position = Vec2d(evt.data[0], evt.data[1]);
model.wipe_tower.rotation = Geometry::rad2deg(evt.data(2));
}
void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)
@ -3789,13 +3784,6 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
if (view3D->get_canvas3d()->get_gizmos_manager().wants_reslice_supports_on_undo())
snapshot_data.flags |= UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS;
//FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config.
// This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config.
if (this->printer_technology == ptFFF) {
const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y"));
model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle");
}
const GLGizmosManager& gizmos = view3D->get_canvas3d()->get_gizmos_manager();
if (snapshot_type == UndoRedo::SnapshotType::ProjectSeparator && get_config_bool("clear_undo_redo_stack_on_new_project"))
@ -3865,13 +3853,6 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
}
// Save the last active preset name of a particular printer technology.
((this->printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name) = wxGetApp().preset_bundle->printers.get_selected_preset_name();
//FIXME updating the Wipe tower config values at the ModelWipeTower from the Print config.
// This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config.
if (this->printer_technology == ptFFF) {
const DynamicPrintConfig &config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
model.wipe_tower.position = Vec2d(config.opt_float("wipe_tower_x"), config.opt_float("wipe_tower_y"));
model.wipe_tower.rotation = config.opt_float("wipe_tower_rotation_angle");
}
const int layer_range_idx = it_snapshot->snapshot_data.layer_range_idx;
// Flags made of Snapshot::Flags enum values.
unsigned int new_flags = it_snapshot->snapshot_data.flags;
@ -3921,22 +3902,6 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
// This also switches the printer technology based on the printer technology of the active printer profile.
wxGetApp().load_current_presets();
}
//FIXME updating the Print config from the Wipe tower config values at the ModelWipeTower.
// This is a workaround until we refactor the Wipe Tower position / orientation to live solely inside the Model, not in the Print config.
if (this->printer_technology == ptFFF) {
const DynamicPrintConfig &current_config = wxGetApp().preset_bundle->prints.get_edited_preset().config;
Vec2d current_position(current_config.opt_float("wipe_tower_x"), current_config.opt_float("wipe_tower_y"));
double current_rotation = current_config.opt_float("wipe_tower_rotation_angle");
if (current_position != model.wipe_tower.position || current_rotation != model.wipe_tower.rotation) {
DynamicPrintConfig new_config;
new_config.set_key_value("wipe_tower_x", new ConfigOptionFloat(model.wipe_tower.position.x()));
new_config.set_key_value("wipe_tower_y", new ConfigOptionFloat(model.wipe_tower.position.y()));
new_config.set_key_value("wipe_tower_rotation_angle", new ConfigOptionFloat(model.wipe_tower.rotation));
Tab *tab_print = wxGetApp().get_tab(Preset::TYPE_PRINT);
tab_print->load_config(new_config);
tab_print->update_dirty();
}
}
// set selection mode for ObjectList on sidebar
this->sidebar->obj_list()->set_selection_mode(new_selected_settings_on_sidebar ? ObjectList::SELECTION_MODE::smSettings :
new_selected_layer_on_sidebar ? ObjectList::SELECTION_MODE::smLayer :

View File

@ -1635,10 +1635,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Wipe tower"));
optgroup->append_single_option_line("wipe_tower");
optgroup->append_single_option_line("wipe_tower_x");
optgroup->append_single_option_line("wipe_tower_y");
optgroup->append_single_option_line("wipe_tower_width");
optgroup->append_single_option_line("wipe_tower_rotation_angle");
optgroup->append_single_option_line("wipe_tower_width");
optgroup->append_single_option_line("wipe_tower_brim_width");
optgroup->append_single_option_line("wipe_tower_bridging");
optgroup->append_single_option_line("wipe_tower_cone_angle");