Vectorization of the wipe tower

This commit is contained in:
Lukas Matena 2024-10-19 22:55:33 +02:00
parent da13a0a80e
commit fae06e0773
17 changed files with 117 additions and 97 deletions

View File

@ -567,7 +567,7 @@ void ArrangeableSlicerModel::for_each_arrangeable_(Self &&self, Fn &&fn)
template<class Self, class Fn> template<class Self, class Fn>
void ArrangeableSlicerModel::visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn) void ArrangeableSlicerModel::visit_arrangeable_(Self &&self, const ObjectID &id, Fn &&fn)
{ {
if (id == self.m_model->wipe_tower.id()) { if (id == wipe_tower_instance_id(0)) {
self.m_wth->visit(fn); self.m_wth->visit(fn);
return; return;

View File

@ -47,6 +47,8 @@ namespace pt = boost::property_tree;
#include "libslic3r/NSVGUtils.hpp" #include "libslic3r/NSVGUtils.hpp"
#include "libslic3r/MultipleBeds.hpp"
#include <fast_float.h> #include <fast_float.h>
// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter, // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
@ -773,7 +775,7 @@ namespace Slic3r {
} }
// Initialize the wipe tower position (see the end of this function): // Initialize the wipe tower position (see the end of this function):
model.wipe_tower.position.x() = std::numeric_limits<double>::max(); model.get_wipe_tower_vector().front().position.x() = std::numeric_limits<double>::max();
// Read root model file // Read root model file
if (start_part_stat.m_file_index < num_entries) { if (start_part_stat.m_file_index < num_entries) {
@ -850,13 +852,13 @@ namespace Slic3r {
} }
if (model.wipe_tower.position.x() == std::numeric_limits<double>::max()) { if (model.get_wipe_tower_vector().front().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 // 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. // 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). // 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.get_wipe_tower_vector().front().position.x() = 180;
model.wipe_tower.position.y() = 140; model.get_wipe_tower_vector().front().position.y() = 140;
model.wipe_tower.rotation = 0.; model.get_wipe_tower_vector().front().rotation = 0.;
for (mz_uint i = 0; i < num_entries; ++i) { for (mz_uint i = 0; i < num_entries; ++i) {
if (mz_zip_reader_file_stat(&archive, i, &stat)) { if (mz_zip_reader_file_stat(&archive, i, &stat)) {
@ -1679,17 +1681,29 @@ namespace Slic3r {
pt::ptree main_tree; pt::ptree main_tree;
pt::read_xml(iss, main_tree); pt::read_xml(iss, main_tree);
try { for (const auto& bed_block : main_tree) {
auto& node = main_tree.get_child("wipe_tower_information"); if (bed_block.first != "wipe_tower_information")
double pos_x = node.get<double>("<xmlattr>.position_x"); continue;
double pos_y = node.get<double>("<xmlattr>.position_y"); try {
double rot_deg = node.get<double>("<xmlattr>.rotation_deg"); int bed_idx = 0;
model.wipe_tower.position = Vec2d(pos_x, pos_y); try {
model.wipe_tower.rotation = rot_deg; bed_idx = bed_block.second.get<int>("<xmlattr>.bed_idx");
} catch (const boost::property_tree::ptree_bad_path&) { } catch (const boost::property_tree::ptree_bad_path&) {
// Handles missing node or attribute. // Probably an old project with no bed_idx info - pretend that we saw 0.
add_error("Error while reading wipe tower information."); }
return; if (bed_idx >= int(m_model->get_wipe_tower_vector().size()))
continue;
double pos_x = bed_block.second.get<double>("<xmlattr>.position_x");
double pos_y = bed_block.second.get<double>("<xmlattr>.position_y");
double rot_deg = bed_block.second.get<double>("<xmlattr>.rotation_deg");
model.get_wipe_tower_vector()[bed_idx].position = Vec2d(pos_x, pos_y);
model.get_wipe_tower_vector()[bed_idx].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;
}
} }
} }
@ -1725,11 +1739,11 @@ namespace Slic3r {
value_ss >> val; value_ss >> val;
if (! value_ss.fail()) { if (! value_ss.fail()) {
if (boost::starts_with(line, "wipe_tower_x")) if (boost::starts_with(line, "wipe_tower_x"))
model.wipe_tower.position.x() = val; model.get_wipe_tower_vector().front().position.x() = val;
else if (boost::starts_with(line, "wipe_tower_y")) else if (boost::starts_with(line, "wipe_tower_y"))
model.wipe_tower.position.y() = val; model.get_wipe_tower_vector().front().position.y() = val;
else else
model.wipe_tower.rotation = val; model.get_wipe_tower_vector().front().rotation = val;
} }
} }
} }
@ -3621,11 +3635,11 @@ namespace Slic3r {
std::string opt_serialized; std::string opt_serialized;
if (key == "wipe_tower_x") if (key == "wipe_tower_x")
opt_serialized = float_to_string_decimal_point(model.wipe_tower.position.x()); opt_serialized = float_to_string_decimal_point(model.get_wipe_tower_vector().front().position.x());
else if (key == "wipe_tower_y") else if (key == "wipe_tower_y")
opt_serialized = float_to_string_decimal_point(model.wipe_tower.position.y()); opt_serialized = float_to_string_decimal_point(model.get_wipe_tower_vector().front().position.y());
else if (key == "wipe_tower_rotation_angle") else if (key == "wipe_tower_rotation_angle")
opt_serialized = float_to_string_decimal_point(model.wipe_tower.rotation); opt_serialized = float_to_string_decimal_point(model.get_wipe_tower_vector().front().rotation);
else else
opt_serialized = config.opt_serialize(key); opt_serialized = config.opt_serialize(key);
@ -3842,11 +3856,19 @@ bool _3MF_Exporter::_add_wipe_tower_information_file_to_archive( mz_zip_archive&
std::string out = ""; std::string out = "";
pt::ptree tree; pt::ptree tree;
pt::ptree& main_tree = tree.add("wipe_tower_information", "");
main_tree.put("<xmlattr>.position_x", model.wipe_tower.position.x()); size_t bed_idx = 0;
main_tree.put("<xmlattr>.position_y", model.wipe_tower.position.y()); for (const ModelWipeTower& wipe_tower : model.get_wipe_tower_vector()) {
main_tree.put("<xmlattr>.rotation_deg", model.wipe_tower.rotation); pt::ptree& main_tree = tree.add("wipe_tower_information", "");
main_tree.put("<xmlattr>.bed_idx", bed_idx);
main_tree.put("<xmlattr>.position_x", wipe_tower.position.x());
main_tree.put("<xmlattr>.position_y", wipe_tower.position.y());
main_tree.put("<xmlattr>.rotation_deg", wipe_tower.rotation);
++bed_idx;
if (bed_idx >= s_multiple_beds.get_number_of_beds())
break;
}
std::ostringstream oss; std::ostringstream oss;
boost::property_tree::write_xml(oss, tree); boost::property_tree::write_xml(oss, tree);

View File

@ -1314,7 +1314,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); std::vector<std::pair<coordf_t, ObjectsLayerToPrint>> layers_to_print = collect_layers_to_print(print);
// Prusa Multi-Material wipe tower. // Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) { if (has_wipe_tower && ! layers_to_print.empty()) {
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()); 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. // Set position for wipe tower generation.
Vec3d new_position = this->writer().get_position(); 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 // 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. // We need to get position and angle of the wipe tower to transform them to actual position.
Transform2d trafo = Transform2d trafo =
Eigen::Translation2d(print.model().wipe_tower.position.x(), print.model().wipe_tower.position.y()) * Eigen::Translation2d(print.model().wipe_tower().position.x(), print.model().wipe_tower().position.y()) *
Eigen::Rotation2Dd(Geometry::deg2rad(print.model().wipe_tower.rotation)); Eigen::Rotation2Dd(Geometry::deg2rad(print.model().wipe_tower().rotation));
BoundingBoxf bbox; BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) { for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {

View File

@ -70,7 +70,7 @@ Model& Model::assign_copy(const Model &rhs)
// copy custom code per height // copy custom code per height
this->custom_gcode_per_print_z_vector = rhs.custom_gcode_per_print_z_vector; this->custom_gcode_per_print_z_vector = rhs.custom_gcode_per_print_z_vector;
this->wipe_tower = rhs.wipe_tower; this->wipe_tower_vector = rhs.wipe_tower_vector;
return *this; return *this;
} }
@ -93,7 +93,7 @@ Model& Model::assign_copy(Model &&rhs)
// copy custom code per height // copy custom code per height
this->custom_gcode_per_print_z_vector = std::move(rhs.custom_gcode_per_print_z_vector); this->custom_gcode_per_print_z_vector = std::move(rhs.custom_gcode_per_print_z_vector);
this->wipe_tower = rhs.wipe_tower; this->wipe_tower_vector = rhs.wipe_tower_vector;
return *this; return *this;
} }
@ -120,6 +120,16 @@ void Model::update_links_bottom_up_recursive()
} }
} }
ModelWipeTower& Model::wipe_tower()
{
return const_cast<ModelWipeTower&>(const_cast<const Model*>(this)->wipe_tower());
}
const ModelWipeTower& Model::wipe_tower() const
{
return wipe_tower_vector[s_multiple_beds.get_active_bed()];
}
CustomGCode::Info& Model::custom_gcode_per_print_z() CustomGCode::Info& Model::custom_gcode_per_print_z()
{ {
return const_cast<CustomGCode::Info&>(const_cast<const Model*>(this)->custom_gcode_per_print_z()); return const_cast<CustomGCode::Info&>(const_cast<const Model*>(this)->custom_gcode_per_print_z());

View File

@ -1253,7 +1253,7 @@ private:
// Note: The following class does not have to inherit from ObjectID, it is currently // 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. // only used for arrangement. It might be good to refactor this in future.
class ModelWipeTower final : public ObjectBase class ModelWipeTower
{ {
public: public:
Vec2d position = Vec2d(180., 140.); Vec2d position = Vec2d(180., 140.);
@ -1265,25 +1265,9 @@ public:
// Assignment operator does not touch the ID! // Assignment operator does not touch the ID!
ModelWipeTower& operator=(const ModelWipeTower& rhs) { position = rhs.position; rotation = rhs.rotation; return *this; } ModelWipeTower& operator=(const ModelWipeTower& rhs) { position = rhs.position; rotation = rhs.rotation; return *this; }
private:
friend class cereal::access;
friend class UndoRedo::StackImpl;
friend class Model;
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
explicit ModelWipeTower() {} explicit ModelWipeTower() {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
explicit ModelWipeTower(int) : ObjectBase(-1) {}
// Copy constructor copies the ID.
explicit ModelWipeTower(const ModelWipeTower &cfg) = default; explicit ModelWipeTower(const ModelWipeTower &cfg) = default;
// 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. // For serialization / deserialization of ModelWipeTower composed into another class into the Undo / Redo stack as a separate object.
template<typename Archive> void serialize(Archive &ar) { ar(position, rotation); } template<typename Archive> void serialize(Archive &ar) { ar(position, rotation); }
}; };
@ -1301,14 +1285,21 @@ public:
ModelMaterialMap materials; ModelMaterialMap materials;
// Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
ModelObjectPtrs objects; ModelObjectPtrs objects;
// Wipe tower object.
ModelWipeTower wipe_tower; ModelWipeTower& wipe_tower();
const ModelWipeTower& wipe_tower() const;
std::vector<ModelWipeTower>& get_wipe_tower_vector() { return wipe_tower_vector; }
const std::vector<ModelWipeTower>& get_wipe_tower_vector() const { return wipe_tower_vector; }
CustomGCode::Info& custom_gcode_per_print_z(); CustomGCode::Info& custom_gcode_per_print_z();
const CustomGCode::Info& custom_gcode_per_print_z() const; const CustomGCode::Info& custom_gcode_per_print_z() const;
std::vector<CustomGCode::Info>& get_custom_gcode_per_print_z_vector() { return custom_gcode_per_print_z_vector; } std::vector<CustomGCode::Info>& get_custom_gcode_per_print_z_vector() { return custom_gcode_per_print_z_vector; }
private: private:
// Wipe tower object.
std::vector<ModelWipeTower> wipe_tower_vector = std::vector<ModelWipeTower>(MAX_NUMBER_OF_BEDS);
// Extensions for color print // Extensions for color print
std::vector<CustomGCode::Info> custom_gcode_per_print_z_vector = std::vector<CustomGCode::Info>(MAX_NUMBER_OF_BEDS); std::vector<CustomGCode::Info> custom_gcode_per_print_z_vector = std::vector<CustomGCode::Info>(MAX_NUMBER_OF_BEDS);
@ -1413,8 +1404,7 @@ private:
friend class cereal::access; friend class cereal::access;
friend class UndoRedo::StackImpl; friend class UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) { template<class Archive> void serialize(Archive &ar) {
Internal::StaticSerializationWrapper<ModelWipeTower> wipe_tower_wrapper(wipe_tower); ar(materials, objects, wipe_tower_vector);
ar(materials, objects, wipe_tower_wrapper);
} }
}; };

