// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine. // Original source of Thomas Rahm's tree supports: // https://github.com/ThomasRahm/CuraEngine // // Original CuraEngine copyright: // Copyright (c) 2021 Ultimaker B.V. // CuraEngine is released under the terms of the AGPLv3 or higher. #include "TreeSupportCommon.hpp" namespace Slic3r::FFFTreeSupport { TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object) { const PrintConfig &print_config = print_object.print()->config(); const PrintObjectConfig &config = print_object.config(); const SlicingParameters &slicing_params = print_object.slicing_parameters(); // const std::vector<unsigned int> printing_extruders = print_object.object_extruders(); // Support must be enabled and set to Tree style. assert(config.support_material || config.support_material_enforce_layers > 0); assert(config.support_material_style == smsTree || config.support_material_style == smsOrganic); // Calculate maximum external perimeter width over all printing regions, taking into account the default layer height. coordf_t external_perimeter_width = 0.; for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) { const PrintRegion ®ion = print_object.printing_region(region_id); external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width()); } this->layer_height = scaled<coord_t>(config.layer_height.value); this->resolution = scaled<coord_t>(print_config.gcode_resolution.value); // Arache feature this->min_feature_size = scaled<coord_t>(config.min_feature_size.value); // +1 makes the threshold inclusive this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_material_threshold + 1) * M_PI / 180., 0., 0.5 * M_PI); this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width(); this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width(); //FIXME add it to SlicingParameters and reuse in both tree and normal supports? this->support_bottom_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value != 0; this->support_bottom_height = this->support_bottom_enable ? (config.support_material_bottom_interface_layers.value > 0 ? config.support_material_bottom_interface_layers.value : config.support_material_interface_layers.value) * this->layer_height : 0; this->support_material_buildplate_only = config.support_material_buildplate_only; this->support_xy_distance = scaled<coord_t>(config.support_material_xy_spacing.get_abs_value(external_perimeter_width)); // Separation of interfaces, it is likely smaller than support_xy_distance. this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width)); this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object); this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support); // this->support_interface_skip_height = // this->support_infill_angles = this->support_roof_enable = config.support_material_interface_layers.value > 0; this->support_roof_layers = this->support_roof_enable ? config.support_material_interface_layers.value : 0; this->support_floor_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value > 0; this->support_floor_layers = this->support_floor_enable ? config.support_material_bottom_interface_layers.value : 0; // this->minimum_roof_area = // this->support_roof_angles = this->support_roof_pattern = config.support_material_interface_pattern; this->support_pattern = config.support_material_pattern; this->support_line_spacing = scaled<coord_t>(config.support_material_spacing.value); // this->support_bottom_offset = // this->support_wall_count = config.support_material_with_sheath ? 1 : 0; this->support_wall_count = 1; this->support_roof_line_distance = scaled<coord_t>(config.support_material_interface_spacing.value) + this->support_roof_line_width; // this->minimum_support_area = // this->minimum_bottom_area = // this->support_offset = this->support_tree_branch_distance = scaled<coord_t>(config.support_tree_branch_distance.value); this->support_tree_angle = std::clamp<double>(config.support_tree_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_angle_slow = std::clamp<double>(config.support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON); this->support_tree_branch_diameter = scaled<coord_t>(config.support_tree_branch_diameter.value); this->support_tree_branch_diameter_angle = std::clamp<double>(config.support_tree_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_top_rate = config.support_tree_top_rate.value; // percent // this->support_tree_tip_diameter = this->support_line_width; this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.support_tree_tip_diameter.value), 0, this->support_tree_branch_diameter); } TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params) : angle(mesh_group_settings.support_tree_angle), angle_slow(mesh_group_settings.support_tree_angle_slow), support_line_width(mesh_group_settings.support_line_width), layer_height(mesh_group_settings.layer_height), branch_radius(mesh_group_settings.support_tree_branch_diameter / 2), min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()), maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()), support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0), tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height), max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2), min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)), increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2), increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer), support_rests_on_model(! mesh_group_settings.support_material_buildplate_only), xy_distance(mesh_group_settings.support_xy_distance), xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)), bp_radius(mesh_group_settings.support_tree_bp_diameter / 2), // Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius). bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)), z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))), z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))), // support_infill_angles(mesh_group_settings.support_infill_angles), support_roof_angles(mesh_group_settings.support_roof_angles), roof_pattern(mesh_group_settings.support_roof_pattern), support_pattern(mesh_group_settings.support_pattern), support_roof_line_width(mesh_group_settings.support_roof_line_width), support_line_spacing(mesh_group_settings.support_line_spacing), support_bottom_offset(mesh_group_settings.support_bottom_offset), support_wall_count(mesh_group_settings.support_wall_count), resolution(mesh_group_settings.resolution), support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference. settings(mesh_group_settings), min_feature_size(mesh_group_settings.min_feature_size) { layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer; if (TreeSupportSettings::soluble) { // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1)); xy_distance = std::max(xy_distance, xy_min_distance); } // const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } }; // interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority")); //FIXME this was the default // interface_preference = InterfacePreference::SupportLinesOverwriteInterface; //interface_preference = InterfacePreference::SupportAreaOverwritesInterface; interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport; if (slicing_params.raft_layers() > 0) { // Fill in raft_layers with the heights of the layers below the first object layer. // First layer double z = slicing_params.first_print_layer_height; this->raft_layers.emplace_back(z); // Raft base layers for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { z += slicing_params.base_raft_layer_height; this->raft_layers.emplace_back(z); } // Raft interface layers for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) { z += slicing_params.interface_raft_layer_height; this->raft_layers.emplace_back(z); } // Raft contact layer if (slicing_params.raft_layers() > 1) { z = slicing_params.raft_contact_top_z; this->raft_layers.emplace_back(z); } if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) { // Layers between the raft contacts and bottom of the object. auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height)); double step = dist_to_go / nsteps; for (size_t i = 0; i < nsteps; ++ i) { z += step; this->raft_layers.emplace_back(z); } } } } #if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32) #define TREE_SUPPORT_SHOW_ERRORS_WIN32 #include <windows.h> #endif // Shared with generate_support_areas() bool g_showed_critical_error = false; bool g_showed_performance_warning = false; void tree_supports_show_error(std::string_view message, bool critical) { // todo Remove! ONLY FOR PUBLIC BETA!! printf("Error: %s, critical: %d\n", message.data(), int(critical)); #ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32 static bool showed_critical = false; static bool showed_performance = false; auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n"); bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning); (critical ? g_showed_critical_error : g_showed_performance_warning) = true; if (show) MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(), "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING); #endif // TREE_SUPPORT_SHOW_ERRORS_WIN32 } } // namespace Slic3r::FFFTreeSupport