stage in changes from off-plate-gravity and remove stuff I didn't need

This commit is contained in:
harrierpigeon
2026-03-11 00:27:14 -05:00
committed by Joseph Robertson
parent 0a762dfede
commit 08aa277974
26 changed files with 230 additions and 37 deletions

View File

@@ -26,6 +26,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform vec4 uniform_color; uniform vec4 uniform_color;

View File

@@ -23,6 +23,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform mat4 view_model_matrix; uniform mat4 view_model_matrix;
@@ -71,8 +72,8 @@ void main()
// Point in homogenous coordinates. // Point in homogenous coordinates.
world_pos = volume_world_matrix * vec4(v_position, 1.0); world_pos = volume_world_matrix * vec4(v_position, 1.0);
// z component of normal vector in world coordinate used for slope shading // dot product of world normal with up direction, used for slope shading
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0; world_normal_z = slope.actived ? dot(normalize(slope.volume_world_normal_matrix * v_normal), slope.up_direction) : 0.0;
gl_Position = projection_matrix * position; gl_Position = projection_matrix * position;
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.

View File

@@ -37,6 +37,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform SlopeDetection slope; uniform SlopeDetection slope;
@@ -85,7 +86,7 @@ void main()
color = LightBlue; color = LightBlue;
alpha = 1.0; alpha = 1.0;
} }
else if( transformed_normal.z < slope.normal_z - EPSILON) else if( dot(transformed_normal, slope.up_direction) < slope.normal_z - EPSILON)
{ {
color = color * 0.5 + LightRed * 0.5; color = color * 0.5 + LightRed * 0.5;
alpha = 1.0; alpha = 1.0;

View File

@@ -24,6 +24,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform SlopeDetection slope; uniform SlopeDetection slope;
void main() void main()

View File

@@ -26,6 +26,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform vec4 uniform_color; uniform vec4 uniform_color;

View File

@@ -23,6 +23,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform mat4 view_model_matrix; uniform mat4 view_model_matrix;
@@ -71,8 +72,8 @@ void main()
// Point in homogenous coordinates. // Point in homogenous coordinates.
world_pos = volume_world_matrix * vec4(v_position, 1.0); world_pos = volume_world_matrix * vec4(v_position, 1.0);
// z component of normal vector in world coordinate used for slope shading // dot product of world normal with up direction, used for slope shading
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0; world_normal_z = slope.actived ? dot(normalize(slope.volume_world_normal_matrix * v_normal), slope.up_direction) : 0.0;
gl_Position = projection_matrix * position; gl_Position = projection_matrix * position;
// Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded. // Fill in the scalars for fragment shader clipping. Fragments with any of these components lower than zero are discarded.

View File

@@ -37,6 +37,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform SlopeDetection slope; uniform SlopeDetection slope;
@@ -87,7 +88,7 @@ void main()
color = LightBlue; color = LightBlue;
alpha = 1.0; alpha = 1.0;
} }
else if( transformed_normal.z < slope.normal_z - EPSILON) else if( dot(transformed_normal, slope.up_direction) < slope.normal_z - EPSILON)
{ {
color = color * 0.5 + LightRed * 0.5; color = color * 0.5 + LightRed * 0.5;
alpha = 1.0; alpha = 1.0;

View File

@@ -24,6 +24,7 @@ struct SlopeDetection
bool actived; bool actived;
float normal_z; float normal_z;
mat3 volume_world_normal_matrix; mat3 volume_world_normal_matrix;
vec3 up_direction;
}; };
uniform SlopeDetection slope; uniform SlopeDetection slope;
void main() void main()

View File