View File

@ -8,17 +8,19 @@ namespace Slic3r {
size_t ObjectBase::s_last_id = 0; size_t ObjectBase::s_last_id = 0;
// Unique object / instance ID for the wipe tower. struct WipeTowerId : public ObjectBase {
ObjectID wipe_tower_object_id() // Need to inherit because ObjectBase
{ // destructor is protected.
static ObjectBase mine; using ObjectBase::ObjectBase;
return mine.id(); };
}
ObjectID wipe_tower_instance_id() ObjectID wipe_tower_instance_id(size_t bed_idx)
{ {
static ObjectBase mine; static std::vector<WipeTowerId> mine;
return mine.id(); if (bed_idx >= mine.size()) {
mine.resize(bed_idx + 1);
}
return mine[bed_idx].id();
} }
ObjectWithTimestamp::Timestamp ObjectWithTimestamp::s_last_timestamp = 1; ObjectWithTimestamp::Timestamp ObjectWithTimestamp::s_last_timestamp = 1;

View File

@ -86,9 +86,6 @@ private:
static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); } static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); }
static size_t s_last_id; static size_t s_last_id;
friend ObjectID wipe_tower_object_id();
friend ObjectID wipe_tower_instance_id();
friend class cereal::access; friend class cereal::access;
friend class Slic3r::UndoRedo::StackImpl; friend class Slic3r::UndoRedo::StackImpl;
@ -135,8 +132,7 @@ private:
}; };
// Unique object / instance ID for the wipe tower. // Unique object / instance ID for the wipe tower.
extern ObjectID wipe_tower_object_id(); ObjectID wipe_tower_instance_id(size_t bed_idx);
extern ObjectID wipe_tower_instance_id();
} // namespace Slic3r } // namespace Slic3r

