///|/ Copyright (c) Prusa Research 2022 - 2023 Pavel Mikuš @Godrak ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #ifndef SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ #define SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ #include "Layer.hpp" #include "Line.hpp" #include "PrintBase.hpp" #include "PrintConfig.hpp" #include #include #include namespace Slic3r { namespace SupportSpotsGenerator { struct Params { Params( const std::vector &filament_types, float max_acceleration, int raft_layers_count, BrimType brim_type, float brim_width) : max_acceleration(max_acceleration), raft_layers_count(raft_layers_count), brim_type(brim_type), brim_width(brim_width) { if (filament_types.size() > 1) { BOOST_LOG_TRIVIAL(warning) << "SupportSpotsGenerator does not currently handle different materials properly, only first will be used"; } if (filament_types.empty() || filament_types[0].empty()) { BOOST_LOG_TRIVIAL(error) << "SupportSpotsGenerator error: empty filament_type"; filament_type = std::string("PLA"); } else { filament_type = filament_types[0]; BOOST_LOG_TRIVIAL(debug) << "SupportSpotsGenerator: applying filament type: " << filament_type; } } // the algorithm should use the following units for all computations: distance [mm], mass [g], time [s], force [g*mm/s^2] const float bridge_distance = 16.0f; // mm const float max_acceleration; // mm/s^2 ; max acceleration of object in XY -- should be applicable only to printers with bed slinger, // however we do not have such info yet. The force is usually small anyway, so not such a big deal to include it everytime const int raft_layers_count; std::string filament_type; BrimType brim_type; const float brim_width; const std::pair malformation_distance_factors = std::pair { 0.2, 1.1 }; const float max_curled_height_factor = 10.0f; const float curling_tolerance_limit = 0.1f; const float min_distance_between_support_points = 3.0f; //mm const float support_points_interface_radius = 1.5f; // mm const float min_distance_to_allow_local_supports = 1.0f; //mm const float gravity_constant = 9806.65f; // mm/s^2; gravity acceleration on Earth's surface, algorithm assumes that printer is in upwards position. const double filament_density = 1.25e-3f; // g/mm^3 ; Common filaments are very lightweight, so precise number is not that important const double material_yield_strength = 33.0f * 1e6f; // (g*mm/s^2)/mm^2; 33 MPa is yield strength of ABS, which has the lowest yield strength from common materials. const float standard_extruder_conflict_force = 10.0f * gravity_constant; // force that can occasionally push the model due to various factors (filament leaks, small curling, ... ); const float malformations_additive_conflict_extruder_force = 65.0f * gravity_constant; // for areas with possible high layered curled filaments // MPa * 1e^6 = (g*mm/s^2)/mm^2 = g/(mm*s^2); yield strength of the bed surface double get_bed_adhesion_yield_strength() const { if (raft_layers_count > 0) { return get_support_spots_adhesion_strength() * 2.0; } if (filament_type == "PLA") { return 0.02 * 1e6; } else if (filament_type == "PET" || filament_type == "PETG") { return 0.3 * 1e6; } else if (filament_type == "ABS" || filament_type == "ASA") { return 0.1 * 1e6; //TODO do measurements } else { //PLA default value - defensive approach, PLA has quite low adhesion return 0.02 * 1e6; } } double get_support_spots_adhesion_strength() const { return 0.016f * 1e6; } }; enum class SupportPointCause { LongBridge, // point generated on bridge and straight perimeter extrusion longer than the allowed length FloatingBridgeAnchor, // point generated on unsupported bridge endpoint FloatingExtrusion, // point generated on extrusion that does not hold on its own SeparationFromBed, // point generated for object parts that are connected to the bed, but the area is too small and there is a risk of separation (brim may help) UnstableFloatingPart, // point generated for object parts not connected to the bed, holded only by the other support points (brim will not help here) WeakObjectPart // point generated when some part of the object is too weak to hold the upper part and may break (imagine hourglass) }; // The support points can be sorted into two groups // 1. Local extrusion support for extrusions that are printed in the air and would not // withstand on their own (too long bridges, sharp turns in large overhang, concave bridge holes, etc.) // These points have negative force (-EPSILON) and Vec2f::Zero() direction // The algorithm still expects that these points will be supported and accounts for them in the global stability check. // 2. Global stability support points are generated at each spot, where the algorithm detects that extruding the current line // may cause separation of the object part from the bed and/or its support spots or crack in the weak connection of the object parts. // The generated point's direction is the estimated falling direction of the object part, and the force is equal to te difference // between forces that destabilize the object (extruder conflicts with curled filament, weight if instable center of mass, bed movements etc) // and forces that stabilize the object (bed adhesion, other support spots adhesion, weight if stable center of mass). // Note that the force is only the difference - the amount needed to stabilize the object again. struct SupportPoint { SupportPoint(SupportPointCause cause, const Vec3f &position, float force, float spot_radius, const Vec2f &direction) : cause(cause), position(position), force(force), spot_radius(spot_radius), direction(direction) {} bool is_local_extrusion_support() const { return cause == SupportPointCause::LongBridge || cause == SupportPointCause::FloatingExtrusion; } bool is_global_object_support() const { return !is_local_extrusion_support(); } SupportPointCause cause; // reason why this support point was generated. Used for the user alerts // position is in unscaled coords. The z coordinate is aligned with the layers bottom_z coordiantes Vec3f position; // force that destabilizes the object to the point of falling/breaking. g*mm/s^2 units // It is valid only for global_object_support. For local extrusion support points, the force is -EPSILON // values gathered from large XL model: Min : 0 | Max : 18713800 | Average : 1361186 | Median : 329103 // For reference 18713800 is weight of 1.8 Kg object, 329103 is weight of 0.03 Kg // The final sliced object weight was approx 0.5 Kg float force; // Expected spot size. The support point strength is calculated from the area defined by this value. // Currently equal to the support_points_interface_radius parameter above float spot_radius; // direction of the fall of the object (z part is neglected) Vec2f direction; }; using SupportPoints = std::vector; struct Malformations { std::vector layers; //for each layer }; struct PartialObject { PartialObject(Vec3f centroid, float volume, bool connected_to_bed) : centroid(centroid), volume(volume), connected_to_bed(connected_to_bed) {} Vec3f centroid; float volume; bool connected_to_bed; }; /** * Unsacled values of integrals over a polygonal domain. */ class Integrals{ public: /** * Construct integral x_i int x_i^2 (i=1,2), xy and integral 1 (area). * * @param polygons List of polygons specifing the domain. */ explicit Integrals(const Polygons& polygons); // TODO refactor and delete the default constructor Integrals() = default; float area{}; Vec2f x_i{Vec2f::Zero()}; Vec2f x_i_squared{Vec2f::Zero()}; float xy{}; }; float compute_second_moment( const Integrals& integrals, const Vec2f& axis_direction ); class ExtrusionLine { public: ExtrusionLine(); ExtrusionLine(const Vec2f &a, const Vec2f &b, float len, const ExtrusionEntity *origin_entity); ExtrusionLine(const Vec2f &a, const Vec2f &b); bool is_external_perimeter() const; Vec2f a; Vec2f b; float len; const ExtrusionEntity *origin_entity; std::optional support_point_generated = {}; float form_quality = 1.0f; float curled_up_height = 0.0f; static const constexpr int Dim = 2; using Scalar = Vec2f::Scalar; }; struct SliceConnection { float area{}; Vec3f centroid_accumulator = Vec3f::Zero(); Vec2f second_moment_of_area_accumulator = Vec2f::Zero(); float second_moment_of_area_covariance_accumulator{}; void add(const SliceConnection &other); void print_info(const std::string &tag) const; }; Polygons get_brim(const ExPolygon& slice_polygon, const BrimType brim_type, const float brim_width); class ObjectPart { public: float volume{}; Vec3f volume_centroid_accumulator = Vec3f::Zero(); float sticking_area{}; Vec3f sticking_centroid_accumulator = Vec3f::Zero(); Vec2f sticking_second_moment_of_area_accumulator = Vec2f::Zero(); float sticking_second_moment_of_area_covariance_accumulator{}; bool connected_to_bed = false; ObjectPart( const std::vector& extrusion_collections, const bool connected_to_bed, const coordf_t print_head_z, const coordf_t layer_height, const std::optional& brim ); void add(const ObjectPart &other); void add_support_point(const Vec3f &position, float sticking_area); float compute_elastic_section_modulus( const Vec2f &line_dir, const Vec3f &extreme_point, const Integrals& integrals ) const; std::tuple is_stable_while_extruding(const SliceConnection &connection, const ExtrusionLine &extruded_line, const Vec3f &extreme_point, float layer_z, const Params ¶ms) const; }; using PartialObjects = std::vector; // Both support points and partial objects are sorted from the lowest z to the highest std::tuple full_search(const PrintObject *po, const PrintTryCancel& cancel_func, const Params ¶ms); void estimate_supports_malformations(std::vector &layers, float supports_flow_width, const Params ¶ms); void estimate_malformations(std::vector &layers, const Params ¶ms); // NOTE: the boolean marks if the issue is critical or not for now. std::vector> gather_issues(const SupportPoints &support_points, PartialObjects &partial_objects); }} // namespace Slic3r::SupportSpotsGenerator #endif /* SRC_LIBSLIC3R_SUPPORTABLEISSUESSEARCH_HPP_ */