@@ -377,6 +377,11 @@ inline void translate(ExPolygons &expolys, const Point &p) {
expoly.translate(p); expoly.translate(p);
} }
inline void translate(Polygons &polys, const Point &p) {
for (Polygon &poly : polys)
poly.translate(p);
}
inline void polygons_append(Polygons &dst, const ExPolygon &src) inline void polygons_append(Polygons &dst, const ExPolygon &src)
{ {
dst.reserve(dst.size() + src.holes.size() + 1); dst.reserve(dst.size() + src.holes.size() + 1);

View File

@@ -1010,7 +1010,7 @@ static std::vector<std::string> s_Preset_machine_limits_options {
static std::vector<std::string> s_Preset_printer_options { static std::vector<std::string> s_Preset_printer_options {
"printer_technology", "printer_technology",
"printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "build_plate_tilt_x", "build_plate_tilt_y", "gcode_flavor",
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs", "fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
"single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode", "single_extruder_multi_material", "manual_filament_change", "file_start_gcode", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "wrapping_detection_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
"printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type",

View File

@@ -5918,6 +5918,30 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple; def->mode = comSimple;
def->set_default_value(new ConfigOptionFloatOrPercent(50., true)); def->set_default_value(new ConfigOptionFloatOrPercent(50., true));
def = this->add("build_plate_tilt_x", coFloat);
def->label = L("Build plate tilt X");
def->category = L("Support");
def->tooltip = L("Tilt angle of the build plate along the X axis. "
"A positive value tilts the plate so the +X side is higher, shifting gravity toward -X and increasing overhangs on the +X side. "
"A negative value tilts the -X side higher. Set to 0 for no X-axis tilt.");
def->sidetext = u8"\u00B0";
def->min = -45;
def->max = 45;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("build_plate_tilt_y", coFloat);
def->label = L("Build plate tilt Y");
def->category = L("Support");
def->tooltip = L("Tilt angle of the build plate along the Y axis. "
"A positive value tilts the plate so the +Y side is higher, shifting gravity toward -Y and increasing overhangs on the +Y side. "
"A negative value tilts the -Y side higher. Set to 0 for no Y-axis tilt.");
def->sidetext = u8"\u00B0";
def->min = -45;
def->max = 45;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("tree_support_branch_angle", coFloat); def = this->add("tree_support_branch_angle", coFloat);
def->label = L("Tree support branch angle"); def->label = L("Tree support branch angle");
def->category = L("Support"); def->category = L("Support");

View File

@@ -1410,6 +1410,9 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
PrintConfig, PrintConfig,
(MachineEnvelopeConfig, GCodeConfig), (MachineEnvelopeConfig, GCodeConfig),
// Build plate tilt for off-axis gravity support generation (printer-level setting).
((ConfigOptionFloat, build_plate_tilt_x))
((ConfigOptionFloat, build_plate_tilt_y))
//BBS //BBS
((ConfigOptionInts, additional_cooling_fan_speed)) ((ConfigOptionInts, additional_cooling_fan_speed))
((ConfigOptionBool, reduce_crossing_wall)) ((ConfigOptionBool, reduce_crossing_wall))

View File

@@ -1392,6 +1392,10 @@ static inline ExPolygons detect_overhangs(
const double threshold_rad = Geometry::deg2rad(thresh_angle); const double threshold_rad = Geometry::deg2rad(thresh_angle);
const bool bridge_no_support = object_config.bridge_no_support.value; const bool bridge_no_support = object_config.bridge_no_support.value;
const coordf_t xy_expansion = scale_(object_config.support_expansion.value); const coordf_t xy_expansion = scale_(object_config.support_expansion.value);
// Build plate tilt: compute per-layer XY shift for tilted gravity direction
const double tilt_x_rad = Geometry::deg2rad(print_config.build_plate_tilt_x.value);
const double tilt_y_rad = Geometry::deg2rad(print_config.build_plate_tilt_y.value);
const bool has_tilt = std::abs(tilt_x_rad) > EPSILON || std::abs(tilt_y_rad) > EPSILON;
float lower_layer_offset = 0; float lower_layer_offset = 0;
if (layer_id == 0) if (layer_id == 0)
@@ -1441,9 +1445,17 @@ static inline ExPolygons detect_overhangs(
// Overhang polygons for this layer and region. // Overhang polygons for this layer and region.
Polygons diff_polygons; Polygons diff_polygons;
Polygons layerm_polygons = to_polygons(layerm->slices.surfaces); Polygons layerm_polygons = to_polygons(layerm->slices.surfaces);
// Apply build plate tilt: shift lower layer polygons to simulate tilted gravity
Polygons effective_lower = lower_layer_polygons;
if (has_tilt) {
const double lh = lower_layer.height;
Point tilt_shift(coord_t(scale_(lh * tan(tilt_y_rad))),
coord_t(scale_(lh * tan(tilt_x_rad))));
translate(effective_lower, tilt_shift);
}
if (lower_layer_offset == 0.f) { if (lower_layer_offset == 0.f) {
// Support everything. // Support everything.
diff_polygons = diff(layerm_polygons, lower_layer_polygons); diff_polygons = diff(layerm_polygons, effective_lower);
if (buildplate_only) { if (buildplate_only) {
// Don't support overhangs above the top surfaces. // Don't support overhangs above the top surfaces.
// This step is done before the contact surface is calculated by growing the overhang region. // This step is done before the contact surface is calculated by growing the overhang region.
@@ -1452,9 +1464,9 @@ static inline ExPolygons detect_overhangs(
} else if (auto_normal_support) { } else if (auto_normal_support) {
// Get the regions needing a suport, collapse very tiny spots. // Get the regions needing a suport, collapse very tiny spots.
//FIXME cache the lower layer offset if this layer has multiple regions. //FIXME cache the lower layer offset if this layer has multiple regions.
diff_polygons = diff_polygons =
diff(layerm_polygons, diff(layerm_polygons,
expand(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); expand(effective_lower, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) { if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) {
// Don't support overhangs above the top surfaces. // Don't support overhangs above the top surfaces.
// This step is done before the contact surface is calculated by growing the overhang region. // This step is done before the contact surface is calculated by growing the overhang region.
@@ -1462,9 +1474,9 @@ static inline ExPolygons detect_overhangs(
} }
if (! diff_polygons.empty()) { if (! diff_polygons.empty()) {
// Offset the support regions back to a full overhang, restrict them to the full overhang. // Offset the support regions back to a full overhang, restrict them to the full overhang.
// This is done to increase size of the supporting columns below, as they are calculated by // This is done to increase size of the supporting columns below, as they are calculated by
// propagating these contact surfaces downwards. // propagating these contact surfaces downwards.
diff_polygons = diff(intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), lower_layer_polygons); diff_polygons = diff(intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), effective_lower);
} }
//FIXME add user defined filtering here based on minimal area or minimum radius or whatever. //FIXME add user defined filtering here based on minimal area or minimum radius or whatever.

View File

@@ -671,6 +671,11 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
double thresh_angle = config.support_threshold_angle.value > EPSILON ? config.support_threshold_angle.value + 1 : 30; double thresh_angle = config.support_threshold_angle.value > EPSILON ? config.support_threshold_angle.value + 1 : 30;
thresh_angle = std::min(thresh_angle, 89.); // should be smaller than 90 thresh_angle = std::min(thresh_angle, 89.); // should be smaller than 90
const double threshold_rad = Geometry::deg2rad(thresh_angle); const double threshold_rad = Geometry::deg2rad(thresh_angle);
// Build plate tilt: compute per-layer XY shift for tilted gravity direction
const PrintConfig& print_cfg = m_object->print()->config();
const double tilt_x_rad = Geometry::deg2rad(print_cfg.build_plate_tilt_x.value);
const double tilt_y_rad = Geometry::deg2rad(print_cfg.build_plate_tilt_y.value);
const bool has_tilt = std::abs(tilt_x_rad) > EPSILON || std::abs(tilt_y_rad) > EPSILON;
// FIXME this is a fudge constant! // FIXME this is a fudge constant!
double support_tree_tip_diameter = 0.8; double support_tree_tip_diameter = 0.8;
auto enforcer_overhang_offset = scaled<double>(support_tree_tip_diameter); auto enforcer_overhang_offset = scaled<double>(support_tree_tip_diameter);
@@ -813,8 +818,19 @@ void TreeSupport::detect_overhangs(bool check_support_necessity/* = false*/)
ExPolygons& curr_polys = layer->lslices_extrudable; ExPolygons& curr_polys = layer->lslices_extrudable;
ExPolygons& lower_polys = lower_layer->lslices_extrudable; ExPolygons& lower_polys = lower_layer->lslices_extrudable;
// Apply build plate tilt: shift lower layer polygons to simulate tilted gravity
ExPolygons shifted_lower;
if (has_tilt) {
shifted_lower = lower_polys; // copy
const double lh = lower_layer->height;
Point tilt_shift(coord_t(scale_(lh * tan(tilt_y_rad))),
coord_t(scale_(lh * tan(tilt_x_rad))));
translate(shifted_lower, tilt_shift);
}
const ExPolygons &effective_lower = has_tilt ? shifted_lower : lower_polys;
// normal overhang // normal overhang
ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS); ExPolygons lower_layer_offseted = offset_ex(effective_lower, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
overhangs_all_layers[layer_nr] = std::move(diff_ex(curr_polys, lower_layer_offseted)); overhangs_all_layers[layer_nr] = std::move(diff_ex(curr_polys, lower_layer_offseted));
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() }; double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };

View File

@@ -208,6 +208,10 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
const bool support_threshold_auto = support_threshold == 0; const bool support_threshold_auto = support_threshold == 0;
// +1 makes the threshold inclusive // +1 makes the threshold inclusive
double tan_threshold = support_threshold_auto ? 0. : tan(M_PI * double(support_threshold + 1) / 180.); double tan_threshold = support_threshold_auto ? 0. : tan(M_PI * double(support_threshold + 1) / 180.);
// Build plate tilt: compute per-layer XY shift for tilted gravity direction
const double tilt_x_rad = Geometry::deg2rad(print_config.build_plate_tilt_x.value);
const double tilt_y_rad = Geometry::deg2rad(print_config.build_plate_tilt_y.value);
const bool has_tilt = std::abs(tilt_x_rad) > EPSILON || std::abs(tilt_y_rad) > EPSILON;
//FIXME this is a fudge constant! //FIXME this is a fudge constant!
auto enforcer_overhang_offset = scaled<double>(config.tree_support_tip_diameter.value); auto enforcer_overhang_offset = scaled<double>(config.tree_support_tip_diameter.value);
const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution; const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution;
@@ -229,7 +233,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size())); size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size()));
tbb::parallel_for(tbb::blocked_range<LayerIndex>(1, num_overhang_layers), tbb::parallel_for(tbb::blocked_range<LayerIndex>(1, num_overhang_layers),
[&print_object, &config, &print_config, &enforcers_layers, &blockers_layers, [&print_object, &config, &print_config, &enforcers_layers, &blockers_layers,
support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, radius_sample_resolution, &throw_on_cancel, &out] support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, radius_sample_resolution, has_tilt, tilt_x_rad, tilt_y_rad, &throw_on_cancel, &out]
(const tbb::blocked_range<LayerIndex> &range) { (const tbb::blocked_range<LayerIndex> &range) {
for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
const Layer &current_layer = *print_object.get_layer(layer_id); const Layer &current_layer = *print_object.get_layer(layer_id);
@@ -253,7 +257,15 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
lower_layer_offset = external_perimeter_width - float(scale_(config.support_threshold_overlap.get_abs_value(unscale_(external_perimeter_width)))); lower_layer_offset = external_perimeter_width - float(scale_(config.support_threshold_overlap.get_abs_value(unscale_(external_perimeter_width))));
} else } else
lower_layer_offset = scaled<float>(lower_layer.height / tan_threshold); lower_layer_offset = scaled<float>(lower_layer.height / tan_threshold);
Polygons lower_layer_offseted = offset(lower_layer.lslices_extrudable, lower_layer_offset); // Apply build plate tilt: shift lower layer polygons to simulate tilted gravity
Polygons lower_src = to_polygons(lower_layer.lslices_extrudable);
if (has_tilt) {
const double lh = lower_layer.height;
Point tilt_shift(coord_t(scale_(lh * tan(tilt_y_rad))),
coord_t(scale_(lh * tan(tilt_x_rad))));
translate(lower_src, tilt_shift);
}
Polygons lower_layer_offseted = offset(lower_src, lower_layer_offset);
overhangs = diff(current_layer.lslices_extrudable, lower_layer_offseted); overhangs = diff(current_layer.lslices_extrudable, lower_layer_offseted);
if (lower_layer_offset == 0) { if (lower_layer_offset == 0) {
raw_overhangs = overhangs; raw_overhangs = overhangs;

View File

@@ -233,7 +233,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
return this->select_unsplit_triangle(hit, facet_idx, neighbors); return this->select_unsplit_triangle(hit, facet_idx, neighbors);
} }
void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg) void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg, const Vec3f &up_direction)
{ {
assert(facet_start < m_orig_size_indices); assert(facet_start < m_orig_size_indices);
@@ -294,8 +294,8 @@ void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&c
int facet = facets_to_check[facet_idx]; int facet = facets_to_check[facet_idx];
const Vec3f& facet_normal = m_face_normals[m_triangles[facet].source_triangle]; const Vec3f& facet_normal = m_face_normals[m_triangles[facet].source_triangle];
Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_no_translate.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()); Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_no_translate.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>());
float world_normal_z = (normal_matrix* facet_normal).normalized().z(); float world_normal_dot = (normal_matrix * facet_normal).normalized().dot(up_direction);
if (!visited[facet] && (highlight_by_angle_deg == 0.f || world_normal_z < highlight_angle_limit)) { if (!visited[facet] && (highlight_by_angle_deg == 0.f || world_normal_dot < highlight_angle_limit)) {
if (select_triangle(facet, new_state, triangle_splitting)) { if (select_triangle(facet, new_state, triangle_splitting)) {
// add neighboring facets to list to be processed later // add neighboring facets to list to be processed later
for (int neighbor_idx : m_neighbors[facet]) for (int neighbor_idx : m_neighbors[facet])
@@ -320,7 +320,7 @@ bool TriangleSelector::is_facet_clipped(int facet_idx, const ClippingPlane &clp)
void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate, void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_start, const Transform3d& trafo_no_translate,
const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg, const ClippingPlane &clp, float seed_fill_angle, float highlight_by_angle_deg,
bool force_reselection) bool force_reselection, const Vec3f &up_direction)
{ {
assert(facet_start < m_orig_size_indices); assert(facet_start < m_orig_size_indices);
@@ -344,8 +344,8 @@ void TriangleSelector::seed_fill_select_triangles(const Vec3f &hit, int facet_st
const Vec3f &facet_normal = m_face_normals[m_triangles[current_facet].source_triangle]; const Vec3f &facet_normal = m_face_normals[m_triangles[current_facet].source_triangle];
Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_no_translate.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()); Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_no_translate.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>());
float world_normal_z = (normal_matrix * facet_normal).normalized().z(); float world_normal_dot = (normal_matrix * facet_normal).normalized().dot(up_direction);
if (!visited[current_facet] && (highlight_by_angle_deg == 0.f || world_normal_z < highlight_angle_limit)) { if (!visited[current_facet] && (highlight_by_angle_deg == 0.f || world_normal_dot < highlight_angle_limit)) {
if (m_triangles[current_facet].is_split()) { if (m_triangles[current_facet].is_split()) {
for (int split_triangle_idx = 0; split_triangle_idx <= m_triangles[current_facet].number_of_split_sides(); ++split_triangle_idx) { for (int split_triangle_idx = 0; split_triangle_idx <= m_triangles[current_facet].number_of_split_sides(); ++split_triangle_idx) {
assert(split_triangle_idx < int(m_triangles[current_facet].children.size())); assert(split_triangle_idx < int(m_triangles[current_facet].children.size()));

View File

@@ -308,7 +308,8 @@ public:
EnforcerBlockerType new_state, // enforcer or blocker? EnforcerBlockerType new_state, // enforcer or blocker?
const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation const Transform3d &trafo_no_translate, // matrix to get from mesh to world without translation
bool triangle_splitting, // If triangles will be split base on the cursor or not bool triangle_splitting, // If triangles will be split base on the cursor or not
float highlight_by_angle_deg = 0.f); // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
const Vec3f &up_direction = Vec3f::UnitZ()); // Up direction for overhang detection (accounts for build plate tilt)
void seed_fill_select_triangles(const Vec3f &hit, // point where to start void seed_fill_select_triangles(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to
@@ -316,7 +317,8 @@ public:
const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only const ClippingPlane &clp, // Clipping plane to limit painting to not clipped facets only
float seed_fill_angle, // the maximal angle between two facets to be painted by the same color float seed_fill_angle, // the maximal angle between two facets to be painted by the same color
float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees. float highlight_by_angle_deg = 0.f, // The maximal angle of overhang. If it is set to a non-zero value, it is possible to paint only the triangles of overhang defined by this angle in degrees.
bool force_reselection = false); // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle bool force_reselection = false, // force reselection of the triangle mesh even in cases that mouse is pointing on the selected triangle
const Vec3f &up_direction = Vec3f::UnitZ()); // Up direction for overhang detection (accounts for build plate tilt)
void bucket_fill_select_triangles(const Vec3f &hit, // point where to start void bucket_fill_select_triangles(const Vec3f &hit, // point where to start
int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to int facet_start, // facet of the original mesh (unsplit) that the hit point belongs to

View File

@@ -394,6 +394,8 @@ void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix,
case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom); break; } case Type::Custom: { render_custom(canvas, view_matrix, projection_matrix, bottom); break; }
} }
render_gravity_arrow(view_matrix, projection_matrix);
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
} }
@@ -731,6 +733,63 @@ void Bed3D::render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, co
render_texture(bottom, canvas);*/ render_texture(bottom, canvas);*/
} }
void Bed3D::render_gravity_arrow(const Transform3d& view_matrix, const Transform3d& projection_matrix)
{
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
double tilt_x_deg = cfg.opt_float("build_plate_tilt_x");
double tilt_y_deg = cfg.opt_float("build_plate_tilt_y");
if (tilt_x_deg == 0. && tilt_y_deg == 0.) {
m_gravity_arrow.reset();
return;
}
// Gravity direction (matching the slicer's tilt convention)
double tilt_x_rad = Geometry::deg2rad(tilt_x_deg);
double tilt_y_rad = Geometry::deg2rad(tilt_y_deg);
Vec3d gravity_dir = Vec3d(-tan(tilt_y_rad), -tan(tilt_x_rad), -1.0).normalized();
// Build the arrow model (same dimensions as the axis arrows)
if (!m_gravity_arrow.is_initialized()) {
const float stem_length = Axes::DefaultStemLength;
const float tip_radius = Axes::DefaultTipRadius;
const float tip_length = Axes::DefaultTipLength;
const float stem_radius = stem_length / 75.f; // same ratio as axis cylinders
m_gravity_arrow.init_from(stilized_arrow(16, tip_radius, tip_length, stem_radius, stem_length));
}
// The arrow model points along +Z by default. Compute rotation to align with gravity_dir.
// Rotation axis = cross(+Z, gravity_dir), angle = acos(dot(+Z, gravity_dir))
Vec3d from = Vec3d::UnitZ();
Vec3d to = gravity_dir;
double dot = from.dot(to);
Transform3d rot = Transform3d::Identity();
if (dot < -0.9999) {
// Nearly opposite — rotate 180° around X
rot = Eigen::AngleAxisd(M_PI, Vec3d::UnitX()) * rot;
} else if (dot < 0.9999) {
Vec3d axis = from.cross(to).normalized();
double angle = std::acos(std::clamp(dot, -1.0, 1.0));
rot = Eigen::AngleAxisd(angle, axis) * rot;
}
GLShaderProgram* shader = wxGetApp().get_shader("flat");
if (shader == nullptr)
return;
glsafe(::glEnable(GL_DEPTH_TEST));
shader->start_using();
const Camera& camera = wxGetApp().plater()->get_camera();
Transform3d model_matrix = rot;
shader->set_uniform("view_model_matrix", camera.get_view_matrix() * model_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
m_gravity_arrow.set_color({ 1.0f, 0.85f, 0.0f, 1.0f }); // yellow
m_gravity_arrow.render();
shader->stop_using();
}
void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix) void Bed3D::render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix)
{ {
// m_texture.reset(); // m_texture.reset();

View File

@@ -110,6 +110,7 @@ private:
//GLTexture m_temp_texture; //GLTexture m_temp_texture;
GLModel m_model; GLModel m_model;
Vec3d m_model_offset{ Vec3d::Zero() }; Vec3d m_model_offset{ Vec3d::Zero() };
GLModel m_gravity_arrow;
Axes m_axes; Axes m_axes;
float m_scale_factor{ 1.0f }; float m_scale_factor{ 1.0f };
@@ -177,7 +178,8 @@ private:
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom); void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom);
void render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_default(bool bottom, const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_gravity_arrow(const Transform3d& view_matrix, const Transform3d& projection_matrix);
// BBS: remove the bed picking logic // BBS: remove the bed picking logic
// void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo); // void register_raycasters_for_picking(const GLModel::Geometry& geometry, const Transform3d& trafo);
}; };

View File

@@ -1078,13 +1078,27 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
bool enable_support; bool enable_support;
int support_threshold_angle = get_selection_support_threshold_angle(enable_support); int support_threshold_angle = get_selection_support_threshold_angle(enable_support);
float normal_z = -::cos(Geometry::deg2rad((float) support_threshold_angle)); float normal_z = -::cos(Geometry::deg2rad((float) support_threshold_angle));
// Compute up direction accounting for build plate tilt
Vec3f up_direction = Vec3f::UnitZ();
{
const DynamicPrintConfig& prt_cfg = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config;
double tilt_x_deg = prt_cfg.opt_float("build_plate_tilt_x");
double tilt_y_deg = prt_cfg.opt_float("build_plate_tilt_y");
if (tilt_x_deg != 0. || tilt_y_deg != 0.) {
double tilt_x_rad = Geometry::deg2rad(tilt_x_deg);
double tilt_y_rad = Geometry::deg2rad(tilt_y_deg);
up_direction = Vec3f(float(tan(tilt_y_rad)), float(tan(tilt_x_rad)), 1.f).normalized();
}
}
shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower);
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>())); shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
shader->set_uniform("slope.normal_z", normal_z); shader->set_uniform("slope.normal_z", normal_z);
shader->set_uniform("slope.up_direction", up_direction);
#if ENABLE_ENVIRONMENT_MAP #if ENABLE_ENVIRONMENT_MAP
unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id(); unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id();