View File

@ -1048,8 +1048,8 @@ void Print::process()
if (this->has_wipe_tower()) { if (this->has_wipe_tower()) {
// These values have to be updated here, not during wipe tower generation. // These values have to be updated here, not during wipe tower generation.
// When the wipe tower is moved/rotated, it is not regenerated. // When the wipe tower is moved/rotated, it is not regenerated.
m_wipe_tower_data.position = model().wipe_tower.position; m_wipe_tower_data.position = model().wipe_tower().position;
m_wipe_tower_data.rotation_angle = model().wipe_tower.rotation; 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); auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(objects(), m_wipe_tower_data);
@ -1278,8 +1278,8 @@ Points Print::first_layer_wipe_tower_corners() const
pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha))); pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha)));
for (Vec2d& pt : pts) { for (Vec2d& pt : pts) {
pt = Eigen::Rotation2Dd(Geometry::deg2rad(model().wipe_tower.rotation)) * pt; pt = Eigen::Rotation2Dd(Geometry::deg2rad(model().wipe_tower().rotation)) * pt;
pt += model().wipe_tower.position; pt += model().wipe_tower().position;
pts_scaled.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); pts_scaled.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
} }
} }
@ -1553,7 +1553,7 @@ void Print::_make_wipe_tower()
this->throw_if_canceled(); this->throw_if_canceled();
// Initialize the wipe tower. // Initialize the wipe tower.
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()); 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. // Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < m_config.nozzle_diameter.size(); ++ i) for (size_t i = 0; i < m_config.nozzle_diameter.size(); ++ i)

