mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-11 19:17:33 +00:00
feat(orient): auto-orient the largest overhang toward the cooling fan
New printer option fan_direction (undefine/left/right/both, default undefine) declares which side the auxiliary part-cooling airflow comes from. When set and the printer has an auxiliary fan, auto-orient adds a yaw rotation so the dominant overhang area faces the airflow, and newly added primitive shapes are pre-oriented the same way (except the Cube, whose axis-aligned bounding box the pressure-advance pattern calibration depends on). - FanDirection enum + fan_direction printer option (Accessory group, enabled only with auxiliary_fan) - orient engine: weighted overhang areas per candidate, yaw-direction search, vertical rotation applied on top of the primary orientation; the cooling weights are taken from the candidate actually chosen, including the flat-bottom tie-break - orient_for_cooling() for primitive placement - set fan_direction=left on H2C/H2D/H2D Pro/X1/X1E/P1S 0.4 profiles (X1C/H2S/P2S/X2D/Qidi X-Max 4 already carried the key, which now takes effect) With fan_direction unset or no auxiliary fan the vertical rotation stays identity and auto-orient results are unchanged; slicing and g-code are never affected.
This commit is contained in:
@@ -18740,3 +18740,12 @@ msgstr ""
|
||||
|
||||
msgid "Switch track at Filament Track Switch"
|
||||
msgstr ""
|
||||
|
||||
msgid "Fan direction"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cooling fan direction of the printer"
|
||||
msgstr ""
|
||||
|
||||
msgid "Both"
|
||||
msgstr ""
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"1",
|
||||
"6"
|
||||
],
|
||||
"fan_direction": "left",
|
||||
"hotend_cooling_rate": [
|
||||
"1.6",
|
||||
"1.6",
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"0x0,325x0,325x320,0x320",
|
||||
"25x0,350x0,350x320,25x320"
|
||||
],
|
||||
"fan_direction": "left",
|
||||
"hotend_heating_rate": [
|
||||
"3.6",
|
||||
"3.6",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"0x0,325x0,325x320,0x320",
|
||||
"25x0,350x0,350x320,25x320"
|
||||
],
|
||||
"fan_direction": "left",
|
||||
"hotend_heating_rate": [
|
||||
"3.6",
|
||||
"3.6",
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"extruder_variant_list": [
|
||||
"Direct Drive Standard,Direct Drive High Flow"
|
||||
],
|
||||
"fan_direction": "left",
|
||||
"long_retractions_when_cut": [
|
||||
"0",
|
||||
"0"
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"extruder_variant_list": [
|
||||
"Direct Drive Standard,Direct Drive High Flow"
|
||||
],
|
||||
"fan_direction": "left",
|
||||
"long_retractions_when_cut": [
|
||||
"0",
|
||||
"0"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"extruder_variant_list": [
|
||||
"Direct Drive Standard,Direct Drive High Flow"
|
||||
],
|
||||
"fan_direction": "left",
|
||||
"long_retractions_when_cut": [
|
||||
"0",
|
||||
"0"
|
||||
|
||||
@@ -27,19 +27,20 @@ namespace Slic3r {
|
||||
namespace orientation {
|
||||
|
||||
struct CostItems {
|
||||
float overhang;
|
||||
float bottom;
|
||||
float bottom_hull;
|
||||
float contour;
|
||||
float area_laf; // area_of_low_angle_faces
|
||||
float area_projected; // area of projected 2D profile
|
||||
float volume;
|
||||
float area_total; // total area of all faces
|
||||
float radius; // radius of bounding box
|
||||
float height_to_bottom_hull_ratio; // affects stability, the lower the better
|
||||
float unprintability;
|
||||
float overhang = 0;
|
||||
float bottom = 0;
|
||||
float bottom_hull = 0;
|
||||
float contour = 0;
|
||||
float area_laf = 0; // area_of_low_angle_faces
|
||||
float area_projected = 0; // area of projected 2D profile
|
||||
float volume = 0;
|
||||
float area_total = 0; // total area of all faces
|
||||
float radius = 0; // radius of bounding box
|
||||
float height_to_bottom_hull_ratio = 0; // affects stability, the lower the better
|
||||
float unprintability = 0;
|
||||
Eigen::VectorXf areas_cooling;
|
||||
CostItems(CostItems const & other) = default;
|
||||
CostItems() { memset(this, 0, sizeof(*this)); }
|
||||
CostItems() = default;
|
||||
static std::string field_names() {
|
||||
return " overhang, bottom, bothull, contour, A_laf, A_prj, unprintability";
|
||||
}
|
||||
@@ -68,10 +69,11 @@ public:
|
||||
Eigen::VectorXf z_max, z_max_hull; // max of projected z
|
||||
Eigen::VectorXf z_median; // median of projected z
|
||||
Eigen::VectorXf z_mean; // mean of projected z
|
||||
Eigen::VectorXf areas_cooling; // weighted areas for cool direction
|
||||
std::vector<Vec3f> face_normals;
|
||||
std::vector<Vec3f> face_normals_hull;
|
||||
OrientParams params;
|
||||
|
||||
bool has_cooling_fan = false;
|
||||
|
||||
std::vector< Vec3f> orientations; // Vec3f == stl_normal
|
||||
std::function<void(unsigned)> progressind = { }; // default empty indicator function
|
||||
@@ -85,6 +87,7 @@ public:
|
||||
orient_mesh = orient_mesh_;
|
||||
mesh = &orient_mesh->mesh;
|
||||
params = params_;
|
||||
has_cooling_fan = orient_mesh->has_cooling_fan;
|
||||
progressind = progressind_;
|
||||
params.ASCENT = cos(PI - orient_mesh->overhang_angle * PI / 180); // use per-object overhang angle
|
||||
|
||||
@@ -158,12 +161,14 @@ public:
|
||||
//To avoid flipping, we need to verify if there are orientations with same unprintability.
|
||||
Vec3f n1 = {0, 0, 1};
|
||||
auto best_orientation = results_vector[0].first;
|
||||
size_t best_index = 0;
|
||||
|
||||
for (int i = 1; i< results_vector.size()-1; i++) {
|
||||
if (abs(results_vector[i].second.unprintability - results_vector[0].second.unprintability) < EPSILON && abs(results_vector[0].first.dot(n1)-1) > EPSILON) {
|
||||
if (abs(results_vector[i].first.dot(n1)-1) < EPSILON*EPSILON) {
|
||||
if (abs(results_vector[i].first.dot(n1)-1) < EPSILON*EPSILON) {
|
||||
best_orientation = n1;
|
||||
break;
|
||||
best_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -172,6 +177,9 @@ public:
|
||||
|
||||
}
|
||||
|
||||
// cooling weights are per-orientation, so take them from the orientation actually chosen
|
||||
areas_cooling = results_vector[best_index].second.areas_cooling;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(6) << "best:" << best_orientation.transpose() << ", costs:" << results_vector[0].second.field_values();
|
||||
std::cout << std::fixed << std::setprecision(6) << "best:" << best_orientation.transpose() << ", costs:" << results_vector[0].second.field_values() << std::endl;
|
||||
|
||||
@@ -441,6 +449,19 @@ public:
|
||||
Eigen::MatrixXf laf_areas = ((normal_projection_abs.array() < params.LAF_MAX) * (normal_projection_abs.array() > params.LAF_MIN) * (z_max.array() > total_min_z + params.FIRST_LAY_H)).select(areas, 0);
|
||||
costs.area_laf = laf_areas.sum();
|
||||
|
||||
if (has_cooling_fan)
|
||||
{
|
||||
// Angle range of overhang faces requiring cooling
|
||||
float angle_thres_high = -0.6427f;
|
||||
float angle_thres_low = -0.97f;
|
||||
// compute the weighted overhang faces area
|
||||
Eigen::VectorXf ones_f = Eigen::VectorXf::Ones(mesh->facets_count());
|
||||
auto overhang_area_condition = (normal_projection.array() < angle_thres_high && normal_projection.array() > angle_thres_low).eval();
|
||||
Eigen::VectorXf areas_ = (overhang_area_condition * !bottom_condition_2nd).select(areas, 0);
|
||||
Eigen::VectorXf weighted_areas = areas_.cwiseProduct(ones_f - normal_projection);
|
||||
costs.areas_cooling = weighted_areas;
|
||||
}
|
||||
|
||||
// height to bottom_hull_area ratio
|
||||
//float total_max_z = z_projected.maxCoeff();
|
||||
//costs.height_to_bottom_hull_ratio = SQ(total_max_z) / (costs.bottom_hull + 1e-7);
|
||||
@@ -468,6 +489,67 @@ public:
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
Vec3d find_cooling_direction2(Vec3d euler_angles, const Eigen::VectorXf& areas_in, TriangleMesh& mesh)
|
||||
{
|
||||
Vec3f machine_cool_dir = this->orient_mesh->cooling_direction.cast<float>();
|
||||
const size_t num_faces = areas.rows();
|
||||
Vec3f best_direction = { 0, 0, 0 };
|
||||
|
||||
// 1. Make a copy of input mesh, rotate and translate to the best orientation
|
||||
TriangleMesh mesh_copy = TriangleMesh(mesh.its);
|
||||
mesh_copy.rotate_x(euler_angles(0, 0));
|
||||
mesh_copy.rotate_y(euler_angles(1, 0));
|
||||
mesh_copy.rotate_z(euler_angles(2, 0));
|
||||
auto bounding_box = mesh_copy.bounding_box();
|
||||
Eigen::VectorXf translate_distance = bounding_box.min.array().cast<float>();
|
||||
Vec3d mesh_center = mesh_copy.center();
|
||||
mesh_copy.translate(-mesh_center(0), -mesh_center(1), -translate_distance(2));
|
||||
|
||||
// 2. sample cooling direction
|
||||
const size_t sample_nums = 180;
|
||||
std::vector<Vec3f> cool_dirs;
|
||||
for (size_t i = 0; i < sample_nums; i++)
|
||||
{
|
||||
float angle_deg = i * (360.0 / sample_nums);
|
||||
float angle_rad = angle_deg * (PI / 180.0);
|
||||
cool_dirs.push_back(Vec3f{ std::cos(angle_rad), std::sin(angle_rad), 0});
|
||||
}
|
||||
|
||||
// 3. accumulate the weighted projected overhang area, find the max weighted project area direction
|
||||
std::vector<Vec3f> face_normals_copy = its_face_normals(mesh_copy.its);
|
||||
float overhang_projected_max = 0.f;
|
||||
float overhang_projected_origin = 0.f;
|
||||
for (auto cool_dir : cool_dirs)
|
||||
{
|
||||
float overhang_projected_tmp = 0.f;
|
||||
for (size_t i = 0; i < num_faces; i++)
|
||||
{
|
||||
float cool_dir_projection = face_normals_copy[i].dot(cool_dir);
|
||||
if (areas_in[i] > 0 && cool_dir_projection > 0)
|
||||
{
|
||||
overhang_projected_tmp += areas_in[i] * cool_dir_projection;
|
||||
}
|
||||
}
|
||||
if (overhang_projected_tmp > overhang_projected_max)
|
||||
{
|
||||
overhang_projected_max = overhang_projected_tmp;
|
||||
best_direction = cool_dir;
|
||||
}
|
||||
if (cool_dir.dot(machine_cool_dir) > 0.999)
|
||||
{
|
||||
overhang_projected_origin = overhang_projected_tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// The symmetric model has similar overhang projection at all angles, so Z-axis rotation is unnecessary.
|
||||
if (std::abs(overhang_projected_origin - overhang_projected_max) < 1.0f)
|
||||
{
|
||||
best_direction = machine_cool_dir;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "best cooling dir = " << best_direction.transpose() << "\n";
|
||||
return best_direction.cast<double>();
|
||||
}
|
||||
};
|
||||
|
||||
void _orient(OrientMeshs& meshs_,
|
||||
@@ -497,6 +579,13 @@ void _orient(OrientMeshs& meshs_,
|
||||
mesh_.orientation = orienter.process();
|
||||
Geometry::rotation_from_two_vectors(mesh_.orientation, { 0,0,1 }, mesh_.axis, mesh_.angle, &mesh_.rotation_matrix);
|
||||
mesh_.euler_angles = Geometry::extract_euler_angles(mesh_.rotation_matrix);
|
||||
// find cool direction
|
||||
if (mesh_.has_cooling_fan)
|
||||
{
|
||||
mesh_.orientation_vertical = orienter.find_cooling_direction2(mesh_.euler_angles, orienter.areas_cooling, mesh_.mesh);
|
||||
BOOST_LOG_TRIVIAL(info) << "cooling direction: " << mesh_.orientation_vertical.transpose() << "\n";
|
||||
Geometry::rotation_from_two_vectors(mesh_.orientation_vertical, mesh_.cooling_direction, mesh_.axis_vertical, mesh_.angle_vertical, &mesh_.rotation_matrix_vertical);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << "rotation_from_two_vectors: " << mesh_.orientation << "; " << mesh_.axis << "; " << mesh_.angle << "; euler: " << mesh_.euler_angles.transpose();
|
||||
}});
|
||||
}
|
||||
@@ -539,6 +628,94 @@ void orient(ModelInstance* instance)
|
||||
instance->rotate(rotation_matrix);
|
||||
}
|
||||
|
||||
void orient_for_cooling(TriangleMesh& mesh, const FanDirection& fan_dir)
|
||||
{
|
||||
Vec3f best_direction{ 0, 0, 0 };
|
||||
Vec3f machine_cool_dir{ 0, 0, 0 };
|
||||
|
||||
if (fan_dir == FanDirection::fdUndefine)
|
||||
{
|
||||
// no cooling fan, do not rotate along z axis
|
||||
return;
|
||||
}
|
||||
else if (fan_dir == FanDirection::fdRight)
|
||||
{
|
||||
machine_cool_dir = { 1, 0, 0 }; // the cooling fan is on the right side.
|
||||
}
|
||||
else
|
||||
{
|
||||
// the cooling fan is on the left side or both side has cooling fans
|
||||
machine_cool_dir = { -1, 0, 0 };
|
||||
}
|
||||
|
||||
// 1. filter the overhang_areas
|
||||
int nfaces = mesh.facets_count();
|
||||
auto face_normals = its_face_normals(mesh.its);
|
||||
|
||||
Eigen::VectorXf normal_projection(nfaces, 1);
|
||||
for (auto i = 0; i < nfaces; i++)
|
||||
{
|
||||
normal_projection(i) = face_normals[i].dot(Vec3f(0, 0, 1));
|
||||
}
|
||||
float angle_thres_high = -0.6427f;
|
||||
float angle_thres_low = -0.97f;
|
||||
// 2. compute the weighted overhang faces area
|
||||
Eigen::VectorXf weighted_areas = Eigen::VectorXf::Zero(nfaces);
|
||||
for (int i = 0; i < nfaces; i++)
|
||||
{
|
||||
if (normal_projection(i) < angle_thres_high && normal_projection(i) > angle_thres_low)
|
||||
{
|
||||
weighted_areas(i) = mesh.its.facet_area(i) * (1.0f - normal_projection(i));
|
||||
}
|
||||
}
|
||||
|
||||
const size_t sample_nums = 180;
|
||||
std::vector<Vec3f> cool_dirs;
|
||||
for (size_t i = 0; i < sample_nums; i++)
|
||||
{
|
||||
float angle_deg = i * (360.0 / sample_nums);
|
||||
float angle_rad = angle_deg * (PI / 180.0);
|
||||
cool_dirs.push_back(Vec3f{ std::cos(angle_rad), std::sin(angle_rad), 0 });
|
||||
}
|
||||
|
||||
// 3. accumulate the weighted projected overhang area, find the max weighted project area direction
|
||||
float overhang_projected_max = 0.f;
|
||||
float overhang_projected_origin = 0.f;
|
||||
for (auto cool_dir : cool_dirs)
|
||||
{
|
||||
float overhang_projected_tmp = 0.f;
|
||||
for (size_t i = 0; i < nfaces; i++)
|
||||
{
|
||||
float cool_dir_projection = face_normals[i].dot(cool_dir);
|
||||
if (weighted_areas[i] > 0 && cool_dir_projection > 0)
|
||||
{
|
||||
overhang_projected_tmp += weighted_areas[i] * cool_dir_projection;
|
||||
}
|
||||
}
|
||||
if (overhang_projected_tmp > overhang_projected_max)
|
||||
{
|
||||
overhang_projected_max = overhang_projected_tmp;
|
||||
best_direction = cool_dir;
|
||||
}
|
||||
if (cool_dir.dot(machine_cool_dir) > 0.999)
|
||||
{
|
||||
overhang_projected_origin = overhang_projected_tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// The symmetric model has similar overhang projection at all angles, so Z-axis rotation is unnecessary.
|
||||
if (std::abs(overhang_projected_origin - overhang_projected_max) < 1.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// rotate the mesh
|
||||
Vec3d axis;
|
||||
double angle;
|
||||
Matrix3d rotation_matrix;
|
||||
Geometry::rotation_from_two_vectors(best_direction.cast<double>(), machine_cool_dir.cast<double>(), axis, angle, &rotation_matrix);
|
||||
mesh.rotate(angle, axis);
|
||||
}
|
||||
|
||||
} // namespace arr
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -26,10 +26,18 @@ struct OrientMesh {
|
||||
TriangleMesh mesh; /// The real mesh data
|
||||
double overhang_angle = 30;
|
||||
double angle{ 0 };
|
||||
double angle_vertical{ 0 };
|
||||
Vec3d axis{ 0,0,1 };
|
||||
Vec3d axis_vertical{ 0,0,1 };
|
||||
Vec3d orientation{ 0,0,1 };
|
||||
Matrix3d rotation_matrix;
|
||||
Vec3d euler_angles;
|
||||
Vec3d orientation_vertical{ -1,0,0 };
|
||||
Matrix3d rotation_matrix = Matrix3d::Identity();
|
||||
Matrix3d rotation_matrix_vertical = Matrix3d::Identity();
|
||||
Vec3d euler_angles = {0, 0, 0};
|
||||
Vec3d euler_angles_vertical = {0, 0, 0};
|
||||
Vec3d cooling_direction = {0, 0, 0};
|
||||
bool has_cooling_fan{false};
|
||||
|
||||
std::string name;
|
||||
|
||||
/// Optional setter function which can store arbitrary data in its closure
|
||||
@@ -154,6 +162,9 @@ void orient(ModelObject* obj);
|
||||
|
||||
void orient(ModelInstance* instance);
|
||||
|
||||
// rotate z axis for cooling
|
||||
void orient_for_cooling(TriangleMesh& mesh, const FanDirection& fan_dir);
|
||||
|
||||
}} // namespace Slic3r::orientment
|
||||
|
||||
#endif // MODELORIENT_HPP
|
||||
|
||||
@@ -1390,7 +1390,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"default_print_profile", "inherits",
|
||||
"silent_mode",
|
||||
"scan_first_layer", "enable_power_loss_recovery", "wrapping_detection_layers", "wrapping_exclude_area", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "time_cost", "machine_pause_gcode", "template_custom_gcode",
|
||||
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","cooling_filter_enabled","printer_structure","farthest_point_timelapse",
|
||||
"nozzle_type", "nozzle_hrc","auxiliary_fan", "fan_direction", "nozzle_volume","upward_compatible_machine", "z_hop_types", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","cooling_filter_enabled","printer_structure","farthest_point_timelapse",
|
||||
"best_object_pos", "head_wrap_detect_zone",
|
||||
"host_type", "print_host", "printhost_apikey", "flashforge_serial_number", "bbl_use_printhost", "printer_agent",
|
||||
"print_host_webui",
|
||||
|
||||
@@ -512,6 +512,14 @@ static t_config_enum_values s_keys_map_NozzleType {
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NozzleType)
|
||||
|
||||
static t_config_enum_values s_keys_map_FanDirection {
|
||||
{ "undefine", int(FanDirection::fdUndefine) },
|
||||
{ "left", int(FanDirection::fdLeft) },
|
||||
{ "right", int(FanDirection::fdRight) },
|
||||
{ "both", int(FanDirection::fdBoth) }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FanDirection)
|
||||
|
||||
static t_config_enum_values s_keys_map_PrinterStructure {
|
||||
{"undefine", int(PrinterStructure::psUndefine)},
|
||||
{"corexy", int(PrinterStructure::psCoreXY)},
|
||||
@@ -3997,6 +4005,21 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("fan_direction", coEnum);
|
||||
def->label = L("Fan direction");
|
||||
def->tooltip = L("Cooling fan direction of the printer");
|
||||
def->enum_keys_map = &ConfigOptionEnum<FanDirection>::get_enum_values();
|
||||
def->enum_values.push_back("undefine");
|
||||
def->enum_values.push_back("left");
|
||||
def->enum_values.push_back("right");
|
||||
def->enum_values.push_back("both");
|
||||
def->enum_labels.push_back(L("Undefined"));
|
||||
def->enum_labels.push_back(L("Left"));
|
||||
def->enum_labels.push_back(L("Right"));
|
||||
def->enum_labels.push_back(L("Both"));
|
||||
def->mode = comDevelop;
|
||||
def->set_default_value(new ConfigOptionEnum<FanDirection>(fdUndefine));
|
||||
|
||||
def = this->add("fan_speedup_time", coFloat);
|
||||
// Label is set in Tab.cpp in the Line object.
|
||||
//def->label = L("Fan speed-up time");
|
||||
|
||||
@@ -362,6 +362,13 @@ enum LayerSeq {
|
||||
flsCustomize
|
||||
};
|
||||
|
||||
enum FanDirection {
|
||||
fdUndefine = 0,
|
||||
fdLeft,
|
||||
fdRight,
|
||||
fdBoth
|
||||
};
|
||||
|
||||
static std::unordered_map<NozzleType, std::string>NozzleTypeEumnToStr = {
|
||||
{NozzleType::ntUndefine, "undefine"},
|
||||
{NozzleType::ntHardenedSteel, "hardened_steel"},
|
||||
@@ -1494,6 +1501,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionEnumsGenericNullable,nozzle_type))
|
||||
((ConfigOptionInt, nozzle_hrc))
|
||||
((ConfigOptionBool, auxiliary_fan))
|
||||
((ConfigOptionEnum<FanDirection>, fan_direction))
|
||||
((ConfigOptionBool, support_air_filtration))
|
||||
((ConfigOptionBool, cooling_filter_enabled))
|
||||
((ConfigOptionEnum<PrinterStructure>,printer_structure))
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "slic3r/Utils/FixModelByCgal.hpp"
|
||||
#include "libslic3r/Format/bbs_3mf.hpp"
|
||||
#include "libslic3r/Orient.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#ifdef __WXMSW__
|
||||
@@ -2560,6 +2561,15 @@ void ObjectList::load_shape_object(const std::string &type_name)
|
||||
// Create mesh
|
||||
BoundingBoxf3 bb;
|
||||
TriangleMesh mesh = create_mesh(type_name, bb);
|
||||
// Rotate the largest overhang area toward the part cooling fan so new shapes start in a
|
||||
// cooling friendly orientation. Skip the plain cube: the pressure advance pattern
|
||||
// calibration reuses it as an axis-aligned anchor and scales it by its bounding box,
|
||||
// so its orientation must stay fixed.
|
||||
const Slic3r::DynamicPrintConfig& full_config = wxGetApp().preset_bundle->full_config();
|
||||
if (type_name != "Cube" && full_config.has("fan_direction") && full_config.has("auxiliary_fan")) {
|
||||
FanDirection config_dir = full_config.option<ConfigOptionEnum<FanDirection>>("fan_direction")->value;
|
||||
orientation::orient_for_cooling(mesh, config_dir);
|
||||
}
|
||||
// BBS: remove "Shape" prefix
|
||||
load_mesh_object(mesh, _(type_name));
|
||||
wxGetApp().mainframe->update_title();
|
||||
|
||||
@@ -229,15 +229,32 @@ orientation::OrientMesh OrientJob::get_orient_mesh(ModelInstance* instance)
|
||||
auto obj = instance->get_object();
|
||||
om.name = obj->name;
|
||||
om.mesh = obj->mesh(); // don't know the difference to obj->raw_mesh(). Both seem OK
|
||||
const Slic3r::DynamicPrintConfig& config = wxGetApp().preset_bundle->full_config();
|
||||
if (obj->config.has("support_threshold_angle"))
|
||||
om.overhang_angle = obj->config.opt_int("support_threshold_angle");
|
||||
else {
|
||||
const Slic3r::DynamicPrintConfig& config = wxGetApp().preset_bundle->full_config();
|
||||
om.overhang_angle = config.opt_int("support_threshold_angle");
|
||||
}
|
||||
|
||||
if (config.has("fan_direction") && config.has("auxiliary_fan")) {
|
||||
FanDirection config_dir = config.option<ConfigOptionEnum<FanDirection>>("fan_direction")->value;
|
||||
if (config_dir == FanDirection::fdUndefine || !config.opt_bool("auxiliary_fan")) {
|
||||
// no part cooling airflow to face, keep the orientation around the z axis unchanged
|
||||
om.cooling_direction = {0, 0, 0};
|
||||
} else if (config_dir == FanDirection::fdRight) {
|
||||
// the part cooling airflow comes from the right side
|
||||
om.cooling_direction = {1, 0, 0};
|
||||
om.has_cooling_fan = true;
|
||||
} else {
|
||||
// the part cooling airflow comes from the left side, or from both sides
|
||||
om.cooling_direction = {-1, 0, 0};
|
||||
om.has_cooling_fan = true;
|
||||
}
|
||||
}
|
||||
|
||||
om.setter = [instance](const OrientMesh& p) {
|
||||
instance->rotate(p.rotation_matrix);
|
||||
instance->rotate(p.rotation_matrix_vertical);
|
||||
instance->get_object()->invalidate_bounding_box();
|
||||
instance->get_object()->ensure_on_bed();
|
||||
};
|
||||
|
||||
@@ -4956,6 +4956,7 @@ void TabPrinter::build_fff()
|
||||
optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type");
|
||||
optgroup->append_single_option_line("nozzle_hrc", "printer_basic_information_accessory#nozzle-hrc");
|
||||
optgroup->append_single_option_line("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan");
|
||||
optgroup->append_single_option_line("fan_direction");
|
||||
optgroup->append_single_option_line("support_chamber_temp_control", "printer_basic_information_accessory#support-controlling-chamber-temperature");
|
||||
optgroup->append_single_option_line("support_air_filtration", "printer_basic_information_accessory#support-air-filtration");
|
||||
|
||||
@@ -5892,6 +5893,8 @@ void TabPrinter::toggle_options()
|
||||
|
||||
const bool support_parallel_printheads = printer_cfg.opt_bool("support_parallel_printheads");
|
||||
toggle_line("parallel_printheads_count", support_parallel_printheads);
|
||||
|
||||
toggle_line("fan_direction", m_config->opt_bool("auxiliary_fan"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user