View File

@@ -64,6 +64,7 @@ static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF =
{ L("Support") , { "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap", { L("Support") , { "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap",
"support_base_pattern", "support_on_build_plate_only","support_critical_regions_only", "support_base_pattern", "support_on_build_plate_only","support_critical_regions_only",
"support_remove_small_overhang", "support_remove_small_overhang",
"build_plate_tilt_x", "build_plate_tilt_y",
"support_base_pattern_spacing", "support_expansion"}}, "support_base_pattern_spacing", "support_expansion"}},
//BBS //BBS
{ L("Flush options") , { "flush_into_infill", "flush_into_objects", "flush_into_support"} } { L("Flush options") , { "flush_into_infill", "flush_into_objects", "flush_into_support"} }
@@ -94,7 +95,8 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::OBJECT_C
{"support_bottom_z_distance", "",19},{"support_top_z_distance", "",20},{"support_base_pattern", "",21},{"support_base_pattern_spacing", "",22}, {"support_bottom_z_distance", "",19},{"support_top_z_distance", "",20},{"support_base_pattern", "",21},{"support_base_pattern_spacing", "",22},
{"support_interface_top_layers", "",23},{"support_interface_bottom_layers", "",24},{"support_interface_spacing", "",25},{"support_bottom_interface_spacing", "",26}, {"support_interface_top_layers", "",23},{"support_interface_bottom_layers", "",24},{"support_interface_spacing", "",25},{"support_bottom_interface_spacing", "",26},
{"support_object_xy_distance", "",27}, {"bridge_no_support", "",28},{"max_bridge_length", "",29},{"support_critical_regions_only", "",30},{"support_remove_small_overhang","",31}, {"support_object_xy_distance", "",27}, {"bridge_no_support", "",28},{"max_bridge_length", "",29},{"support_critical_regions_only", "",30},{"support_remove_small_overhang","",31},
{"support_object_first_layer_gap","",32} {"build_plate_tilt_x","",32},{"build_plate_tilt_y","",33},
{"support_object_first_layer_gap","",34}
}}, }},
{ L("Speed"), {{"support_speed", "",12}, {"support_interface_speed", "",13} { L("Speed"), {{"support_speed", "",12}, {"support_interface_speed", "",13}
}} }}

