diff --git a/src/libslic3r/GCode/ConflictChecker.cpp b/src/libslic3r/GCode/ConflictChecker.cpp index 8226c6c9ec..21a4e2dc08 100644 --- a/src/libslic3r/GCode/ConflictChecker.cpp +++ b/src/libslic3r/GCode/ConflictChecker.cpp @@ -94,6 +94,91 @@ inline Grids line_rasterization(const Line &line, int64_t xdist = RasteXDistance } } // namespace RasterizationImpl + + +static std::vector getFakeExtrusionPathsFromWipeTower(const WipeTowerData& wtd) +{ + float h = wtd.height; + float lh = wtd.first_layer_height; + int d = scale_(wtd.depth); + int w = scale_(wtd.width); + int bd = scale_(wtd.brim_width); + Point minCorner = { -wtd.brim_width, -wtd.brim_width }; + Point maxCorner = { minCorner.x() + w + bd, minCorner.y() + d + bd }; + float width = wtd.width; + float depth = wtd.depth; + float height = wtd.height; + float cone_angle = wtd.cone_angle; + const auto& z_and_depth_pairs = wtd.z_and_depth_pairs; + + const auto [cone_base_R, cone_scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth, cone_angle); + + std::vector paths; + for (float hh = 0.f; hh < h; hh += lh) { + + if (hh != 0.f) { + // The wipe tower may be getting smaller. Find the depth for this layer. + size_t i = 0; + for (i=0; i= z_and_depth_pairs[i].first && hh < z_and_depth_pairs[i+1].first) + break; + d = scale_(z_and_depth_pairs[i].second); + minCorner = {0.f, -d/2 + scale_(z_and_depth_pairs.front().second/2.f)}; + maxCorner = { minCorner.x() + w, minCorner.y() + d }; + } + + + ExtrusionPath path({ minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }, + ExtrusionAttributes{ ExtrusionRole::WipeTower, ExtrusionFlow{ 0.0, 0.0, lh } }); + paths.push_back({ path }); + + // We added the border, now add several parallel lines so we can detect an object that is fully inside the tower. + // For now, simply use fixed spacing of 3mm. + for (coord_t y=minCorner.y()+scale_(3.); y 0.) { + path.polyline.clear(); + double r = cone_base_R * (1 - hh/height); + for (double alpha=0; alpha<2.01*M_PI; alpha+=2*M_PI/20.) + path.polyline.points.emplace_back(Point::new_scale(width/2. + r * std::cos(alpha)/cone_scale_x, depth/2. + r * std::sin(alpha))); + paths.back().emplace_back(path); + if (hh == 0.f) { // Cone brim. + for (float bw=wtd.brim_width; bw>0.f; bw-=3.f) { + path.polyline.clear(); + for (double alpha=0; alpha<2.01*M_PI; alpha+=2*M_PI/20.) // see load_wipe_tower_preview, where the same is a bit clearer + path.polyline.points.emplace_back(Point::new_scale( + width/2. + cone_base_R * std::cos(alpha)/cone_scale_x * (1. + cone_scale_x*bw/cone_base_R), + depth/2. + cone_base_R * std::sin(alpha) * (1. + bw/cone_base_R)) + ); + paths.back().emplace_back(path); + } + } + } + + // Only the first layer has brim. + if (hh == 0.f) { + minCorner = minCorner + Point(bd, bd); + maxCorner = maxCorner - Point(bd, bd); + } + } + + // Rotate and translate the tower into the final position. + for (ExtrusionPaths& ps : paths) { + for (ExtrusionPath& p : ps) { + p.polyline.rotate(Geometry::deg2rad(wtd.rotation_angle)); + p.polyline.translate(scale_(wtd.position.x()), scale_(wtd.position.y())); + } + } + + return paths; +} + + + void LinesBucketQueue::emplace_back_bucket(std::vector &&paths, const void *objPtr, Points offsets) { if (_objsPtrToId.find(objPtr) == _objsPtrToId.end()) { @@ -209,14 +294,20 @@ ConflictComputeOpt ConflictChecker::find_inter_of_lines(const LineWithIDs &lines } ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, - std::optional wtdptr) // find the first intersection point of lines in different objects + const WipeTowerData& wipe_tower_data) // find the first intersection point of lines in different objects { if (objs.empty() || (objs.size() == 1 && objs.front()->instances().size() == 1)) { return {}; } + // The code ported from BS uses void* to identify objects... + // Let's use the address of this variable to represent the wipe tower. + int wtptr = 0; + LinesBucketQueue conflictQueue; - if (wtdptr.has_value()) { // wipe tower at 0 by default - std::vector wtpaths = (*wtdptr)->getFakeExtrusionPathsFromWipeTower(); - conflictQueue.emplace_back_bucket(std::move(wtpaths), *wtdptr, Points{Point((*wtdptr)->plate_origin)}); + if (! wipe_tower_data.z_and_depth_pairs.empty()) { + // The wipe tower is being generated. + const Vec2d plate_origin = Vec2d::Zero(); + std::vector wtpaths = getFakeExtrusionPathsFromWipeTower(wipe_tower_data); + conflictQueue.emplace_back_bucket(std::move(wtpaths), &wtptr, Points{Point(plate_origin)}); } for (PrintObject *obj : objs) { std::pair, std::vector> layers = getAllLayersExtrusionPathsFromObject(obj); @@ -261,13 +352,11 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP const void *ptr1 = conflictQueue.idToObjsPtr(conflict[0].first._obj1); const void *ptr2 = conflictQueue.idToObjsPtr(conflict[0].first._obj2); double conflictHeight = conflict[0].second; - if (wtdptr.has_value()) { - const FakeWipeTower* wtdp = *wtdptr; - if (ptr1 == wtdp || ptr2 == wtdp) { - if (ptr2 == wtdp) { std::swap(ptr1, ptr2); } - const PrintObject *obj2 = reinterpret_cast(ptr2); - return std::make_optional("WipeTower", obj2->model_object()->name, conflictHeight, nullptr, ptr2); - } + if (ptr1 == &wtptr || ptr2 == &wtptr) { + assert(! wipe_tower_data.z_and_depth_pairs.empty()); + if (ptr2 == &wtptr) { std::swap(ptr1, ptr2); } + const PrintObject *obj2 = reinterpret_cast(ptr2); + return std::make_optional("WipeTower", obj2->model_object()->name, conflictHeight, nullptr, ptr2); } const PrintObject *obj1 = reinterpret_cast(ptr1); const PrintObject *obj2 = reinterpret_cast(ptr2); diff --git a/src/libslic3r/GCode/ConflictChecker.hpp b/src/libslic3r/GCode/ConflictChecker.hpp index 0fee49ed0d..2d4e830dfb 100644 --- a/src/libslic3r/GCode/ConflictChecker.hpp +++ b/src/libslic3r/GCode/ConflictChecker.hpp @@ -124,7 +124,7 @@ using ConflictObjName = std::optional>; struct ConflictChecker { - static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, std::optional wtdptr); + static ConflictResultOpt find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs, const WipeTowerData& wtd); static ConflictComputeOpt find_inter_of_lines(const LineWithIDs &lines); static ConflictComputeOpt line_intersect(const LineWithID &l1, const LineWithID &l2); }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index cbc9404e84..2264de546f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1011,12 +1011,13 @@ void Print::process() this->set_done(psSkirtBrim); } - std::optional wipe_tower_opt = {}; if (this->has_wipe_tower()) { - m_fake_wipe_tower.set_pos_and_rotation({ m_config.wipe_tower_x, m_config.wipe_tower_y }, m_config.wipe_tower_rotation_angle); - wipe_tower_opt = std::make_optional(&m_fake_wipe_tower); + // These values have to be updated here, not during wipe tower generation. + // When the wipe tower is moved, 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; } - auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt); + auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, m_wipe_tower_data); m_conflict_result = conflictRes; if (conflictRes.has_value()) @@ -1505,9 +1506,6 @@ void Print::_make_wipe_tower() // Initialize the wipe tower. WipeTower wipe_tower(m_config, m_default_region_config, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder()); - //wipe_tower.set_retract(); - //wipe_tower.set_zhop(); - // Set the extruder & material properties at the wipe tower object. for (size_t i = 0; i < m_config.nozzle_diameter.size(); ++ i) wipe_tower.set_extruder(i, m_config); @@ -1576,10 +1574,9 @@ void Print::_make_wipe_tower() m_wipe_tower_data.used_filament = wipe_tower.get_used_filament(); m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); - const Vec3d origin = Vec3d::Zero(); - m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_wipe_tower_height(), config().first_layer_height, m_wipe_tower_data.depth, - m_wipe_tower_data.z_and_depth_pairs, m_wipe_tower_data.brim_width, config().wipe_tower_rotation_angle, config().wipe_tower_cone_angle, {scale_(origin.x()), scale_(origin.y())}); - + m_wipe_tower_data.width = wipe_tower.width(); + m_wipe_tower_data.first_layer_height = config().first_layer_height; + m_wipe_tower_data.cone_angle = config().wipe_tower_cone_angle; } // Generate a recommended G-code output file name based on the format template, default extension, and template parameters @@ -1668,80 +1665,5 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co return final_path; } - std::vector FakeWipeTower::getFakeExtrusionPathsFromWipeTower() const - { - float h = height; - float lh = layer_height; - int d = scale_(depth); - int w = scale_(width); - int bd = scale_(brim_width); - Point minCorner = { -bd, -bd }; - Point maxCorner = { minCorner.x() + w + bd, minCorner.y() + d + bd }; - - const auto [cone_base_R, cone_scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth, cone_angle); - - std::vector paths; - for (float hh = 0.f; hh < h; hh += lh) { - - if (hh != 0.f) { - // The wipe tower may be getting smaller. Find the depth for this layer. - size_t i = 0; - for (i=0; i= z_and_depth_pairs[i].first && hh < z_and_depth_pairs[i+1].first) - break; - d = scale_(z_and_depth_pairs[i].second); - minCorner = {0.f, -d/2 + scale_(z_and_depth_pairs.front().second/2.f)}; - maxCorner = { minCorner.x() + w, minCorner.y() + d }; - } - - - ExtrusionPath path({ minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner }, - ExtrusionAttributes{ ExtrusionRole::WipeTower, ExtrusionFlow{ 0.0, 0.0, lh } }); - paths.push_back({ path }); - - // We added the border, now add several parallel lines so we can detect an object that is fully inside the tower. - // For now, simply use fixed spacing of 3mm. - for (coord_t y=minCorner.y()+scale_(3.); y 0.) { - path.polyline.clear(); - double r = cone_base_R * (1 - hh/height); - for (double alpha=0; alpha<2.01*M_PI; alpha+=2*M_PI/20.) - path.polyline.points.emplace_back(Point::new_scale(width/2. + r * std::cos(alpha)/cone_scale_x, depth/2. + r * std::sin(alpha))); - paths.back().emplace_back(path); - if (hh == 0.f) { // Cone brim. - for (float bw=brim_width; bw>0.f; bw-=3.f) { - path.polyline.clear(); - for (double alpha=0; alpha<2.01*M_PI; alpha+=2*M_PI/20.) // see load_wipe_tower_preview, where the same is a bit clearer - path.polyline.points.emplace_back(Point::new_scale( - width/2. + cone_base_R * std::cos(alpha)/cone_scale_x * (1. + cone_scale_x*bw/cone_base_R), - depth/2. + cone_base_R * std::sin(alpha) * (1. + bw/cone_base_R)) - ); - paths.back().emplace_back(path); - } - } - } - - // Only the first layer has brim. - if (hh == 0.f) { - minCorner = minCorner + Point(bd, bd); - maxCorner = maxCorner - Point(bd, bd); - } - } - - // Rotate and translate the tower into the final position. - for (ExtrusionPaths& ps : paths) { - for (ExtrusionPath& p : ps) { - p.polyline.rotate(Geometry::deg2rad(rotation_angle)); - p.polyline.translate(scale_(pos.x()), scale_(pos.y())); - } - } - - return paths; - } } // namespace Slic3r diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 8e55044531..7e5e5fecdf 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -439,38 +439,7 @@ private: FillLightning::GeneratorPtr m_lightning_generator; }; -struct FakeWipeTower -{ - // generate fake extrusion - Vec2f pos; - float width; - float height; - float layer_height; - float depth; - std::vector> z_and_depth_pairs; - float brim_width; - float rotation_angle; - float cone_angle; - Vec2d plate_origin; - void set_fake_extrusion_data(const Vec2f& p, float w, float h, float lh, float d, const std::vector>& zad, float bd, float ra, float ca, const Vec2d& o) - { - pos = p; - width = w; - height = h; - layer_height = lh; - depth = d; - z_and_depth_pairs = zad; - brim_width = bd; - rotation_angle = ra; - cone_angle = ca; - plate_origin = o; - } - - void set_pos_and_rotation(const Vec2f& p, float rotation) { pos = p; rotation_angle = rotation; } - - std::vector getFakeExtrusionPathsFromWipeTower() const; -}; struct WipeTowerData { @@ -491,6 +460,13 @@ struct WipeTowerData float brim_width; float height; + // Data needed to generate fake extrusions for conflict checking. + float width; + float first_layer_height; + float cone_angle; + Vec2d position; + float rotation_angle; + void clear() { priming.reset(nullptr); tool_changes.clear(); @@ -499,6 +475,11 @@ struct WipeTowerData number_of_toolchanges = -1; depth = 0.f; brim_width = 0.f; + width = 0.f; + first_layer_height = 0.f; + cone_angle = 0.f; + position = Vec2d::Zero(); + rotation_angle = 0.f; } private: @@ -740,7 +721,6 @@ private: friend class PrintObject; ConflictResultOpt m_conflict_result; - FakeWipeTower m_fake_wipe_tower; }; } /* slic3r_Print_hpp_ */