View File

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

View File

@ -606,7 +606,7 @@ int GLVolumeCollection::load_wipe_tower_preview(
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
v.composite_id = GLVolume::CompositeID(INT_MAX, 0, 0); v.composite_id = GLVolume::CompositeID(INT_MAX, 0, 0);
v.geometry_id.first = 0; v.geometry_id.first = 0;
v.geometry_id.second = wipe_tower_instance_id().id; v.geometry_id.second = wipe_tower_instance_id(0).id;
v.is_wipe_tower = true; v.is_wipe_tower = true;
v.shader_outside_printer_detection_enabled = !size_unknown; v.shader_outside_printer_detection_enabled = !size_unknown;
return int(volumes.size() - 1); return int(volumes.size() - 1);

View File

@ -1698,8 +1698,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 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; const float brim_width = wipe_tower_data.brim_width;
if (depth != 0.) { if (depth != 0.) {
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, 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); max_z, config.wipe_tower_cone_angle, wxGetApp().plater()->model().wipe_tower().rotation, false, brim_width);
GLVolume* volume = m_shells.volumes.volumes.back(); GLVolume* volume = m_shells.volumes.volumes.back();
volume->color.a(0.25f); volume->color.a(0.25f);
volume->force_native_color = true; volume->force_native_color = true;

View File

@ -2454,10 +2454,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (extruders_count > 1 && wt && !co) { if (extruders_count > 1 && wt && !co) {
const float x = m_model->wipe_tower.position.x(); const float x = m_model->wipe_tower().position.x();
const float y = m_model->wipe_tower.position.y(); 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 w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_width"))->value;
const float a = m_model->wipe_tower.rotation; 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 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; const float ca = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_cone_angle"))->value;
@ -4042,7 +4042,7 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED)); post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
if (wipe_tower_origin != Vec3d::Zero()) { if (wipe_tower_origin != Vec3d::Zero()) {
m_model->wipe_tower.position = Vec2d(wipe_tower_origin[0], wipe_tower_origin[1]); m_model->wipe_tower().position = Vec2d(wipe_tower_origin[0], wipe_tower_origin[1]);
post_event(SimpleEvent(EVT_GLCANVAS_WIPETOWER_TOUCHED)); post_event(SimpleEvent(EVT_GLCANVAS_WIPETOWER_TOUCHED));
} }
@ -4088,8 +4088,8 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
const Vec3d offset = v->get_volume_offset(); const Vec3d offset = v->get_volume_offset();
Vec3d rot_unit_x = v->get_volume_transformation().get_matrix().linear() * Vec3d::UnitX(); 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()); double z_rot = std::atan2(rot_unit_x.y(), rot_unit_x.x());
m_model->wipe_tower.position = Vec2d(offset.x(), offset.y()); m_model->wipe_tower().position = Vec2d(offset.x(), offset.y());
m_model->wipe_tower.rotation = (180./M_PI) * z_rot; m_model->wipe_tower().rotation = (180. / M_PI) * z_rot;
} }
const int object_idx = v->object_idx(); const int object_idx = v->object_idx();
if (object_idx < 0 || (int)m_model->objects.size() <= object_idx) if (object_idx < 0 || (int)m_model->objects.size() <= object_idx)
@ -4406,9 +4406,9 @@ GLCanvas3D::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const
for (const GLVolume* vol : m_volumes.volumes) { for (const GLVolume* vol : m_volumes.volumes) {
if (vol->is_wipe_tower) { if (vol->is_wipe_tower) {
wti.m_pos = Vec2d(m_model->wipe_tower.position.x(), wti.m_pos = Vec2d(m_model->wipe_tower().position.x(),
m_model->wipe_tower.position.y()); m_model->wipe_tower().position.y());
wti.m_rotation = (M_PI/180.) * m_model->wipe_tower.rotation; wti.m_rotation = (M_PI/180.) * m_model->wipe_tower().rotation;
const BoundingBoxf3& bb = vol->bounding_box(); const BoundingBoxf3& bb = vol->bounding_box();
wti.m_bb = BoundingBoxf{to_2d(bb.min), to_2d(bb.max)}; wti.m_bb = BoundingBoxf{to_2d(bb.min), to_2d(bb.max)};
break; break;
@ -7000,8 +7000,8 @@ const SLAPrint* GLCanvas3D::sla_print() const
void GLCanvas3D::WipeTowerInfo::apply_wipe_tower(Vec2d pos, double rot) void GLCanvas3D::WipeTowerInfo::apply_wipe_tower(Vec2d pos, double rot)
{ {
wxGetApp().plater()->model().wipe_tower.position = pos; wxGetApp().plater()->model().wipe_tower().position = pos;
wxGetApp().plater()->model().wipe_tower.rotation = (180./M_PI) * rot; wxGetApp().plater()->model().wipe_tower().rotation = (180. / M_PI) * rot;
} }
void GLCanvas3D::RenderTimer::Notify() void GLCanvas3D::RenderTimer::Notify()

View File

@ -180,7 +180,7 @@ arr2::SceneBuilder build_scene(Plater &plater, ArrangeSelectionMode mode)
AnyPtr<WTH> wth; AnyPtr<WTH> wth;
if (wti) { if (wti) {
wth = std::make_unique<WTH>(plater.model().wipe_tower.id(), wti); wth = std::make_unique<WTH>(wipe_tower_instance_id(0), wti);
} }
if (plater.config()) { if (plater.config()) {

View File

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

View File

@ -1330,7 +1330,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (load_config) { if (load_config) {
this->model.get_custom_gcode_per_print_z_vector() = model.get_custom_gcode_per_print_z_vector(); this->model.get_custom_gcode_per_print_z_vector() = model.get_custom_gcode_per_print_z_vector();
this->model.wipe_tower = model.wipe_tower; this->model.get_wipe_tower_vector() = model.get_wipe_tower_vector();
} }
} }
@ -3388,13 +3388,13 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) void Plater::priv::on_wipetower_moved(Vec3dEvent &evt)
{ {
model.wipe_tower.position = Vec2d(evt.data[0], evt.data[1]); model.wipe_tower().position = Vec2d(evt.data[0], evt.data[1]);
} }
void Plater::priv::on_wipetower_rotated(Vec3dEvent& evt) void Plater::priv::on_wipetower_rotated(Vec3dEvent& evt)
{ {
model.wipe_tower.position = Vec2d(evt.data[0], evt.data[1]); model.wipe_tower().position = Vec2d(evt.data[0], evt.data[1]);
model.wipe_tower.rotation = Geometry::rad2deg(evt.data(2)); model.wipe_tower().rotation = Geometry::rad2deg(evt.data(2));
} }
void Plater::priv::on_update_geometry(Vec3dsEvent<2>&) void Plater::priv::on_update_geometry(Vec3dsEvent<2>&)

View File

@ -1001,7 +1001,7 @@ TEST_CASE("Test SceneBuilder", "[arrange2][integration]")
arr2::SceneBuilder bld; arr2::SceneBuilder bld;
Model mdl; Model mdl;
bld.set_model(mdl); bld.set_model(mdl);
bld.set_wipe_tower_handler(std::make_unique<arr2::MocWTH>(mdl.wipe_tower.id())); bld.set_wipe_tower_handler(std::make_unique<arr2::MocWTH>(wipe_tower_instance_id(0)));
WHEN("the selection mask is initialized as a fallback default in the created scene") WHEN("the selection mask is initialized as a fallback default in the created scene")
{ {
@ -1014,7 +1014,7 @@ TEST_CASE("Test SceneBuilder", "[arrange2][integration]")
bool wt_selected = false; bool wt_selected = false;
scene.model() scene.model()
.visit_arrangeable(mdl.wipe_tower.id(), .visit_arrangeable(wipe_tower_instance_id(0),
[&wt_selected]( [&wt_selected](
const arr2::Arrangeable &arrbl) { const arr2::Arrangeable &arrbl) {
wt_selected = arrbl.is_selected(); wt_selected = arrbl.is_selected();