View File

@@ -537,6 +537,14 @@ int GLGizmoFdmSupports::get_selection_support_threshold_angle()
return auto_support ? support_threshold_angle : 0; return auto_support ? support_threshold_angle : 0;
} }
std::pair<double, double> GLGizmoFdmSupports::get_build_plate_tilt()
{
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
double tilt_x = cfg.opt_float("build_plate_tilt_x");
double tilt_y = cfg.opt_float("build_plate_tilt_y");
return {tilt_x, tilt_y};
}
void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block) void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
{ {
float threshold = (float(M_PI)/180.f)*threshold_deg; float threshold = (float(M_PI)/180.f)*threshold_deg;
@@ -544,6 +552,12 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
const ModelObject* mo = m_c->selection_info()->model_object(); const ModelObject* mo = m_c->selection_info()->model_object();
const ModelInstance* mi = mo->instances[selection.get_instance_idx()]; const ModelInstance* mi = mo->instances[selection.get_instance_idx()];
// Compute gravity direction accounting for build plate tilt
auto [tilt_x_deg, tilt_y_deg] = get_build_plate_tilt();
double tilt_x_rad = tilt_x_deg * M_PI / 180.0;
double tilt_y_rad = tilt_y_deg * M_PI / 180.0;
Vec3d gravity_dir = Vec3d(-tan(tilt_y_rad), -tan(tilt_x_rad), -1.0).normalized();
int mesh_id = -1; int mesh_id = -1;
for (const ModelVolume* mv : mo->volumes) { for (const ModelVolume* mv : mo->volumes) {
if (! mv->is_model_part()) if (! mv->is_model_part())
@@ -552,10 +566,8 @@ void GLGizmoFdmSupports::select_facets_by_angle(float threshold_deg, bool block)
++mesh_id; ++mesh_id;
const Transform3d trafo_matrix = mi->get_matrix_no_offset() * mv->get_matrix_no_offset(); const Transform3d trafo_matrix = mi->get_matrix_no_offset() * mv->get_matrix_no_offset();
Vec3f down = (trafo_matrix.inverse() * (-Vec3d::UnitZ())).cast<float>().normalized(); Vec3f down = (trafo_matrix.inverse() * gravity_dir).cast<float>().normalized();
Vec3f limit = (trafo_matrix.inverse() * Vec3d(std::sin(threshold), 0, -std::cos(threshold))).cast<float>().normalized(); float dot_limit = std::cos(threshold);
float dot_limit = limit.dot(down);
// Now calculate dot product of vert_direction and facets' normals. // Now calculate dot product of vert_direction and facets' normals.
int idx = 0; int idx = 0;

View File

@@ -59,6 +59,7 @@ private:
void select_facets_by_angle(float threshold, bool block); void select_facets_by_angle(float threshold, bool block);
// BBS // BBS
int get_selection_support_threshold_angle(); int get_selection_support_threshold_angle();
std::pair<double, double> get_build_plate_tilt();
int m_support_threshold_angle = -1; int m_support_threshold_angle = -1;

View File

@@ -73,6 +73,18 @@ GLGizmoPainterBase::ClippingPlaneDataWrapper GLGizmoPainterBase::get_clipping_pl
return clp_data_out; return clp_data_out;
} }
Vec3f GLGizmoPainterBase::get_tilt_up_direction() const
{
const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->printers.get_edited_preset().config;
double tilt_x_deg = cfg.opt_float("build_plate_tilt_x");
double tilt_y_deg = cfg.opt_float("build_plate_tilt_y");
if (tilt_x_deg == 0. && tilt_y_deg == 0.)
return Vec3f::UnitZ();
double tilt_x_rad = Geometry::deg2rad(tilt_x_deg);
double tilt_y_rad = Geometry::deg2rad(tilt_y_deg);
return Vec3f(float(tan(tilt_y_rad)), float(tan(tilt_x_rad)), 1.f).normalized();
}
void GLGizmoPainterBase::render_triangles(const Selection& selection) const void GLGizmoPainterBase::render_triangles(const Selection& selection) const
{ {
auto* shader = wxGetApp().get_shader("mm_gouraud"); auto* shader = wxGetApp().get_shader("mm_gouraud");
@@ -119,11 +131,15 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const
float normal_z = -::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg)); float normal_z = -::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg));
Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()); Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_matrix.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>());
// Compute up direction accounting for build plate tilt
Vec3f up_direction = get_tilt_up_direction();
shader->set_uniform("volume_world_matrix", trafo_matrix); shader->set_uniform("volume_world_matrix", trafo_matrix);
shader->set_uniform("volume_mirrored", is_left_handed); shader->set_uniform("volume_mirrored", is_left_handed);
shader->set_uniform("slope.actived", m_parent.is_using_slope()); shader->set_uniform("slope.actived", m_parent.is_using_slope());
shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix); shader->set_uniform("slope.volume_world_normal_matrix", normal_matrix);
shader->set_uniform("slope.normal_z", normal_z); shader->set_uniform("slope.normal_z", normal_z);
shader->set_uniform("slope.up_direction", up_direction);
m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix);
if (is_left_handed) if (is_left_handed)
@@ -691,7 +707,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
mi->get_assemble_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix() : mi->get_assemble_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix() :
mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix(); mi->get_transformation().get_matrix() * mo->volumes[m_rr.mesh_id]->get_matrix();
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle, m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, this->get_clipping_plane_in_volume_coordinates(trafo_matrix), m_smart_fill_angle,
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true, get_tilt_up_direction());
m_triangle_selectors[m_rr.mesh_id]->request_update_render_data(); m_triangle_selectors[m_rr.mesh_id]->request_update_render_data();
m_seed_fill_last_mesh_id = m_rr.mesh_id; m_seed_fill_last_mesh_id = m_rr.mesh_id;
} }
@@ -803,7 +819,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::SinglePointCursor::cursor_factory(phr.z_world, std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::SinglePointCursor::cursor_factory(phr.z_world,
camera_pos, m_cursor_height, trafo_matrix, clp); camera_pos, m_cursor_height, trafo_matrix, clp);
m_triangle_selectors[mesh_idx]->select_patch(int(phr.first_facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, m_triangle_selectors[mesh_idx]->select_patch(int(phr.first_facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate,
m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, get_tilt_up_direction());
m_triangle_selectors[mesh_idx]->request_update_render_data(true); m_triangle_selectors[mesh_idx]->request_update_render_data(true);
m_last_mouse_click = _mouse_position; m_last_mouse_click = _mouse_position;
@@ -855,7 +871,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state); m_triangle_selectors[mesh_idx]->seed_fill_apply_on_triangles(new_state);
if (m_tool_type == ToolType::SMART_FILL) if (m_tool_type == ToolType::SMART_FILL)
m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle, m_triangle_selectors[mesh_idx]->seed_fill_select_triangles(mesh_hit, facet_idx, trafo_matrix_not_translate, clp, m_smart_fill_angle,
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true); m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, true, get_tilt_up_direction());
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
// BBS: add infill_angle parameter // BBS: add infill_angle parameter
m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, -1.f, false, true); m_triangle_selectors[mesh_idx]->bucket_fill_select_triangles(mesh_hit, facet_idx, clp, -1.f, false, true);
@@ -874,12 +890,12 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
camera_pos, m_cursor_radius, camera_pos, m_cursor_radius,
m_cursor_type, trafo_matrix, clp); m_cursor_type, trafo_matrix, clp);
m_triangle_selectors[mesh_idx]->select_patch(int(first_position.facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, m_triangle_selectors[mesh_idx]->select_patch(int(first_position.facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate,
m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, get_tilt_up_direction());
} else { } else {
for (auto first_position_it = projected_mouse_positions.cbegin(); first_position_it != projected_mouse_positions.cend() - 1; ++first_position_it) { for (auto first_position_it = projected_mouse_positions.cbegin(); first_position_it != projected_mouse_positions.cend() - 1; ++first_position_it) {
auto second_position_it = first_position_it + 1; auto second_position_it = first_position_it + 1;
std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::DoublePointCursor::cursor_factory(first_position_it->mesh_hit, second_position_it->mesh_hit, camera_pos, m_cursor_radius, m_cursor_type, trafo_matrix, clp); std::unique_ptr<TriangleSelector::Cursor> cursor = TriangleSelector::DoublePointCursor::cursor_factory(first_position_it->mesh_hit, second_position_it->mesh_hit, camera_pos, m_cursor_radius, m_cursor_type, trafo_matrix, clp);
m_triangle_selectors[mesh_idx]->select_patch(int(first_position_it->facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); m_triangle_selectors[mesh_idx]->select_patch(int(first_position_it->facet_idx), std::move(cursor), new_state, trafo_matrix_not_translate, m_triangle_splitting_enabled, m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, get_tilt_up_direction());
} }
} }
} }
@@ -953,7 +969,7 @@ bool GLGizmoPainterBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix); const TriangleSelector::ClippingPlane &clp = this->get_clipping_plane_in_volume_coordinates(trafo_matrix);
if (m_tool_type == ToolType::SMART_FILL) if (m_tool_type == ToolType::SMART_FILL)
m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle, m_triangle_selectors[m_rr.mesh_id]->seed_fill_select_triangles(m_rr.hit, int(m_rr.facet), trafo_matrix_not_translate, clp, m_smart_fill_angle,
m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f); m_paint_on_overhangs_only ? m_highlight_by_angle_threshold_deg : 0.f, false, get_tilt_up_direction());
else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER) else if (m_tool_type == ToolType::BRUSH && m_cursor_type == TriangleSelector::CursorType::POINTER)
// BBS: add infill_angle parameter // BBS: add infill_angle parameter
m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, -1.f, false); m_triangle_selectors[m_rr.mesh_id]->bucket_fill_select_triangles(m_rr.hit, int(m_rr.facet), clp, -1.f, false);

View File

@@ -281,6 +281,9 @@ protected:
bool m_paint_on_overhangs_only = false; bool m_paint_on_overhangs_only = false;
float m_highlight_by_angle_threshold_deg = 0.f; float m_highlight_by_angle_threshold_deg = 0.f;
// Returns the up direction accounting for build plate tilt (default: UnitZ)
Vec3f get_tilt_up_direction() const;
GLModel m_circle; GLModel m_circle;
Vec2d m_old_center{ Vec2d::Zero() }; Vec2d m_old_center{ Vec2d::Zero() };
float m_old_cursor_radius{ 0.0f }; float m_old_cursor_radius{ 0.0f };

View File

@@ -4364,6 +4364,8 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line(option, "printer_basic_information_printable_space#excluded-bed-area"); optgroup->append_single_option_line(option, "printer_basic_information_printable_space#excluded-bed-area");
// optgroup->append_single_option_line("printable_area"); // optgroup->append_single_option_line("printable_area");
optgroup->append_single_option_line("printable_height", "printer_basic_information_printable_space#printable-height"); optgroup->append_single_option_line("printable_height", "printer_basic_information_printable_space#printable-height");
optgroup->append_single_option_line("build_plate_tilt_x");
optgroup->append_single_option_line("build_plate_tilt_y");
optgroup->append_single_option_line("support_multi_bed_types","printer_basic_information_printable_space#support-multi-bed-types"); optgroup->append_single_option_line("support_multi_bed_types","printer_basic_information_printable_space#support-multi-bed-types");
optgroup->append_single_option_line("best_object_pos", "printer_basic_information_printable_space#best-object-position"); optgroup->append_single_option_line("best_object_pos", "printer_basic_information_printable_space#best-object-position");
// todo: for multi_extruder test // todo: for multi_extruder test