WIP: Port latest support code from BBS (#8212)

This includes the latest changes from BBS, including vertical support
painting.

Huge PR, lots of tests are needed.
This commit is contained in:
SoftFever 2025-02-26 20:53:54 +08:00 committed by GitHub
commit 41584cfae3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 2253 additions and 4614 deletions

View file

@ -7351,11 +7351,11 @@ msgstr ""
msgid "" msgid ""
"When using support material for the support interface, We recommend the " "When using support material for the support interface, We recommend the "
"following settings:\n" "following settings:\n"
"0 top z distance, 0 interface spacing, concentric pattern and disable " "0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable "
"independent support layer height" "independent support layer height"
msgstr "" msgstr ""
"当使用支持界面的支持材料时,我们推荐以下设置:\n" "当使用支持界面的支持材料时,我们推荐以下设置:\n"
"0顶层z距离0接触层间距同心图案,并且禁用独立支撑层高" "0顶层z距离0接触层间距交叠直线图案,并且禁用独立支撑层高"
msgid "" msgid ""
"Enabling this option will modify the model's shape. If your print requires " "Enabling this option will modify the model's shape. If your print requires "

View file

@ -310,16 +310,16 @@ set(lisbslic3r_sources
Support/SupportLayer.hpp Support/SupportLayer.hpp
Support/SupportMaterial.cpp Support/SupportMaterial.cpp
Support/SupportMaterial.hpp Support/SupportMaterial.hpp
Support/SupportParameters.hpp
Support/SupportSpotsGenerator.cpp Support/SupportSpotsGenerator.cpp
Support/SupportSpotsGenerator.hpp Support/SupportSpotsGenerator.hpp
Support/TreeSupport.hpp Support/TreeSupport.hpp
Support/TreeSupport.cpp Support/TreeSupport.cpp
Support/TreeSupport3D.cpp
Support/TreeSupport3D.hpp Support/TreeSupport3D.hpp
Support/TreeSupportCommon.hpp Support/TreeSupport3D.cpp
Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp Support/TreeModelVolumes.hpp
Support/TreeModelVolumes.cpp
Support/TreeSupportCommon.hpp
Support/SupportParameters.hpp
PrincipalComponents2D.cpp PrincipalComponents2D.cpp
PrincipalComponents2D.hpp PrincipalComponents2D.hpp
MinimumSpanningTree.hpp MinimumSpanningTree.hpp

View file

@ -664,6 +664,12 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &c
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); } { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::Polygons diff_clipped(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return diff(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); } { return diff(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
{ return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset); }
Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons & subject, const Slic3r::ExPolygons & clip, ApplySafetyOffset do_safety_offset)
{
return diff_ex(subject, ClipperUtils::clip_clipper_polygons_with_subject_bbox(clip, get_extents(subject).inflated(SCALED_EPSILON)), do_safety_offset);
}
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset)
{ return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); } { return _clipper(ClipperLib::ctDifference, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonsProvider(clip), do_safety_offset); }
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset) Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)

View file

@ -433,6 +433,8 @@ Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygon
// Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox(). // Optimized version clipping the "clipping" polygon using clip_clipper_polygon_with_subject_bbox().
// To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon. // To be used with complex clipping polygons, where majority of the clipping polygons are outside of the source polygon.
Slic3r::Polygons diff_clipped(const Slic3r::Polygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff_clipped(const Slic3r::Polygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::Polygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::ExPolygons diff_clipped(const Slic3r::ExPolygons &src, const Slic3r::ExPolygons &clipping, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);
Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No); Slic3r::Polygons diff(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset = ApplySafetyOffset::No);

View file

@ -57,7 +57,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipOctagramSpiral: return new FillOctagramSpiral(); case ipOctagramSpiral: return new FillOctagramSpiral();
case ipAdaptiveCubic: return new FillAdaptive::Filler(); case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler(); case ipSupportCubic: return new FillAdaptive::Filler();
case ipSupportBase: return new FillSupportBase(); case ipSupportBase: return new FillSupportBase(); // simply line fill
case ipLightning: return new FillLightning::Filler(); case ipLightning: return new FillLightning::Filler();
// BBS: for internal solid infill only // BBS: for internal solid infill only
case ipConcentricInternal: return new FillConcentricInternal(); case ipConcentricInternal: return new FillConcentricInternal();

View file

@ -1202,14 +1202,23 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions. // Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) { || (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
double top_cd = object.config().support_top_z_distance; double top_cd = object.config().support_top_z_distance;
double bottom_cd = object.config().support_bottom_z_distance; double bottom_cd = object.config().support_bottom_z_distance == 0. ? top_cd : object.config().support_bottom_z_distance;
//if (!object.print()->config().independent_support_layer_height)
{ // the actual support gap may be larger than the configured one due to rounding to layer height for organic support, regardless of independent support layer height
top_cd = std::ceil(top_cd / object.config().layer_height) * object.config().layer_height;
bottom_cd = std::ceil(bottom_cd / object.config().layer_height) * object.config().layer_height;
}
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd); double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
// raft contact distance should not trigger any warning // raft contact distance should not trigger any warning
if(last_extrusion_layer && last_extrusion_layer->support_layer) if (last_extrusion_layer && last_extrusion_layer->support_layer) {
double raft_gap = object.config().raft_contact_distance.value;
//if (!object.print()->config().independent_support_layer_height)
{
raft_gap = std::ceil(raft_gap / object.config().layer_height) * object.config().layer_height;
}
extra_gap = std::max(extra_gap, object.config().raft_contact_distance.value); extra_gap = std::max(extra_gap, object.config().raft_contact_distance.value);
}
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.) double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
+ layer_to_print.layer()->height + layer_to_print.layer()->height
+ std::max(0., extra_gap); + std::max(0., extra_gap);

View file

@ -139,7 +139,7 @@ public:
// BBS // BBS
mutable ExPolygons sharp_tails; mutable ExPolygons sharp_tails;
mutable ExPolygons cantilevers; mutable ExPolygons cantilevers;
mutable std::map<const ExPolygon*, float> sharp_tails_height; mutable std::vector<float> sharp_tails_height;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged. // (with possibly differing extruder ID and slicing parameters) and merged.
@ -150,6 +150,7 @@ public:
// These lslices are also used to detect overhangs and overlaps between successive layers, therefore it is important // These lslices are also used to detect overhangs and overlaps between successive layers, therefore it is important
// that the 1st lslice is not compensated by the Elephant foot compensation algorithm. // that the 1st lslice is not compensated by the Elephant foot compensation algorithm.
ExPolygons lslices; ExPolygons lslices;
ExPolygons lslices_extrudable; // BBS: the extrudable part of lslices used for tree support
std::vector<BoundingBox> lslices_bboxes; std::vector<BoundingBox> lslices_bboxes;
// BBS // BBS
@ -274,11 +275,10 @@ public:
ExPolygons support_islands; ExPolygons support_islands;
// Extrusion paths for the support base and for the support interface and contacts. // Extrusion paths for the support base and for the support interface and contacts.
ExtrusionEntityCollection support_fills; ExtrusionEntityCollection support_fills;
SupportInnerType support_type = stInnerNormal; SupportInnerType support_type = stInnerNormal;
// for tree supports // for tree supports
ExPolygons base_areas; ExPolygons base_areas;
ExPolygons overhang_areas;
// Is there any valid extrusion assigned to this LayerRegion? // Is there any valid extrusion assigned to this LayerRegion?
@ -311,14 +311,13 @@ protected:
{ {
ExPolygon *area; ExPolygon *area;
int type; int type;
int interface_id = 0;
coordf_t dist_to_top; // mm dist to top coordf_t dist_to_top; // mm dist to top
bool need_infill = false; bool need_infill = false;
bool need_extra_wall = false; bool need_extra_wall = false;
AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {} AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {}
}; };
enum OverhangType { Detected = 0, Enforced };
std::vector<AreaGroup> area_groups; std::vector<AreaGroup> area_groups;
std::map<const ExPolygon *, OverhangType> overhang_types;
}; };
template<typename LayerContainer> template<typename LayerContainer>

View file

@ -1363,7 +1363,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
if (!zs.empty() && is_volume_sinking(painted, volume_trafo)) { if (!zs.empty() && is_volume_sinking(painted, volume_trafo)) {
std::vector<float> zs_sinking = {0.f}; std::vector<float> zs_sinking = {0.f};
Slic3r::append(zs_sinking, zs); Slic3r::append(zs_sinking, zs);
slice_mesh_slabs(painted, zs_sinking, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, throw_on_cancel_callback); slice_mesh_slabs(painted, zs_sinking, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, nullptr, throw_on_cancel_callback);
MeshSlicingParams slicing_params; MeshSlicingParams slicing_params;
slicing_params.trafo = volume_trafo; slicing_params.trafo = volume_trafo;
@ -1374,7 +1374,7 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
bottom[0] = union_(bottom[0], bottom_slice); bottom[0] = union_(bottom[0], bottom_slice);
} else } else
slice_mesh_slabs(painted, zs, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, throw_on_cancel_callback); slice_mesh_slabs(painted, zs, volume_trafo, max_top_layers > 0 ? &top : nullptr, max_bottom_layers > 0 ? &bottom : nullptr, nullptr, throw_on_cancel_callback);
auto merge = [](std::vector<Polygons> &&src, std::vector<Polygons> &dst) { auto merge = [](std::vector<Polygons> &&src, std::vector<Polygons> &dst) {
auto it_src = find_if(src.begin(), src.end(), [](const Polygons &p){ return ! p.empty(); }); auto it_src = find_if(src.begin(), src.end(), [](const Polygons &p){ return ! p.empty(); });
if (it_src != src.end()) { if (it_src != src.end()) {

View file

@ -794,7 +794,7 @@ static std::vector<std::string> s_Preset_print_options {
"fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence", "fuzzy_skin", "fuzzy_skin_thickness", "fuzzy_skin_point_distance", "fuzzy_skin_first_layer", "fuzzy_skin_noise_type", "fuzzy_skin_scale", "fuzzy_skin_octaves", "fuzzy_skin_persistence",
"max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only", "max_volumetric_extrusion_rate_slope", "max_volumetric_extrusion_rate_slope_segment_length","extrusion_rate_smoothing_external_perimeter_only",
"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed", "inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_interface_speed", "top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed",
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height", "draft_shield", "outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height", "draft_shield",
"brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers", "brim_width", "brim_object_gap", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers",
@ -814,7 +814,7 @@ static std::vector<std::string> s_Preset_print_options {
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits", "wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
"flush_into_infill", "flush_into_objects", "flush_into_support", "flush_into_infill", "flush_into_objects", "flush_into_support",
"tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter", "tree_support_branch_angle", "tree_support_angle_slow", "tree_support_wall_count", "tree_support_top_rate", "tree_support_branch_distance", "tree_support_tip_diameter",
"tree_support_branch_diameter", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall", "tree_support_branch_diameter", "tree_support_branch_diameter_angle",
"detect_narrow_internal_solid_infill", "detect_narrow_internal_solid_infill",
"gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/ "gcode_add_line_number", "enable_arc_fitting", "precise_z_height", "infill_combination","infill_combination_max_layer_height", /*"adaptive_layer_height",*/
"support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed", "support_bottom_interface_spacing", "enable_overhang_speed", "slowdown_for_curled_perimeters", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",

View file

@ -1167,7 +1167,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
// Custom layering is not allowed for tree supports as of now. // Custom layering is not allowed for tree supports as of now.
for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx) for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx)
if (const PrintObject &print_object = *m_objects[print_object_idx]; if (const PrintObject &print_object = *m_objects[print_object_idx];
print_object.has_support_material() && is_tree(print_object.config().support_type.value) && (print_object.config().support_style.value == smsOrganic || print_object.has_support_material() && is_tree(print_object.config().support_type.value) && (print_object.config().support_style.value == smsTreeOrganic ||
// Orca: use organic as default // Orca: use organic as default
print_object.config().support_style.value == smsDefault) && print_object.config().support_style.value == smsDefault) &&
print_object.model_object()->has_custom_layering()) { print_object.model_object()->has_custom_layering()) {
@ -1340,7 +1340,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
// Prusa: Fixing crashes with invalid tip diameter or branch diameter // Prusa: Fixing crashes with invalid tip diameter or branch diameter
// https://github.com/prusa3d/PrusaSlicer/commit/96b3ae85013ac363cd1c3e98ec6b7938aeacf46d // https://github.com/prusa3d/PrusaSlicer/commit/96b3ae85013ac363cd1c3e98ec6b7938aeacf46d
if (is_tree(object->config().support_type.value) && (object->config().support_style == smsOrganic || if (is_tree(object->config().support_type.value) && (object->config().support_style == smsTreeOrganic ||
// Orca: use organic as default // Orca: use organic as default
object->config().support_style == smsDefault)) { object->config().support_style == smsDefault)) {
float extrusion_width = std::min( float extrusion_width = std::min(

View file

@ -385,7 +385,7 @@ public:
size_t support_layer_count() const { return m_support_layers.size(); } size_t support_layer_count() const { return m_support_layers.size(); }
void clear_support_layers(); void clear_support_layers();
SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } SupportLayer* get_support_layer(int idx) { return idx<m_support_layers.size()? m_support_layers[idx]:nullptr; }
const SupportLayer* get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const; const SupportLayer* get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
SupportLayer* get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon); SupportLayer* get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon);
SupportLayer* add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z); SupportLayer* add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z);
@ -427,7 +427,7 @@ public:
std::vector<Polygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); } std::vector<Polygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
// Helpers to project custom facets on slices // Helpers to project custom facets on slices
void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<Polygons>& expolys) const; void project_and_append_custom_facets(bool seam, EnforcerBlockerType type, std::vector<Polygons>& expolys, std::vector<std::pair<Vec3f,Vec3f>>* vertical_points=nullptr) const;
//BBS //BBS
BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name); BoundingBox get_first_layer_bbox(float& area, float& layer_height, std::string& name);

View file

@ -232,7 +232,7 @@ static t_config_enum_values s_keys_map_SupportMaterialStyle {
{ "tree_slim", smsTreeSlim }, { "tree_slim", smsTreeSlim },
{ "tree_strong", smsTreeStrong }, { "tree_strong", smsTreeStrong },
{ "tree_hybrid", smsTreeHybrid }, { "tree_hybrid", smsTreeHybrid },
{ "organic", smsOrganic } { "organic", smsTreeOrganic }
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
@ -4618,6 +4618,17 @@ void PrintConfigDef::init_fff_params()
//Support with too small spacing may touch the object and difficult to remove. //Support with too small spacing may touch the object and difficult to remove.
def->set_default_value(new ConfigOptionFloat(0.35)); def->set_default_value(new ConfigOptionFloat(0.35));
def = this->add("support_object_first_layer_gap", coFloat);
def->label = L("Support/object first layer gap");
def->category = L("Support");
def->tooltip = L("XY separation between an object and its support at the first layer.");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comAdvanced;
//Support with too small spacing may touch the object and difficult to remove.
def->set_default_value(new ConfigOptionFloat(0.2));
def = this->add("support_angle", coFloat); def = this->add("support_angle", coFloat);
def->label = L("Pattern angle"); def->label = L("Pattern angle");
def->category = L("Support"); def->category = L("Support");
@ -5030,16 +5041,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(5.)); def->set_default_value(new ConfigOptionFloat(5.));
def = this->add("tree_support_branch_diameter_organic", coFloat);
def->label = L("Tree support branch diameter");
def->category = L("Support");
def->tooltip = L("This setting determines the initial diameter of support nodes.");
def->sidetext = L("mm");
def->min = 1.0;
def->max = 10;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2.));
def = this->add("tree_support_branch_diameter_angle", coFloat); def = this->add("tree_support_branch_diameter_angle", coFloat);
// TRN PrintSettings: #lmFIXME // TRN PrintSettings: #lmFIXME
def->label = L("Branch Diameter Angle"); def->label = L("Branch Diameter Angle");
@ -5054,23 +5055,22 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(5)); def->set_default_value(new ConfigOptionFloat(5));
def = this->add("tree_support_branch_diameter_double_wall", coFloat); def = this->add("tree_support_branch_diameter_organic", coFloat);
def->label = L("Branch Diameter with double walls"); def->label = L("Tree support branch diameter");
def->category = L("Support"); def->category = L("Support");
// TRN PrintSettings: "Organic supports" > "Branch Diameter" def->tooltip = L("This setting determines the initial diameter of support nodes.");
def->tooltip = L("Branches with area larger than the area of a circle of this diameter will be printed with double walls for stability. "
"Set this value to zero for no double walls.");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0; def->min = 1.0;
def->max = 100.f; def->max = 10;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(3.)); def->set_default_value(new ConfigOptionFloat(2.));
def = this->add("tree_support_wall_count", coInt); def = this->add("tree_support_wall_count", coInt);
def->label = L("Support wall loops"); def->label = L("Support wall loops");
def->category = L("Support"); def->category = L("Support");
def->tooltip = L("This setting specify the count of walls around support"); def->tooltip = L("This setting specifies the count of support walls in the range of [0,2]. 0 means auto.");
def->min = 0; def->min = 0;
def->max = 2;
def->mode = comAdvanced; def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0)); def->set_default_value(new ConfigOptionInt(0));

View file

@ -132,7 +132,7 @@ enum SupportMaterialPattern {
}; };
enum SupportMaterialStyle { enum SupportMaterialStyle {
smsDefault, smsGrid, smsSnug, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic, smsDefault, smsGrid, smsSnug, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic,
}; };
enum LongRectrationLevel enum LongRectrationLevel
@ -840,6 +840,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, support_threshold_angle)) ((ConfigOptionInt, support_threshold_angle))
((ConfigOptionFloatOrPercent, support_threshold_overlap)) ((ConfigOptionFloatOrPercent, support_threshold_overlap))
((ConfigOptionFloat, support_object_xy_distance)) ((ConfigOptionFloat, support_object_xy_distance))
((ConfigOptionFloat, support_object_first_layer_gap))
((ConfigOptionFloat, xy_hole_compensation)) ((ConfigOptionFloat, xy_hole_compensation))
((ConfigOptionFloat, xy_contour_compensation)) ((ConfigOptionFloat, xy_contour_compensation))
((ConfigOptionBool, flush_into_objects)) ((ConfigOptionBool, flush_into_objects))
@ -850,9 +851,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, tree_support_branch_distance)) ((ConfigOptionFloat, tree_support_branch_distance))
((ConfigOptionFloat, tree_support_tip_diameter)) ((ConfigOptionFloat, tree_support_tip_diameter))
((ConfigOptionFloat, tree_support_branch_diameter)) ((ConfigOptionFloat, tree_support_branch_diameter))
((ConfigOptionFloat, tree_support_branch_diameter_angle))
((ConfigOptionFloat, tree_support_branch_diameter_double_wall))
((ConfigOptionFloat, tree_support_branch_angle)) ((ConfigOptionFloat, tree_support_branch_angle))
((ConfigOptionFloat, tree_support_branch_diameter_angle))
((ConfigOptionFloat, tree_support_angle_slow)) ((ConfigOptionFloat, tree_support_angle_slow))
((ConfigOptionInt, tree_support_wall_count)) ((ConfigOptionInt, tree_support_wall_count))
((ConfigOptionBool, tree_support_adaptive_layer_height)) ((ConfigOptionBool, tree_support_adaptive_layer_height))

View file

@ -633,13 +633,9 @@ void PrintObject::generate_support_material()
if (this->set_started(posSupportMaterial)) { if (this->set_started(posSupportMaterial)) {
this->clear_support_layers(); this->clear_support_layers();
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) { if(!has_support() && !m_print->get_no_check_flag()) {
m_print->set_status(50, L("Generating support"));
this->_generate_support_material();
m_print->throw_if_canceled();
} else if(!m_print->get_no_check_flag()) {
// BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled // BBS: pop a warning if objects have significant amount of overhangs but support material is not enabled
// Note: we also need to pop warning if support is disabled and only raft is enabled
m_print->set_status(50, L("Checking support necessity")); m_print->set_status(50, L("Checking support necessity"));
typedef std::chrono::high_resolution_clock clock_; typedef std::chrono::high_resolution_clock clock_;
typedef std::chrono::duration<double, std::ratio<1> > second_; typedef std::chrono::duration<double, std::ratio<1> > second_;
@ -669,6 +665,12 @@ void PrintObject::generate_support_material()
#endif #endif
} }
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && !m_layers.empty())) {
m_print->set_status(50, L("Generating support"));
this->_generate_support_material();
m_print->throw_if_canceled();
}
this->set_done(posSupportMaterial); this->set_done(posSupportMaterial);
} }
} }
@ -731,7 +733,8 @@ void PrintObject::simplify_extrusion_path()
} }
if (this->set_started(posSimplifySupportPath)) { if (this->set_started(posSimplifySupportPath)) {
//BBS: share same progress //BBS: disable circle simplification for support as it causes separation of support walls
#if 0
m_print->set_status(75, L("Optimizing toolpath")); m_print->set_status(75, L("Optimizing toolpath"));
BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - start";
tbb::parallel_for( tbb::parallel_for(
@ -745,6 +748,7 @@ void PrintObject::simplify_extrusion_path()
); );
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - end";
#endif
this->set_done(posSimplifySupportPath); this->set_done(posSimplifySupportPath);
} }
} }
@ -843,14 +847,8 @@ void PrintObject::clear_support_layers()
std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache() std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
{ {
if (!m_tree_support_preview_cache) { if (!m_tree_support_preview_cache) {
const coordf_t layer_height = m_config.layer_height.value;
const coordf_t xy_distance = m_config.support_object_xy_distance.value; const coordf_t xy_distance = m_config.support_object_xy_distance.value;
const double angle = m_config.tree_support_branch_angle.value * M_PI / 180.; m_tree_support_preview_cache = std::make_shared<TreeSupportData>(*this, xy_distance, g_config_tree_support_collision_resolution);
const coordf_t max_move_distance
= (angle < M_PI / 2) ? (coordf_t)(tan(angle) * layer_height) : std::numeric_limits<coordf_t>::max();
const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution;
m_tree_support_preview_cache = std::make_shared<TreeSupportData>(*this, xy_distance, max_move_distance, radius_sample_resolution);
} }
return m_tree_support_preview_cache; return m_tree_support_preview_cache;
@ -1015,6 +1013,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_base_pattern" || opt_key == "support_base_pattern"
|| opt_key == "support_style" || opt_key == "support_style"
|| opt_key == "support_object_xy_distance" || opt_key == "support_object_xy_distance"
|| opt_key == "support_object_first_layer_gap"
|| opt_key == "support_base_pattern_spacing" || opt_key == "support_base_pattern_spacing"
|| opt_key == "support_expansion" || opt_key == "support_expansion"
//|| opt_key == "independent_support_layer_height" // BBS //|| opt_key == "independent_support_layer_height" // BBS
@ -1036,7 +1035,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "tree_support_branch_diameter" || opt_key == "tree_support_branch_diameter"
|| opt_key == "tree_support_branch_diameter_organic" || opt_key == "tree_support_branch_diameter_organic"
|| opt_key == "tree_support_branch_diameter_angle" || opt_key == "tree_support_branch_diameter_angle"
|| opt_key == "tree_support_branch_diameter_double_wall"
|| opt_key == "tree_support_branch_angle" || opt_key == "tree_support_branch_angle"
|| opt_key == "tree_support_branch_angle_organic" || opt_key == "tree_support_branch_angle_organic"
|| opt_key == "tree_support_angle_slow" || opt_key == "tree_support_angle_slow"
@ -3935,91 +3933,8 @@ template void PrintObject::remove_bridges_from_contacts<Polygons>(
SupportNecessaryType PrintObject::is_support_necessary() SupportNecessaryType PrintObject::is_support_necessary()
{ {
static const double super_overhang_area_threshold = SQ(scale_(5.0));
const double cantilevel_dist_thresh = scale_(6); const double cantilevel_dist_thresh = scale_(6);
#if 0
double threshold_rad = (m_config.support_threshold_angle.value < EPSILON ? 30 : m_config.support_threshold_angle.value + 1) * M_PI / 180.;
int enforce_support_layers = m_config.enforce_support_layers;
// not fixing in extrusion width % PR b/c never called
const coordf_t extrusion_width = m_config.line_width.value;
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
float max_bridge_length = scale_(m_config.max_bridge_length.value);
const bool bridge_no_support = max_bridge_length > 0;// config.bridge_no_support.value;
for (size_t layer_nr = enforce_support_layers + 1; layer_nr < this->layer_count(); layer_nr++) {
Layer* layer = m_layers[layer_nr];
Layer* lower_layer = layer->lower_layer;
coordf_t support_offset_scaled = extrusion_width_scaled * 0.9;
ExPolygons lower_layer_offseted = offset_ex(lower_layer->lslices, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
// 1. check sharp tail
for (const LayerRegion* layerm : layer->regions()) {
for (const ExPolygon& expoly : layerm->raw_slices) {
// detect sharp tail
if (intersection_ex({ expoly }, lower_layer_offseted).empty())
return SharpTail;
}
}
// 2. check overhang area
ExPolygons super_overhang_expolys = std::move(diff_ex(layer->lslices, lower_layer_offseted));
super_overhang_expolys.erase(std::remove_if(
super_overhang_expolys.begin(),
super_overhang_expolys.end(),
[extrusion_width_scaled](ExPolygon& area) {
return offset_ex(area, -0.1 * extrusion_width_scaled).empty();
}),
super_overhang_expolys.end());
// remove bridge
if (bridge_no_support)
remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &super_overhang_expolys, max_bridge_length);
Polygons super_overhang_polys = to_polygons(super_overhang_expolys);
super_overhang_polys.erase(std::remove_if(
super_overhang_polys.begin(),
super_overhang_polys.end(),
[extrusion_width_scaled](Polygon& area) {
return offset_ex(area, -0.1 * extrusion_width_scaled).empty();
}),
super_overhang_polys.end());
double super_overhang_area = 0.0;
for (Polygon& poly : super_overhang_polys) {
bool is_ccw = poly.is_counter_clockwise();
double area_ = poly.area();
if (is_ccw) {
if (area_ > super_overhang_area_threshold)
return LargeOverhang;
super_overhang_area += area_;
}
else {
super_overhang_area -= area_;
}
}
//if (super_overhang_area > super_overhang_area_threshold)
// return LargeOverhang;
// 3. check overhang distance
const double distance_threshold_scaled = extrusion_width_scaled * 2;
ExPolygons lower_layer_offseted_2 = offset_ex(lower_layer->lslices, distance_threshold_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
ExPolygons exceed_overhang = std::move(diff_ex(super_overhang_polys, lower_layer_offseted_2));
exceed_overhang.erase(std::remove_if(
exceed_overhang.begin(),
exceed_overhang.end(),
[extrusion_width_scaled](ExPolygon& area) {
// tolerance for 1 extrusion width offset
return offset_ex(area, -0.5 * extrusion_width_scaled).empty();
}),
exceed_overhang.end());
if (!exceed_overhang.empty())
return LargeOverhang;
}
#else
TreeSupport tree_support(*this, m_slicing_params); TreeSupport tree_support(*this, m_slicing_params);
tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection
tree_support.detect_overhangs(true); tree_support.detect_overhangs(true);
@ -4028,7 +3943,7 @@ SupportNecessaryType PrintObject::is_support_necessary()
return SharpTail; return SharpTail;
else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh) else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh)
return Cantilever; return Cantilever;
#endif
return NoNeedSupp; return NoNeedSupp;
} }
@ -4219,7 +4134,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index
} }
void PrintObject::project_and_append_custom_facets( void PrintObject::project_and_append_custom_facets(
bool seam, EnforcerBlockerType type, std::vector<Polygons>& out) const bool seam, EnforcerBlockerType type, std::vector<Polygons>& out, std::vector<std::pair<Vec3f, Vec3f>>* vertical_points) const
{ {
for (const ModelVolume* mv : this->model_object()->volumes) for (const ModelVolume* mv : this->model_object()->volumes)
if (mv->is_model_part()) { if (mv->is_model_part()) {
@ -4234,7 +4149,7 @@ void PrintObject::project_and_append_custom_facets(
else { else {
std::vector<Polygons> projected; std::vector<Polygons> projected;
// Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane. // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane.
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}); slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, vertical_points, [](){});
// Merge these projections with the output, layer by layer. // Merge these projections with the output, layer by layer.
assert(! projected.empty()); assert(! projected.empty());
assert(out.empty() || out.size() == projected.size()); assert(out.empty() || out.size() == projected.size());

View file

@ -808,10 +808,11 @@ void PrintObject::slice()
std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy); std::string warning = fix_slicing_errors(this, m_layers, [this](){ m_print->throw_if_canceled(); }, firstLayerReplacedBy);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
//BBS: send warning message to slicing callback //BBS: send warning message to slicing callback
if (!warning.empty()) { // This warning is inaccurate, because the empty layers may have been replaced, or the model has supports.
BOOST_LOG_TRIVIAL(info) << warning; //if (!warning.empty()) {
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers); // BOOST_LOG_TRIVIAL(info) << warning;
} // this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers);
//}
#endif #endif
// Detect and process holes that should be converted to polyholes // Detect and process holes that should be converted to polyholes

View file

@ -1030,6 +1030,9 @@ void reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const s
void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near) void chain_and_reorder_extrusion_entities(std::vector<ExtrusionEntity*> &entities, const Point *start_near)
{ {
// this function crashes if there are empty elements in entities
entities.erase(std::remove_if(entities.begin(), entities.end(), [](ExtrusionEntity *entity) { return static_cast<ExtrusionEntityCollection *>(entity)->empty(); }),
entities.end());
reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near)); reorder_extrusion_entities(entities, chain_extrusion_entities(entities, start_near));
} }

View file

@ -114,7 +114,7 @@ SlicingParameters SlicingParameters::create_from_config(
params.min_layer_height = std::min(params.min_layer_height, params.layer_height); params.min_layer_height = std::min(params.min_layer_height, params.layer_height);
params.max_layer_height = std::max(params.max_layer_height, params.layer_height); params.max_layer_height = std::max(params.max_layer_height, params.layer_height);
if (! soluble_interface || is_tree_slim(object_config.support_type.value, object_config.support_style.value)) { if (! soluble_interface) {
params.gap_raft_object = object_config.raft_contact_distance.value; params.gap_raft_object = object_config.raft_contact_distance.value;
//BBS //BBS
params.gap_object_support = object_config.support_bottom_z_distance.value; params.gap_object_support = object_config.support_bottom_z_distance.value;

View file

@ -36,7 +36,7 @@ namespace Slic3r {
// how much we extend support around the actual contact area // how much we extend support around the actual contact area
//FIXME this should be dependent on the nozzle diameter! //FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5 #define SUPPORT_MATERIAL_MARGIN 1.5
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
//#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 //#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5
@ -140,10 +140,11 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
if (! intermediate_layers.empty() && support_params.has_interfaces()) { if (! intermediate_layers.empty() && support_params.has_interfaces()) {
// For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers. // For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
const bool snug_supports = config.support_style.value == smsSnug; const bool snug_supports = support_params.support_style == smsSnug;
const bool smooth_supports = config.support_style.value != smsGrid; const bool smooth_supports = support_params.support_style != smsGrid;
SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first;
SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second;
interface_layers.assign(intermediate_layers.size(), nullptr); interface_layers.assign(intermediate_layers.size(), nullptr);
if (support_params.has_base_interfaces()) if (support_params.has_base_interfaces())
base_interface_layers.assign(intermediate_layers.size(), nullptr); base_interface_layers.assign(intermediate_layers.size(), nullptr);
@ -152,7 +153,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
const auto closing_distance = smoothing_distance; // scaled<float>(config.support_material_closing_radius.value); const auto closing_distance = smoothing_distance; // scaled<float>(config.support_material_closing_radius.value);
// Insert a new layer into base_interface_layers, if intersection with base exists. // Insert a new layer into base_interface_layers, if intersection with base exists.
auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius]( auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius](
SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer,
const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* {
bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty(); bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty();
assert(! bottom.empty() || ! top.empty() || has_top_interface); assert(! bottom.empty() || ! top.empty() || has_top_interface);
@ -194,7 +195,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
}; };
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())), tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
[&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params, [&bottom_contacts, &top_contacts, &top_interface_layers, &top_base_interface_layers, &intermediate_layers, &insert_layer, &support_params,
snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) { snug_supports, &interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
// Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below // Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
// this intermediate layer. // this intermediate layer.
// Index of the first top contact layer intersecting the current intermediate layer. // Index of the first top contact layer intersecting the current intermediate layer.
@ -230,7 +231,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
//FIXME maybe this adds one interface layer in excess? //FIXME maybe this adds one interface layer in excess?
if (top_contact_layer.bottom_z - EPSILON > top_z) if (top_contact_layer.bottom_z - EPSILON > top_z)
break; break;
polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, polygons_append(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface,
// For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers. // For snug supports, project the overhang polygons covering the whole overhang, so that they will merge without a gap with support polygons of the other layers.
// For grid supports, merging of support regions will be performed by the projection into grid. // For grid supports, merging of support regions will be performed by the projection into grid.
snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons); snug_supports ? *top_contact_layer.overhang_polygons : top_contact_layer.polygons);
@ -242,7 +243,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max(); coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
if (support_params.num_bottom_base_interface_layers > 0) if (support_params.num_bottom_base_interface_layers > 0)
// Some bottom base interface layers will be generated. // Some bottom base interface layers will be generated.
bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ? bottom_interface_z = support_params.num_bottom_interface_layers_only() == 0 ?
// Only base interface layers to generate. // Only base interface layers to generate.
std::numeric_limits<coordf_t>::max() : std::numeric_limits<coordf_t>::max() :
intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z; intermediate_layers[std::max(0, idx_intermediate_layer - int(support_params.num_bottom_interface_layers_only()))]->bottom_z;
@ -307,7 +308,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers); base_interface_layers = merge_remove_empty(base_interface_layers, top_base_interface_layers);
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
} }
return base_and_interface_layers; return base_and_interface_layers;
} }
@ -329,19 +330,21 @@ SupportGeneratorLayersPtr generate_raft_base(
const BrimType brim_type = object.config().brim_type; const BrimType brim_type = object.config().brim_type;
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner; const bool brim_inner = brim_type == btInnerOnly || brim_type == btOuterAndInner;
const auto brim_separation = scaled<float>(object.config().brim_object_gap.value + object.config().brim_width.value); // BBS: the pattern of raft and brim are the same, thus the brim can be serpated by support raft.
const auto brim_object_gap = scaled<float>(object.config().brim_object_gap.value);
//const auto brim_object_gap = scaled<float>(object.config().brim_object_gap.value + object.config().brim_width.value);
for (const ExPolygon &ex : object.layers().front()->lslices) { for (const ExPolygon &ex : object.layers().front()->lslices) {
if (brim_outer && brim_inner) if (brim_outer && brim_inner)
polygons_append(brim, offset(ex, brim_separation)); polygons_append(brim, offset(ex, brim_object_gap));
else { else {
if (brim_outer) if (brim_outer)
polygons_append(brim, offset(ex.contour, brim_separation, ClipperLib::jtRound, float(scale_(0.1)))); polygons_append(brim, offset(ex.contour, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1))));
else else
brim.emplace_back(ex.contour); brim.emplace_back(ex.contour);
if (brim_inner) { if (brim_inner) {
Polygons holes = ex.holes; Polygons holes = ex.holes;
polygons_reverse(holes); polygons_reverse(holes);
holes = shrink(holes, brim_separation, ClipperLib::jtRound, float(scale_(0.1))); holes = shrink(holes, brim_object_gap, ClipperLib::jtRound, float(scale_(0.1)));
polygons_reverse(holes); polygons_reverse(holes);
polygons_append(brim, std::move(holes)); polygons_append(brim, std::move(holes));
} else } else
@ -378,7 +381,7 @@ SupportGeneratorLayersPtr generate_raft_base(
polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (base_interfaces != nullptr && ! base_interfaces->polygons.empty()) if (base_interfaces != nullptr && ! base_interfaces->polygons.empty())
polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS)); polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
// Output vector. // Output vector.
SupportGeneratorLayersPtr raft_layers; SupportGeneratorLayersPtr raft_layers;
@ -402,12 +405,12 @@ SupportGeneratorLayersPtr generate_raft_base(
} }
if (! interface_polygons.empty()) { if (! interface_polygons.empty()) {
// Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface.
base = union_(base, interface_polygons); base = union_(base, interface_polygons);
} }
// Do not add the raft contact layer, only add the raft layers below the contact layer. // Do not add the raft contact layer, only add the raft layers below the contact layer.
// Insert the 1st layer. // Insert the 1st layer.
{ {
SupportGeneratorLayer &new_layer = layer_storage.allocate_unguarded(slicing_params.base_raft_layers > 0 ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface); SupportGeneratorLayer &new_layer = layer_storage.allocate(slicing_params.base_raft_layers > 0 ? SupporLayerType::RaftBase : SupporLayerType::RaftInterface);
raft_layers.push_back(&new_layer); raft_layers.push_back(&new_layer);
new_layer.print_z = slicing_params.first_print_layer_height; new_layer.print_z = slicing_params.first_print_layer_height;
new_layer.height = slicing_params.first_print_layer_height; new_layer.height = slicing_params.first_print_layer_height;
@ -441,7 +444,13 @@ SupportGeneratorLayersPtr generate_raft_base(
if (columns_base != nullptr) { if (columns_base != nullptr) {
// Expand the bases of the support columns in the 1st layer. // Expand the bases of the support columns in the 1st layer.
Polygons &raft = columns_base->polygons; Polygons &raft = columns_base->polygons;
Polygons trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS); Polygons trimming;
// BBS: if first layer of support is intersected with object island, it must have the same function as brim unless in nobrim mode.
// brim_object_gap is changed to 0 by default, it's no longer appropriate to use it to determine the gap of first layer support.
//if (object.has_brim())
// trimming = offset(object.layers().front()->lslices, (float)scale_(object.config().brim_object_gap.value), SUPPORT_SURFACES_OFFSET_PARAMETERS);
//else
trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy_first_layer), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (inflate_factor_1st_layer > SCALED_EPSILON) { if (inflate_factor_1st_layer > SCALED_EPSILON) {
// Inflate in multiple steps to avoid leaking of the support 1st layer through object walls. // Inflate in multiple steps to avoid leaking of the support 1st layer through object walls.
auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width()))); auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width())));
@ -536,10 +545,10 @@ static Polylines draw_perimeters(const ExPolygon &expoly, double clip_length)
return polylines; return polylines;
} }
static inline void tree_supports_generate_paths( void tree_supports_generate_paths(
ExtrusionEntitiesPtr &dst, ExtrusionEntitiesPtr &dst,
const Polygons &polygons, const Polygons &polygons,
const Flow &flow, const Flow &flow,
const SupportParameters &support_params) const SupportParameters &support_params)
{ {
// Offset expolygon inside, returns number of expolygons collected (0 or 1). // Offset expolygon inside, returns number of expolygons collected (0 or 1).
@ -606,7 +615,7 @@ static inline void tree_supports_generate_paths(
// No hole remaining after an offset. Just copy the outer contour. // No hole remaining after an offset. Just copy the outer contour.
append(out, std::move(contours)); append(out, std::move(contours));
} else { } else {
// Negative offset. There is a chance, that the offsetted hole intersects the outer contour. // Negative offset. There is a chance, that the offsetted hole intersects the outer contour.
// Subtract the offsetted holes from the offsetted contours. // Subtract the offsetted holes from the offsetted contours.
ClipperLib_Z::Clipper clipper; ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
@ -637,124 +646,126 @@ static inline void tree_supports_generate_paths(
ClipperLib_Z::Paths anchor_candidates; ClipperLib_Z::Paths anchor_candidates;
for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) { for (ExPolygon& expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5 * flow.scaled_width()))) {
std::unique_ptr<ExtrusionEntityCollection> eec; std::unique_ptr<ExtrusionEntityCollection> eec;
ExPolygons regions_to_draw_inner_wall{expoly};
if (support_params.tree_branch_diameter_double_wall_area_scaled > 0) if (support_params.tree_branch_diameter_double_wall_area_scaled > 0)
if (double area = expoly.area(); area > support_params.tree_branch_diameter_double_wall_area_scaled) { if (double area = expoly.area(); area > support_params.tree_branch_diameter_double_wall_area_scaled) {
BOOST_LOG_TRIVIAL(debug)<< "TreeSupports: double wall area: " << area<< " > " << support_params.tree_branch_diameter_double_wall_area_scaled;
eec = std::make_unique<ExtrusionEntityCollection>(); eec = std::make_unique<ExtrusionEntityCollection>();
// Don't reoder internal / external loops of the same island, always start with the internal loop. // Don't reorder internal / external loops of the same island, always start with the internal loop.
eec->no_sort = true; eec->no_sort = true;
// Make the tree branch stable by adding another perimeter. // Make the tree branch stable by adding another perimeter.
ExPolygons level2 = offset2_ex({ expoly }, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width()); ExPolygons level2 = offset2_ex({expoly}, -1.5 * flow.scaled_width(), 0.5 * flow.scaled_width());
if (level2.size() == 1) { if (level2.size() > 0) {
Polylines polylines; regions_to_draw_inner_wall = level2;
extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), extrusion_entities_append_paths(eec->entities, draw_perimeters(expoly, clip_length), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(),
// Disable reversal of the path, always start with the anchor, always print CCW. // Disable reversal of the path, always start with the anchor, always print CCW.
false); false);
expoly = level2.front(); expoly = level2.front();
} }
} }
for (ExPolygon &expoly : regions_to_draw_inner_wall)
{
// Try to produce one more perimeter to place the seam anchor.
// First genrate a 2nd perimeter loop as a source for anchor candidates.
// The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection.
anchor_candidates.clear();
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates);
// Orient all contours CW.
for (auto &path : anchor_candidates)
if (ClipperLib_Z::Area(path) > 0) std::reverse(path.begin(), path.end());
// Try to produce one more perimeter to place the seam anchor. // Draw the perimeters.
// First genrate a 2nd perimeter loop as a source for anchor candidates. Polylines polylines;
// The anchor candidate points are annotated with an index of the source contour or with -1 if on intersection. polylines.reserve(expoly.holes.size() + 1);
anchor_candidates.clear(); for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++idx_loop) {
shrink_expolygon_with_contour_idx(expoly, flow.scaled_width(), DefaultJoinType, 1.2, anchor_candidates); // Open the loop with a seam.
// Orient all contours CW. const Polygon &loop = expoly.contour_or_hole(idx_loop);
for (auto &path : anchor_candidates) Polyline pl(loop.points);
if (ClipperLib_Z::Area(path) > 0) // Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor.
std::reverse(path.begin(), path.end()); if (idx_loop == 0)
// It is an outer contour.
// Draw the perimeters. pl.reverse();
Polylines polylines; pl.points.emplace_back(pl.points.front());
polylines.reserve(expoly.holes.size() + 1); pl.clip_end(clip_length);
for (int idx_loop = 0; idx_loop < int(expoly.num_contours()); ++ idx_loop) { if (pl.size() < 2) continue;
// Open the loop with a seam. // Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour.
const Polygon &loop = expoly.contour_or_hole(idx_loop); ClipperLib_Z::Path *closest_contour = nullptr;
Polyline pl(loop.points); Vec2d closest_point;
// Orient all contours CW, because the anchor will be added to the end of polyline while we want to start a loop with the anchor. int closest_point_idx = -1;
if (idx_loop == 0) double closest_point_t = 0.;
// It is an outer contour. double d2min = std::numeric_limits<double>::max();
pl.reverse(); Vec2d seam_pt = pl.back().cast<double>();
pl.points.emplace_back(pl.points.front()); for (ClipperLib_Z::Path &path : anchor_candidates)
pl.clip_end(clip_length); for (int i = 0; i < int(path.size()); ++i) {
if (pl.size() < 2) int j = next_idx_modulo(i, path);
continue; if (path[i].z() == idx_loop || path[j].z() == idx_loop) {
// Find the foot of the seam point on anchor_candidates. Only pick an anchor point that was created by offsetting the source contour. Vec2d pi(path[i].x(), path[i].y());
ClipperLib_Z::Path *closest_contour = nullptr; Vec2d pj(path[j].x(), path[j].y());
Vec2d closest_point; Vec2d v = pj - pi;
int closest_point_idx = -1; Vec2d w = seam_pt - pi;
double closest_point_t = 0.; auto l2 = v.squaredNorm();
double d2min = std::numeric_limits<double>::max(); auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.);
Vec2d seam_pt = pl.back().cast<double>(); if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) {
for (ClipperLib_Z::Path &path : anchor_candidates) // Closest point.
for (int i = 0; i < int(path.size()); ++ i) { Vec2d fp = pi + v * t;
int j = next_idx_modulo(i, path); double d2 = (fp - seam_pt).squaredNorm();
if (path[i].z() == idx_loop || path[j].z() == idx_loop) { if (d2 < d2min) {
Vec2d pi(path[i].x(), path[i].y()); d2min = d2;
Vec2d pj(path[j].x(), path[j].y()); closest_contour = &path;
Vec2d v = pj - pi; closest_point = fp;
Vec2d w = seam_pt - pi; closest_point_idx = i;
auto l2 = v.squaredNorm(); closest_point_t = t;
auto t = std::clamp((l2 == 0) ? 0 : v.dot(w) / l2, 0., 1.); }
if ((path[i].z() == idx_loop || t > EPSILON) && (path[j].z() == idx_loop || t < 1. - EPSILON)) {
// Closest point.
Vec2d fp = pi + v * t;
double d2 = (fp - seam_pt).squaredNorm();
if (d2 < d2min) {
d2min = d2;
closest_contour = &path;
closest_point = fp;
closest_point_idx = i;
closest_point_t = t;
} }
} }
} }
} if (d2min < sqr(flow.scaled_width() * 3.)) {
if (d2min < sqr(flow.scaled_width() * 3.)) { // Try to cut an anchor from the closest_contour.
// Try to cut an anchor from the closest_contour. // Both closest_contour and pl are CW oriented.
// Both closest_contour and pl are CW oriented. pl.points.emplace_back(closest_point.cast<coord_t>());
pl.points.emplace_back(closest_point.cast<coord_t>()); const ClipperLib_Z::Path &path = *closest_contour;
const ClipperLib_Z::Path &path = *closest_contour; double remaining_length = anchor_length - (seam_pt - closest_point).norm();
double remaining_length = anchor_length - (seam_pt - closest_point).norm(); int i = closest_point_idx;
int i = closest_point_idx; int j = next_idx_modulo(i, *closest_contour);
int j = next_idx_modulo(i, *closest_contour); Vec2d pi(path[i].x(), path[i].y());
Vec2d pi(path[i].x(), path[i].y()); Vec2d pj(path[j].x(), path[j].y());
Vec2d pj(path[j].x(), path[j].y()); Vec2d v = pj - pi;
Vec2d v = pj - pi; double l = v.norm();
double l = v.norm(); if (remaining_length < (1. - closest_point_t) * l) {
if (remaining_length < (1. - closest_point_t) * l) { // Just trim the current line.
// Just trim the current line. pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>());
pl.points.emplace_back((closest_point + v * (remaining_length / l)).cast<coord_t>()); } else {
} else { // Take the rest of the current line, continue with the other lines.
// Take the rest of the current line, continue with the other lines.
pl.points.emplace_back(path[j].x(), path[j].y());
pi = pj;
for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) {
j = next_idx_modulo(i, path);
pj = Vec2d(path[j].x(), path[j].y());
v = pj - pi;
l = v.norm();
if (i == closest_point_idx) {
// Back at the first segment. Most likely this should not happen and we may end the anchor.
break;
}
if (remaining_length <= l) {
pl.points.emplace_back((pi + v * (remaining_length / l)).cast<coord_t>());
break;
}
pl.points.emplace_back(path[j].x(), path[j].y()); pl.points.emplace_back(path[j].x(), path[j].y());
remaining_length -= l; pi = pj;
for (i = j; path[i].z() == idx_loop && remaining_length > 0; i = j, pi = pj) {
j = next_idx_modulo(i, path);
pj = Vec2d(path[j].x(), path[j].y());
v = pj - pi;
l = v.norm();
if (i == closest_point_idx) {
// Back at the first segment. Most likely this should not happen and we may end the anchor.
break;
}
if (remaining_length <= l) {
pl.points.emplace_back((pi + v * (remaining_length / l)).cast<coord_t>());
break;
}
pl.points.emplace_back(path[j].x(), path[j].y());
remaining_length -= l;
}
} }
} }
// Start with the anchor.
pl.reverse();
polylines.emplace_back(std::move(pl));
} }
// Start with the anchor.
pl.reverse();
polylines.emplace_back(std::move(pl));
}
ExtrusionEntitiesPtr &out = eec ? eec->entities : dst; ExtrusionEntitiesPtr &out = eec ? eec->entities : dst;
extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(), extrusion_entities_append_paths(out, std::move(polylines), ExtrusionRole::erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height(),
// Disable reversal of the path, always start with the anchor, always print CCW. // Disable reversal of the path, always start with the anchor, always print CCW.
false); false);
}
if (eec) { if (eec) {
std::reverse(eec->entities.begin(), eec->entities.end()); std::reverse(eec->entities.begin(), eec->entities.end());
dst.emplace_back(eec.release()); dst.emplace_back(eec.release());
@ -762,20 +773,27 @@ static inline void tree_supports_generate_paths(
} }
} }
static inline void fill_expolygons_with_sheath_generate_paths( void fill_expolygons_with_sheath_generate_paths(
ExtrusionEntitiesPtr &dst, ExtrusionEntitiesPtr &dst,
const Polygons &polygons, const Polygons &polygons,
Fill *filler, Fill *filler,
float density, float density,
ExtrusionRole role, ExtrusionRole role,
const Flow &flow, const Flow &flow,
const SupportParameters& support_params,
bool with_sheath, bool with_sheath,
bool no_sort) bool no_sort)
{ {
if (polygons.empty()) if (polygons.empty())
return; return;
if (! with_sheath) { if (with_sheath) {
if (density == 0) {
tree_supports_generate_paths(dst, polygons, flow, support_params);
return;
}
}
else {
fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow);
return; return;
} }
@ -819,8 +837,8 @@ struct SupportGeneratorLayerExtruded
return layer == nullptr || layer->polygons.empty(); return layer == nullptr || layer->polygons.empty();
} }
void set_polygons_to_extrude(Polygons &&polygons) { void set_polygons_to_extrude(Polygons &&polygons) {
if (m_polygons_to_extrude == nullptr) if (m_polygons_to_extrude == nullptr)
m_polygons_to_extrude = std::make_unique<Polygons>(std::move(polygons)); m_polygons_to_extrude = std::make_unique<Polygons>(std::move(polygons));
else else
*m_polygons_to_extrude = std::move(polygons); *m_polygons_to_extrude = std::move(polygons);
@ -829,9 +847,9 @@ struct SupportGeneratorLayerExtruded
const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; } const Polygons& polygons_to_extrude() const { return (m_polygons_to_extrude == nullptr) ? layer->polygons : *m_polygons_to_extrude; }
bool could_merge(const SupportGeneratorLayerExtruded &other) const { bool could_merge(const SupportGeneratorLayerExtruded &other) const {
return ! this->empty() && ! other.empty() && return ! this->empty() && ! other.empty() &&
std::abs(this->layer->height - other.layer->height) < EPSILON && std::abs(this->layer->height - other.layer->height) < EPSILON &&
this->layer->bridging == other.layer->bridging; this->layer->bridging == other.layer->bridging;
} }
// Merge regions, perform boolean union over the merged polygons. // Merge regions, perform boolean union over the merged polygons.
@ -937,7 +955,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact
const Point* operator()(const Point &pt) const { return &pt; } const Point* operator()(const Point &pt) const { return &pt; }
}; };
typedef ClosestPointInRadiusLookup<Point, PointAccessor> ClosestPointLookupType; typedef ClosestPointInRadiusLookup<Point, PointAccessor> ClosestPointLookupType;
Polygons loops0; Polygons loops0;
{ {
// find centerline of the external loop of the contours // find centerline of the external loop of the contours
@ -1034,7 +1052,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact
for (int i = 1; i < n_contact_loops; ++ i) for (int i = 1; i < n_contact_loops; ++ i)
polygons_append(loop_polygons, polygons_append(loop_polygons,
opening( opening(
loops0, loops0,
i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(), i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(),
0.5f * flow.scaled_spacing())); 0.5f * flow.scaled_spacing()));
// Clip such loops to the side oriented towards the object. // Clip such loops to the side oriented towards the object.
@ -1094,7 +1112,7 @@ void LoopInterfaceProcessor::generate(SupportGeneratorLayerExtruded &top_contact
// Remove empty lines. // Remove empty lines.
remove_degenerate(loop_lines); remove_degenerate(loop_lines);
} }
// add the contact infill area to the interface area // add the contact infill area to the interface area
// note that growing loops by $circle_radius ensures no tiny // note that growing loops by $circle_radius ensures no tiny
// extrusions are left inside the circles; however it creates // extrusions are left inside the circles; however it creates
@ -1166,7 +1184,7 @@ static void modulate_extrusion_by_overlapping_layers(
// Split the extrusions by the overlapping layers, reduce their extrusion rate. // Split the extrusions by the overlapping layers, reduce their extrusion rate.
// The last path_fragment is from this_layer. // The last path_fragment is from this_layer.
std::vector<ExtrusionPathFragment> path_fragments( std::vector<ExtrusionPathFragment> path_fragments(
n_overlapping_layers + 1, n_overlapping_layers + 1,
ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height));
// Don't use it, it will be released. // Don't use it, it will be released.
extrusion_path_template = nullptr; extrusion_path_template = nullptr;
@ -1218,7 +1236,7 @@ static void modulate_extrusion_by_overlapping_layers(
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
// End points of the original paths. // End points of the original paths.
std::vector<std::pair<Point, Point>> path_ends; std::vector<std::pair<Point, Point>> path_ends;
// Collect the paths of this_layer. // Collect the paths of this_layer.
{ {
Polylines &polylines = path_fragments.back().polylines; Polylines &polylines = path_fragments.back().polylines;
@ -1399,6 +1417,10 @@ SupportGeneratorLayersPtr generate_support_layers(
append(layers_sorted, intermediate_layers); append(layers_sorted, intermediate_layers);
append(layers_sorted, interface_layers); append(layers_sorted, interface_layers);
append(layers_sorted, base_interface_layers); append(layers_sorted, base_interface_layers);
// remove dupliated layers
std::sort(layers_sorted.begin(), layers_sorted.end());
layers_sorted.erase(std::unique(layers_sorted.begin(), layers_sorted.end()), layers_sorted.end());
// Sort the layers lexicographically by a raising print_z and a decreasing height. // Sort the layers lexicographically by a raising print_z and a decreasing height.
std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
int layer_id = 0; int layer_id = 0;
@ -1435,7 +1457,7 @@ SupportGeneratorLayersPtr generate_support_layers(
height_min = std::min(height_min, layer.height); height_min = std::min(height_min, layer.height);
} }
if (! empty) { if (! empty) {
// Here the upper_layer and lower_layer pointers are left to null at the support layers, // Here the upper_layer and lower_layer pointers are left to null at the support layers,
// as they are never used. These pointers are candidates for removal. // as they are never used. These pointers are candidates for removal.
bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces;
size_t this_layer_id_interface = layer_id_interface; size_t this_layer_id_interface = layer_id_interface;
@ -1510,7 +1532,7 @@ void generate_support_toolpaths(
// Print the support base below the support columns, or the support base for the support columns plus the contacts. // Print the support base below the support columns, or the support base for the support columns plus the contacts.
if (support_layer_id > 0) { if (support_layer_id > 0) {
const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ?
raft_layer.polygons : raft_layer.polygons :
//FIXME misusing contact_polygons for support columns. //FIXME misusing contact_polygons for support columns.
((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons);
@ -1531,7 +1553,7 @@ void generate_support_toolpaths(
filler, float(support_params.support_density), filler, float(support_params.support_density),
// Extrusion parameters // Extrusion parameters
ExtrusionRole::erSupportMaterial, flow, ExtrusionRole::erSupportMaterial, flow,
support_params.with_sheath, false); support_params, support_params.with_sheath, false);
} }
if (! tree_polygons.empty()) if (! tree_polygons.empty())
tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params); tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow, support_params);
@ -1558,15 +1580,15 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
fill_expolygons_with_sheath_generate_paths( fill_expolygons_with_sheath_generate_paths(
// Destination // Destination
support_layer.support_fills.entities, support_layer.support_fills.entities,
// Regions to fill // Regions to fill
tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons), tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons),
// Filler and its parameters // Filler and its parameters
filler, density, filler, density,
// Extrusion parameters // Extrusion parameters
(support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, flow, (support_layer_id < slicing_params.base_raft_layers) ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, flow,
// sheath at first layer // sheath at first layer
support_layer_id == 0, support_layer_id == 0); support_params, support_layer_id == 0, support_layer_id == 0);
} }
}); });
@ -1610,12 +1632,12 @@ void generate_support_toolpaths(
// Pointer to the 1st layer interface filler. // Pointer to the 1st layer interface filler.
auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get(); auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get();
// Filler for the 1st layer interface, if different from filler_interface. // Filler for the 1st layer interface, if different from filler_interface.
auto filler_raft_contact_ptr = std::unique_ptr<Fill>(range.begin() == n_raft_layers && config.support_interface_top_layers.value == 0 ? auto filler_raft_contact_ptr = std::unique_ptr<Fill>(range.begin() == n_raft_layers && config.support_interface_top_layers.value == 0 ?
Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr); Fill::new_from_type(support_params.raft_interface_fill_pattern) : nullptr);
// Pointer to the 1st layer interface filler. // Pointer to the 1st layer interface filler.
auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get();
// Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer).
auto filler_base_interface = std::unique_ptr<Fill>(base_interface_layers.empty() ? nullptr : auto filler_base_interface = std::unique_ptr<Fill>(base_interface_layers.empty() ? nullptr :
Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase));
auto filler_support = std::unique_ptr<Fill>(Fill::new_from_type(support_params.base_fill_pattern)); auto filler_support = std::unique_ptr<Fill>(Fill::new_from_type(support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object); filler_interface->set_bounding_box(bbox_object);
@ -1630,7 +1652,7 @@ void generate_support_toolpaths(
{ {
SupportLayer &support_layer = *support_layers[support_layer_id]; SupportLayer &support_layer = *support_layers[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id];
const float support_interface_angle = config.support_style.value == smsGrid ? const float support_interface_angle = (support_params.support_style == smsGrid || config.support_interface_pattern == smipRectilinear) ?
support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()); support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id());
// Find polygons with the same print_z. // Find polygons with the same print_z.
@ -1678,7 +1700,7 @@ void generate_support_toolpaths(
// to trim other layers. // to trim other layers.
if (top_contact_layer.could_merge(interface_layer) && ! raft_layer) if (top_contact_layer.could_merge(interface_layer) && ! raft_layer)
top_contact_layer.merge(std::move(interface_layer)); top_contact_layer.merge(std::move(interface_layer));
} }
if ((config.support_interface_top_layers == 0 || config.support_interface_bottom_layers == 0) && support_params.can_merge_support_regions) { if ((config.support_interface_top_layers == 0 || config.support_interface_bottom_layers == 0) && support_params.can_merge_support_regions) {
if (base_layer.could_merge(bottom_contact_layer)) if (base_layer.could_merge(bottom_contact_layer))
base_layer.merge(std::move(bottom_contact_layer)); base_layer.merge(std::move(bottom_contact_layer));
@ -1712,14 +1734,14 @@ void generate_support_toolpaths(
auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); auto *filler = raft_contact ? filler_raft_contact : filler_interface.get();
auto interface_flow = layer_ex.layer->bridging ? auto interface_flow = layer_ex.layer->bridging ?
Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) : Flow::bridging_flow(layer_ex.layer->height, support_params.support_material_bottom_interface_flow.nozzle_diameter()) :
(raft_contact ? &support_params.raft_interface_flow : (raft_contact ? &support_params.raft_interface_flow :
interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow) interface_as_base ? &support_params.support_material_flow : &support_params.support_material_interface_flow)
->with_height(float(layer_ex.layer->height)); ->with_height(float(layer_ex.layer->height));
filler->angle = interface_as_base ? filler->angle = interface_as_base ?
// If zero interface layers are configured, use the same angle as for the base layers. // If zero interface layers are configured, use the same angle as for the base layers.
angles[support_layer_id % angles.size()] : angles[support_layer_id % angles.size()] :
// Use interface angle for the interface layers. // Use interface angle for the interface layers.
raft_contact ? raft_contact ?
support_params.raft_interface_angle(support_layer.interface_id()) : support_params.raft_interface_angle(support_layer.interface_id()) :
support_interface_angle; support_interface_angle;
double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density;
@ -1728,13 +1750,13 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
layer_ex.extrusions, layer_ex.extrusions,
// Regions to fill // Regions to fill
union_safety_offset_ex(layer_ex.polygons_to_extrude()), union_safety_offset_ex(layer_ex.polygons_to_extrude()),
// Filler and its parameters // Filler and its parameters
filler, float(density), filler, float(density),
// Extrusion parameters // Extrusion parameters
ExtrusionRole::erSupportMaterialInterface, interface_flow); interface_as_base ? ExtrusionRole::erSupportMaterial : ExtrusionRole::erSupportMaterialInterface, interface_flow);
} }
}; };
const bool top_interfaces = config.support_interface_top_layers.value != 0; const bool top_interfaces = config.support_interface_top_layers.value != 0;
@ -1755,7 +1777,7 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density));
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
base_interface_layer.extrusions, base_interface_layer.extrusions,
//base_layer_interface.extrusions, //base_layer_interface.extrusions,
// Regions to fill // Regions to fill
union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), union_safety_offset_ex(base_interface_layer.polygons_to_extrude()),
@ -1767,7 +1789,7 @@ void generate_support_toolpaths(
// Base support or flange. // Base support or flange.
if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) { if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) {
Fill *filler = filler_support.get(); Fill *filler = filler_support.get();
filler->angle = angles[support_layer_id % angles.size()]; filler->angle = angles[support_layer_id % angles.size()];
// We don't use $base_flow->spacing because we need a constant spacing // We don't use $base_flow->spacing because we need a constant spacing
// value that guarantees that all layers are correctly aligned. // value that guarantees that all layers are correctly aligned.
@ -1792,10 +1814,12 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
sheath = true; sheath = true;
no_sort = true; no_sort = true;
} else if (config.support_style == SupportMaterialStyle::smsOrganic || } else if (support_params.support_style == SupportMaterialStyle::smsTreeOrganic) {
// Orca: use organic as default // if the tree supports are too tall, use double wall to make it stronger
config.support_style == smsDefault) { SupportParameters support_params2 = support_params;
tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params); if (support_layer.print_z > 100.0)
support_params2.tree_branch_diameter_double_wall_area_scaled = 0.1;
tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow, support_params2);
done = true; done = true;
} }
if (! done) if (! done)
@ -1808,7 +1832,7 @@ void generate_support_toolpaths(
filler, density, filler, density,
// Extrusion parameters // Extrusion parameters
ExtrusionRole::erSupportMaterial, flow, ExtrusionRole::erSupportMaterial, flow,
sheath, no_sort); support_params, sheath, no_sort);
} }
// Merge base_interface_layers to base_layers to avoid unneccessary retractions // Merge base_interface_layers to base_layers to avoid unneccessary retractions
@ -1912,7 +1936,7 @@ void PrintObjectSupportMaterial::clip_by_pillars(
coord_t pillar_size = scale_(PILLAR_SIZE); coord_t pillar_size = scale_(PILLAR_SIZE);
coord_t pillar_spacing = scale_(PILLAR_SPACING); coord_t pillar_spacing = scale_(PILLAR_SPACING);
// A regular grid of pillars, filling the 2D bounding box. // A regular grid of pillars, filling the 2D bounding box.
Polygons grid; Polygons grid;
{ {
@ -1922,7 +1946,7 @@ void PrintObjectSupportMaterial::clip_by_pillars(
pillar.points.push_back(Point(pillar_size, 0)); pillar.points.push_back(Point(pillar_size, 0));
pillar.points.push_back(Point(pillar_size, pillar_size)); pillar.points.push_back(Point(pillar_size, pillar_size));
pillar.points.push_back(Point(0, pillar_size)); pillar.points.push_back(Point(0, pillar_size));
// 2D bounding box of the projection of all contact polygons. // 2D bounding box of the projection of all contact polygons.
BoundingBox bbox; BoundingBox bbox;
for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it) for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
@ -1936,30 +1960,30 @@ void PrintObjectSupportMaterial::clip_by_pillars(
} }
} }
} }
// add pillars to every layer // add pillars to every layer
for my $i (0..n_support_z) { for my $i (0..n_support_z) {
$shape->[$i] = [ @$grid ]; $shape->[$i] = [ @$grid ];
} }
// build capitals // build capitals
for my $i (0..n_support_z) { for my $i (0..n_support_z) {
my $z = $support_z->[$i]; my $z = $support_z->[$i];
my $capitals = intersection( my $capitals = intersection(
$grid, $grid,
$contact->{$z} // [], $contact->{$z} // [],
); );
// work on one pillar at time (if any) to prevent the capitals from being merged // work on one pillar at time (if any) to prevent the capitals from being merged
// but store the contact area supported by the capital because we need to make // but store the contact area supported by the capital because we need to make
// sure nothing is left // sure nothing is left
my $contact_supported_by_capitals = []; my $contact_supported_by_capitals = [];
foreach my $capital (@$capitals) { foreach my $capital (@$capitals) {
// enlarge capital tops // enlarge capital tops
$capital = offset([$capital], +($pillar_spacing - $pillar_size)/2); $capital = offset([$capital], +($pillar_spacing - $pillar_size)/2);
push @$contact_supported_by_capitals, @$capital; push @$contact_supported_by_capitals, @$capital;
for (my $j = $i-1; $j >= 0; $j--) { for (my $j = $i-1; $j >= 0; $j--) {
my $jz = $support_z->[$j]; my $jz = $support_z->[$j];
$capital = offset($capital, -$self->interface_flow->scaled_width/2); $capital = offset($capital, -$self->interface_flow->scaled_width/2);
@ -1967,7 +1991,7 @@ void PrintObjectSupportMaterial::clip_by_pillars(
push @{ $shape->[$j] }, @$capital; push @{ $shape->[$j] }, @$capital;
} }
} }
// Capitals will not generally cover the whole contact area because there will be // Capitals will not generally cover the whole contact area because there will be
// remainders. For now we handle this situation by projecting such unsupported // remainders. For now we handle this situation by projecting such unsupported
// areas to the ground, just like we would do with a normal support. // areas to the ground, just like we would do with a normal support.
@ -1985,10 +2009,10 @@ void PrintObjectSupportMaterial::clip_by_pillars(
sub clip_with_shape { sub clip_with_shape {
my ($self, $support, $shape) = @_; my ($self, $support, $shape) = @_;
foreach my $i (keys %$support) { foreach my $i (keys %$support) {
// don't clip bottom layer with shape so that we // don't clip bottom layer with shape so that we
// can generate a continuous base flange // can generate a continuous base flange
// also don't clip raft layers // also don't clip raft layers
next if $i == 0; next if $i == 0;
next if $i < $self->object_config->raft_layers; next if $i < $self->object_config->raft_layers;

View file

@ -50,6 +50,11 @@ SupportGeneratorLayersPtr generate_raft_base(
const SupportGeneratorLayersPtr &base_layers, const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage); SupportGeneratorLayerStorage &layer_storage);
void tree_supports_generate_paths(ExtrusionEntitiesPtr &dst, const Polygons &polygons, const Flow &flow, const SupportParameters &support_params);
void fill_expolygons_with_sheath_generate_paths(
ExtrusionEntitiesPtr &dst, const Polygons &polygons, Fill *filler, float density, ExtrusionRole role, const Flow &flow, const SupportParameters& support_params, bool with_sheath, bool no_sort);
// returns sorted layers // returns sorted layers
SupportGeneratorLayersPtr generate_support_layers( SupportGeneratorLayersPtr generate_support_layers(
PrintObject &object, PrintObject &object,

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
#include "Slicing.hpp" #include "Slicing.hpp"
#include "Fill/FillBase.hpp" #include "Fill/FillBase.hpp"
#include "SupportLayer.hpp" #include "SupportLayer.hpp"
#include "SupportParameters.hpp"
namespace Slic3r { namespace Slic3r {
class PrintObject; class PrintObject;
@ -18,35 +19,6 @@ class PrintObjectConfig;
// the parameters of the raft to determine the 1st layer height and thickness. // the parameters of the raft to determine the 1st layer height and thickness.
class PrintObjectSupportMaterial class PrintObjectSupportMaterial
{ {
public:
struct SupportParams {
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
};
public: public:
PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params); PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params);
@ -97,25 +69,7 @@ private:
SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayersPtr &intermediate_layers,
const std::vector<Polygons> &layer_support_areas) const; const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
SupportGeneratorLayersPtr generate_raft_base(
const PrintObject &object,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers,
const SupportGeneratorLayersPtr &base_layers,
SupportGeneratorLayerStorage &layer_storage) const;
// Turn some of the base layers into base interface layers.
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
// extruder to improve adhesion of the soluble filament to the base.
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage) const;
// Trim support layers by an object to leave a defined gap between // Trim support layers by an object to leave a defined gap between
// the support volume and the object. // the support volume and the object.
@ -131,16 +85,6 @@ private:
void clip_with_shape(); void clip_with_shape();
*/ */
// Produce the actual G-code.
void generate_toolpaths(
SupportLayerPtrs &support_layers,
const SupportGeneratorLayersPtr &raft_layers,
const SupportGeneratorLayersPtr &bottom_contacts,
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &intermediate_layers,
const SupportGeneratorLayersPtr &interface_layers,
const SupportGeneratorLayersPtr &base_interface_layers) const;
// Following objects are not owned by SupportMaterial class. // Following objects are not owned by SupportMaterial class.
const PrintObject *m_object; const PrintObject *m_object;
const PrintConfig *m_print_config; const PrintConfig *m_print_config;
@ -149,7 +93,7 @@ private:
// carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc. // carrying information on a raft, 1st layer height, 1st object layer height, gap between the raft and object etc.
SlicingParameters m_slicing_params; SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions. // Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params; SupportParameters m_support_params;
}; };
} // namespace Slic3r } // namespace Slic3r

View file

@ -1,53 +1,53 @@
#ifndef slic3r_SupportParameters_hpp_ #ifndef slic3r_SupportParameters_hpp_
#define slic3r_SupportParameters_hpp_ #define slic3r_SupportParameters_hpp_
#include <boost/log/trivial.hpp>
#include "../libslic3r.h" #include "../libslic3r.h"
#include "../Flow.hpp" #include "../Flow.hpp"
namespace Slic3r { namespace Slic3r {
class PrintObject;
enum InfillPattern : int;
struct SupportParameters { struct SupportParameters {
SupportParameters(const PrintObject &object) SupportParameters() = delete;
SupportParameters(const PrintObject& object)
{ {
const PrintConfig &print_config = object.print()->config(); const PrintConfig& print_config = object.print()->config();
const PrintObjectConfig &object_config = object.config(); const PrintObjectConfig& object_config = object.config();
const SlicingParameters &slicing_params = object.slicing_parameters(); const SlicingParameters& slicing_params = object.slicing_parameters();
this->soluble_interface = slicing_params.soluble_interface; this->soluble_interface = slicing_params.soluble_interface;
this->soluble_interface_non_soluble_base = this->soluble_interface_non_soluble_base =
// Zero z-gap between the overhangs and the support interface. // Zero z-gap between the overhangs and the support interface.
slicing_params.soluble_interface && slicing_params.soluble_interface &&
// Interface extruder soluble. // Interface extruder soluble.
object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) && object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble. // Base extruder: Either "print with active extruder" not soluble.
(object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1)); (object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1));
{ {
int num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value); this->num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value);
int num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ? this->num_bottom_interface_layers = object_config.support_interface_bottom_layers < 0 ?
num_top_interface_layers : object_config.support_interface_bottom_layers; num_top_interface_layers : object_config.support_interface_bottom_layers;
this->has_top_contacts = num_top_interface_layers > 0; this->has_top_contacts = num_top_interface_layers > 0;
this->has_bottom_contacts = num_bottom_interface_layers > 0; this->has_bottom_contacts = num_bottom_interface_layers > 0;
this->num_top_interface_layers = this->has_top_contacts ? size_t(num_top_interface_layers - 1) : 0; if (this->soluble_interface_non_soluble_base) {
this->num_bottom_interface_layers = this->has_bottom_contacts ? size_t(num_bottom_interface_layers - 1) : 0; // Try to support soluble dense interfaces with non-soluble dense interfaces.
if (this->soluble_interface_non_soluble_base) { this->num_top_base_interface_layers = size_t(std::min(int(num_top_interface_layers) / 2, 2));
// Try to support soluble dense interfaces with non-soluble dense interfaces. this->num_bottom_base_interface_layers = size_t(std::min(int(num_bottom_interface_layers) / 2, 2));
this->num_top_base_interface_layers = size_t(std::min(num_top_interface_layers / 2, 2)); } else {
this->num_bottom_base_interface_layers = size_t(std::min(num_bottom_interface_layers / 2, 2)); // BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion
} else { // Note: support materials (such as Supp.W) can't be used as support base now, so support interface and base are still using different filaments even if
this->num_top_base_interface_layers = 0; // support_filament==0
this->num_bottom_base_interface_layers = 0; bool differnt_support_interface_filament = object_config.support_interface_filament != 0 &&
} object_config.support_interface_filament != object_config.support_filament;
} this->num_top_base_interface_layers = differnt_support_interface_filament ? 1 : 0;
this->num_bottom_base_interface_layers = differnt_support_interface_filament ? 1 : 0;
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); }
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); }
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height)); this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
this->raft_interface_flow = support_material_interface_flow; this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
this->raft_interface_flow = support_material_interface_flow;
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um. // Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
this->support_layer_height_min = scaled<coord_t>(0.01); this->support_layer_height_min = scaled<coord_t>(0.01);
for (auto lh : print_config.min_layer_height.values) for (auto lh : print_config.min_layer_height.values)
@ -69,10 +69,11 @@ struct SupportParameters {
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width())); external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(object, frExternalPerimeter, slicing_params.layer_height).width()));
bridge_flow_ratio += region.config().bridge_flow; bridge_flow_ratio += region.config().bridge_flow;
} }
this->gap_xy = object_config.support_object_xy_distance;//.get_abs_value(external_perimeter_width); this->gap_xy = object_config.support_object_xy_distance.value;
this->gap_xy_first_layer = object_config.support_object_first_layer_gap.value;
bridge_flow_ratio /= object.num_printing_regions(); bridge_flow_ratio /= object.num_printing_regions();
this->support_material_bottom_interface_flow = slicing_params.soluble_interface || ! object_config.thick_bridges ? this->support_material_bottom_interface_flow = slicing_params.soluble_interface || !object_config.thick_bridges ?
this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter()); Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter());
@ -85,33 +86,40 @@ struct SupportParameters {
// Object is printed with the same extruder as the support. // Object is printed with the same extruder as the support.
this->can_merge_support_regions = true; this->can_merge_support_regions = true;
} }
double interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / interface_spacing); this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->interface_spacing);
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing(); double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing); this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
double support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing(); this->support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
this->support_density = std::min(1., this->support_material_flow.spacing() / support_spacing); this->support_density = std::min(1., this->support_material_flow.spacing() / this->support_spacing);
if (object_config.support_interface_top_layers.value == 0) { if (object_config.support_interface_top_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern. // No interface layers allowed, print everything with the base support pattern.
this->interface_spacing = this->support_spacing;
this->interface_density = this->support_density; this->interface_density = this->support_density;
} }
SupportMaterialPattern support_pattern = object_config.support_base_pattern; SupportMaterialPattern support_pattern = object_config.support_base_pattern;
this->with_sheath = false;//object_config.support_material_with_sheath; this->with_sheath = object_config.tree_support_wall_count > 0;
this->base_fill_pattern = this->base_fill_pattern =
support_pattern == smpHoneycomb ? ipHoneycomb : support_pattern == smpHoneycomb ? ipHoneycomb :
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase; this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
this->contact_fill_pattern = if (object_config.support_interface_pattern == smipGrid)
this->contact_fill_pattern = ipGrid;
else if (object_config.support_interface_pattern == smipRectilinearInterlaced)
this->contact_fill_pattern = ipRectilinear;
else
this->contact_fill_pattern =
(object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) || (object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) ||
object_config.support_interface_pattern == smipConcentric ? object_config.support_interface_pattern == smipConcentric ?
ipConcentric : ipConcentric :
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value));
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
this->raft_angle_1st_layer = 0.f; this->raft_angle_1st_layer = 0.f;
this->raft_angle_base = 0.f; this->raft_angle_base = 0.f;
this->raft_angle_interface = 0.f; this->raft_angle_interface = 0.f;
@ -142,11 +150,36 @@ struct SupportParameters {
assert(slicing_params.interface_raft_layers == 0); assert(slicing_params.interface_raft_layers == 0);
assert(slicing_params.raft_layers() == 0); assert(slicing_params.raft_layers() == 0);
} }
this->tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
}
// Both top / bottom contacts and interfaces are soluble. const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1);
const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter);
support_extrusion_width = object_config.support_line_width.get_abs_value(nozzle_diameter);
support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width;
independent_layer_height = print_config.independent_support_layer_height;
// force double walls everywhere if wall count is larger than 1
tree_branch_diameter_double_wall_area_scaled = object_config.tree_support_wall_count.value > 1 ? 0.1 :
object_config.tree_support_wall_count.value == 0 ? 0.25 * sqr(scaled<double>(5.0)) * M_PI :
std::numeric_limits<double>::max();
support_style = object_config.support_style;
if (support_style != smsDefault) {
if ((support_style == smsSnug || support_style == smsGrid) && is_tree(object_config.support_type)) support_style = smsDefault;
if ((support_style == smsTreeSlim || support_style == smsTreeStrong || support_style == smsTreeHybrid || support_style == smsTreeOrganic) &&
!is_tree(object_config.support_type))
support_style = smsDefault;
}
if (support_style == smsDefault) {
if (is_tree(object_config.support_type)) {
// Orca: use organic as default
support_style = smsTreeOrganic;
} else {
support_style = smsGrid;
}
}
}
// Both top / bottom contacts and interfaces are soluble.
bool soluble_interface; bool soluble_interface;
// Support contact & interface are soluble, but support base is non-soluble. // Support contact & interface are soluble, but support base is non-soluble.
bool soluble_interface_non_soluble_base; bool soluble_interface_non_soluble_base;
@ -181,23 +214,28 @@ struct SupportParameters {
Flow support_material_bottom_interface_flow; Flow support_material_bottom_interface_flow;
// Flow at raft inteface & contact layers. // Flow at raft inteface & contact layers.
Flow raft_interface_flow; Flow raft_interface_flow;
coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder? // Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions; bool can_merge_support_regions;
coordf_t support_layer_height_min; coordf_t support_layer_height_min;
// coordf_t support_layer_height_max; // coordf_t support_layer_height_max;
coordf_t gap_xy; coordf_t gap_xy;
coordf_t gap_xy_first_layer;
float base_angle; float base_angle;
float interface_angle; float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion=0;
// Density of the top / bottom interface and contact layers. // Density of the top / bottom interface and contact layers.
coordf_t interface_density; coordf_t interface_density;
// Density of the raft interface and contact layers. // Density of the raft interface and contact layers.
coordf_t raft_interface_density; coordf_t raft_interface_density;
coordf_t support_spacing;
// Density of the base support layers. // Density of the base support layers.
coordf_t support_density; coordf_t support_density;
SupportMaterialStyle support_style = smsDefault;
// Pattern of the sparse infill including sparse raft layers. // Pattern of the sparse infill including sparse raft layers.
InfillPattern base_fill_pattern; InfillPattern base_fill_pattern;
@ -210,7 +248,7 @@ struct SupportParameters {
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
bool with_sheath; bool with_sheath;
// Branches of organic supports with area larger than this threshold will be extruded with double lines. // Branches of organic supports with area larger than this threshold will be extruded with double lines.
double tree_branch_diameter_double_wall_area_scaled; double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(5.0)) * M_PI;;
float raft_angle_1st_layer; float raft_angle_1st_layer;
float raft_angle_base; float raft_angle_base;
@ -219,6 +257,9 @@ struct SupportParameters {
// Produce a raft interface angle for a given SupportLayer::interface_id() // Produce a raft interface angle for a given SupportLayer::interface_id()
float raft_interface_angle(size_t interface_id) const float raft_interface_angle(size_t interface_id) const
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
bool independent_layer_height = false;
const double thresh_big_overhang = Slic3r::sqr(scale_(10));
}; };
} // namespace Slic3r } // namespace Slic3r

View file

@ -33,7 +33,7 @@ using namespace std::literals;
// or warning // or warning
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() // had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL()
#define error_level_not_in_cache error #define error_level_not_in_cache debug
//FIXME Machine border is currently ignored. //FIXME Machine border is currently ignored.
static Polygons calculateMachineBorderCollision(Polygon machine_border) static Polygons calculateMachineBorderCollision(Polygon machine_border)

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,8 @@
#include "Flow.hpp" #include "Flow.hpp"
#include "PrintConfig.hpp" #include "PrintConfig.hpp"
#include "Fill/Lightning/Generator.hpp" #include "Fill/Lightning/Generator.hpp"
#include "TreeModelVolumes.hpp"
#include "TreeSupport3D.hpp"
#ifndef SQ #ifndef SQ
#define SQ(x) ((x)*(x)) #define SQ(x) ((x)*(x))
@ -26,17 +28,158 @@ struct LayerHeightData
{ {
coordf_t print_z = 0; coordf_t print_z = 0;
coordf_t height = 0; coordf_t height = 0;
size_t next_layer_nr = 0; size_t obj_layer_nr = 0;
LayerHeightData() = default; LayerHeightData() = default;
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {} LayerHeightData(coordf_t z, coordf_t h, size_t obj_layer) : print_z(z), height(h), obj_layer_nr(obj_layer) {}
coordf_t bottom_z() {
return print_z - height;
}
}; };
struct TreeNode { enum TreeNodeType {
Vec3f pos; eCircle,
std::vector<int> children; // index of children in the storing vector eSquare,
std::vector<int> parents; // index of parents in the storing vector ePolygon
TreeNode(Point pt, float z) { };
pos = { float(unscale_(pt.x())),float(unscale_(pt.y())),z };
/*!
* \brief Represents the metadata of a node in the tree.
*/
struct SupportNode
{
static constexpr SupportNode* NO_PARENT = nullptr;
SupportNode()
: distance_to_top(0)
, position(Point(0, 0))
, obj_layer_nr(0)
, support_roof_layers_below(0)
, to_buildplate(true)
, parent(nullptr)
, print_z(0.0)
, height(0.0)
{}
// when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
SupportNode(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0)
: distance_to_top(distance_to_top)
, position(position)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, to_buildplate(to_buildplate)
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
, radius(radius_)
{
if (parent) {
parents.push_back(parent);
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top == 0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
if (radius == 0 && parent->radius>0)
radius = parent->radius + (dist_mm_to_top - parent->dist_mm_to_top) * diameter_angle_scale_factor;
parent->child = this;
for (auto& neighbor : parent->merged_neighbours) {
neighbor->child = this;
parents.push_back(neighbor);
}
is_sharp_tail = parent->is_sharp_tail;
skin_direction = parent->skin_direction;
}
}
#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
~SupportNode()
{
parent = nullptr;
merged_neighbours.clear();
}
#endif // DEBUG
/*!
* \brief The number of layers to go to the top of this branch.
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
// all nodes will have same diameter_angle_scale_factor because it's defined by user
static double diameter_angle_scale_factor;
/*!
* \brief The position of this node on the layer.
*/
Point position;
Point movement; // movement towards neighbor center or outline
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
TreeNodeType type = eCircle;
bool is_corner = false;
bool is_processed = false;
bool need_extra_wall = false;
bool is_sharp_tail = false;
bool valid = true;
ExPolygon overhang; // when type==ePolygon, set this value to get original overhang area
/*!
* \brief The direction of the skin lines above the tip of the branch.
*
* This determines in which direction we should reduce the width of the
* branch.
*/
Point skin_direction;
/*!
* \brief The number of support roof layers below this one.
*
* When a contact point is created, it is determined whether the mesh
* needs to be supported with support roof or not, since that is a
* per-mesh setting. This is stored in this variable in order to track
* how far we need to extend that support roof downwards.
*/
int support_roof_layers_below;
int obj_layer_nr;
/*!
* \brief Whether to try to go towards the build plate.
*
* If the node is inside the collision areas, it has no choice but to go
* towards the model. If it is not inside the collision areas, it must
* go towards the build plate to prevent a scar on the surface.
*/
bool to_buildplate;
/*!
* \brief The originating node for this one, one layer higher.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
SupportNode* parent;
std::vector<SupportNode*> parents;
SupportNode* child = nullptr;
/*!
* \brief All neighbours (on the same layer) that where merged into this node.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::list<SupportNode*> merged_neighbours;
coordf_t print_z;
coordf_t height;
bool operator==(const SupportNode& other) const
{
return position == other.position;
} }
}; };
@ -54,12 +197,12 @@ public:
* *
* \param xy_distance The required clearance between the model and the * \param xy_distance The required clearance between the model and the
* tree branches. * tree branches.
* \param max_move The maximum allowable movement between nodes on
* adjacent layers
* \param radius_sample_resolution Sample size used to round requested node radii. * \param radius_sample_resolution Sample size used to round requested node radii.
* \param collision_resolution
*/ */
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution); TreeSupportData(const PrintObject& object, coordf_t xy_distance, coordf_t radius_sample_resolution);
~TreeSupportData() {
clear_nodes();
}
TreeSupportData(TreeSupportData&&) = default; TreeSupportData(TreeSupportData&&) = default;
TreeSupportData& operator=(TreeSupportData&&) = default; TreeSupportData& operator=(TreeSupportData&&) = default;
@ -98,9 +241,13 @@ public:
Polygons get_contours(size_t layer_nr) const; Polygons get_contours(size_t layer_nr) const;
Polygons get_contours_with_holes(size_t layer_nr) const; Polygons get_contours_with_holes(size_t layer_nr) const;
SupportNode* create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0);
void clear_nodes();
std::vector<LayerHeightData> layer_heights; std::vector<LayerHeightData> layer_heights;
std::vector<TreeNode> tree_nodes; std::vector<std::unique_ptr<SupportNode>> contact_nodes;
// ExPolygon m_machine_border;
private: private:
/*! /*!
@ -110,7 +257,7 @@ private:
coordf_t radius; coordf_t radius;
size_t layer_nr; size_t layer_nr;
int recursions; int recursions;
}; };
struct RadiusLayerPairEquality { struct RadiusLayerPairEquality {
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const { constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
@ -146,6 +293,7 @@ private:
*/ */
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const; const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
tbb::spin_mutex m_mutex;
public: public:
bool is_slim = false; bool is_slim = false;
@ -154,11 +302,7 @@ public:
*/ */
coordf_t m_xy_distance; coordf_t m_xy_distance;
/*! double branch_scale_factor = 1.0; // tan(45 degrees)
* \brief The maximum distance that the centrepoint of a tree branch may
* move in consequtive layers
*/
coordf_t m_max_move;
/*! /*!
* \brief Sample resolution for radius values. * \brief Sample resolution for radius values.
@ -177,6 +321,8 @@ public:
// union contours of all layers below // union contours of all layers below
std::vector<ExPolygons> m_layer_outlines_below; std::vector<ExPolygons> m_layer_outlines_below;
std::vector<double> m_max_move_distances;
/*! /*!
* \brief Caches for the collision, avoidance and internal model polygons * \brief Caches for the collision, avoidance and internal model polygons
* at given radius and layer indices. * at given radius and layer indices.
@ -185,7 +331,7 @@ public:
* generally considered OK as the functions are still logically const * generally considered OK as the functions are still logically const
* (ie there is no difference in behaviour for the user betweeen * (ie there is no difference in behaviour for the user betweeen
* calculating the values each time vs caching the results). * calculating the values each time vs caching the results).
* *
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for. * coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
* So we change to tbb::concurrent_unordered_map * So we change to tbb::concurrent_unordered_map
*/ */
@ -215,6 +361,10 @@ public:
*/ */
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params); TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
void move_bounds_to_contact_nodes(std::vector<TreeSupport3D::SupportElements> &move_bounds,
PrintObject &print_object,
const TreeSupport3D::TreeSupportSettings &config);
/*! /*!
* \brief Create the areas that need support. * \brief Create the areas that need support.
* *
@ -224,180 +374,28 @@ public:
*/ */
void generate(); void generate();
void detect_overhangs(bool detect_first_sharp_tail_only=false); void detect_overhangs(bool check_support_necessity = false);
enum NodeType { SupportNode* create_node(const Point position,
eCircle, const int distance_to_top,
eSquare, const int obj_layer_nr,
ePolygon const int support_roof_layers_below,
}; const bool to_buildplate,
SupportNode* parent,
/*! coordf_t print_z_,
* \brief Represents the metadata of a node in the tree. coordf_t height_,
*/ coordf_t dist_mm_to_top_ = 0,
struct Node coordf_t radius_ = 0)
{ {
static constexpr Node* NO_PARENT = nullptr; return m_ts_data->create_node(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_);
}
Node()
: distance_to_top(0)
, position(Point(0, 0))
, obj_layer_nr(0)
, support_roof_layers_below(0)
, support_floor_layers_above(0)
, to_buildplate(true)
, parent(nullptr)
, print_z(0.0)
, height(0.0)
{}
// when dist_mm_to_top_==0, new node's dist_mm_to_top=parent->dist_mm_to_top + parent->height;
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
: distance_to_top(distance_to_top)
, position(position)
, obj_layer_nr(obj_layer_nr)
, support_roof_layers_below(support_roof_layers_below)
, support_floor_layers_above(0)
, to_buildplate(to_buildplate)
, parent(parent)
, print_z(print_z_)
, height(height_)
, dist_mm_to_top(dist_mm_to_top_)
{
if (parent) {
type = parent->type;
overhang = parent->overhang;
if (dist_mm_to_top==0)
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
parent->child = this;
for (auto& neighbor : parent->merged_neighbours)
neighbor->child = this;
}
}
#ifdef DEBUG // Clear the delete node's data so if there's invalid access after, we may get a clue by inspecting that node.
~Node()
{
parent = nullptr;
merged_neighbours.clear();
}
#endif // DEBUG
/*!
* \brief The number of layers to go to the top of this branch.
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
int distance_to_top;
coordf_t dist_mm_to_top = 0; // dist to bottom contact in mm
/*!
* \brief The position of this node on the layer.
*/
Point position;
Point movement; // movement towards neighbor center or outline
mutable double radius = 0.0;
mutable double max_move_dist = 0.0;
NodeType type = eCircle;
bool is_merged = false; // this node is generated by merging upper nodes
bool is_corner = false;
bool is_processed = false;
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
/*!
* \brief The direction of the skin lines above the tip of the branch.
*
* This determines in which direction we should reduce the width of the
* branch.
*/
bool skin_direction;
/*!
* \brief The number of support roof layers below this one.
*
* When a contact point is created, it is determined whether the mesh
* needs to be supported with support roof or not, since that is a
* per-mesh setting. This is stored in this variable in order to track
* how far we need to extend that support roof downwards.
*/
int support_roof_layers_below;
int support_floor_layers_above;
int obj_layer_nr;
/*!
* \brief Whether to try to go towards the build plate.
*
* If the node is inside the collision areas, it has no choice but to go
* towards the model. If it is not inside the collision areas, it must
* go towards the build plate to prevent a scar on the surface.
*/
bool to_buildplate;
/*!
* \brief The originating node for this one, one layer higher.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
Node *parent;
Node *child = nullptr;
/*!
* \brief All neighbours (on the same layer) that where merged into this node.
*
* In order to prune branches that can't have any support (because they
* can't be on the model and the path to the buildplate isn't clear),
* the entire branch needs to be known.
*/
std::list<Node*> merged_neighbours;
coordf_t print_z;
coordf_t height;
bool operator==(const Node& other) const
{
return position == other.position;
}
};
struct SupportParams
{
Flow first_layer_flow;
Flow support_material_flow;
Flow support_material_interface_flow;
Flow support_material_bottom_interface_flow;
coordf_t support_extrusion_width;
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
bool can_merge_support_regions;
coordf_t support_layer_height_min;
// coordf_t support_layer_height_max;
coordf_t gap_xy;
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
InfillPattern base_fill_pattern;
InfillPattern interface_fill_pattern;
InfillPattern contact_fill_pattern;
bool with_sheath;
const double thresh_big_overhang = SQ(scale_(10));
};
int avg_node_per_layer = 0; int avg_node_per_layer = 0;
float nodes_angle = 0; float nodes_angle = 0;
bool has_overhangs = false;
bool has_sharp_tails = false; bool has_sharp_tails = false;
bool has_cantilever = false; bool has_cantilever = false;
double max_cantilever_dist = 0; double max_cantilever_dist = 0;
SupportType support_type; SupportType support_type;
SupportMaterialStyle support_style;
std::unique_ptr<FillLightning::Generator> generator; std::unique_ptr<FillLightning::Generator> generator;
std::unordered_map<double, size_t> printZ_to_lightninglayer; std::unordered_map<double, size_t> printZ_to_lightninglayer;
@ -410,6 +408,10 @@ public:
*/ */
ExPolygon m_machine_border; ExPolygon m_machine_border;
enum OverhangType { Detected = 0, Enforced, SharpTail };
std::map<const ExPolygon*, OverhangType> overhang_types;
std::vector<std::pair<Vec3f, Vec3f>> m_vertical_enforcer_points;
private: private:
/*! /*!
* \brief Generator for model collision, avoidance and internal guide volumes * \brief Generator for model collision, avoidance and internal guide volumes
@ -417,20 +419,29 @@ private:
* Lazily computes volumes as needed. * Lazily computes volumes as needed.
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks * \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
*/ */
std::vector<std::vector<SupportNode*>> contact_nodes;
std::shared_ptr<TreeSupportData> m_ts_data; std::shared_ptr<TreeSupportData> m_ts_data;
std::unique_ptr<TreeSupport3D::TreeModelVolumes> m_model_volumes;
PrintObject *m_object; PrintObject *m_object;
const PrintObjectConfig *m_object_config; const PrintObjectConfig* m_object_config;
SlicingParameters m_slicing_params; SlicingParameters m_slicing_params;
// Various precomputed support parameters to be shared with external functions. // Various precomputed support parameters to be shared with external functions.
SupportParams m_support_params; SupportParameters m_support_params;
size_t m_raft_layers = 0; size_t m_raft_layers = 0; // number of raft layers, including raft base, raft interface, raft gap
size_t m_highest_overhang_layer = 0; size_t m_highest_overhang_layer = 0;
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees; std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches; std::vector< std::unordered_map<Line, bool, LineHash>> m_mst_line_x_layer_contour_caches;
coordf_t MAX_BRANCH_RADIUS = 10.0; float DO_NOT_MOVER_UNDER_MM = 0.0;
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0; coordf_t base_radius = 0.0;
coordf_t MIN_BRANCH_RADIUS = 0.5; const coordf_t MAX_BRANCH_RADIUS = 10.0;
float tree_support_branch_diameter_angle = 5.0; const coordf_t MIN_BRANCH_RADIUS = 0.4;
const coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
const coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0;
double diameter_angle_scale_factor = tan(5.0*M_PI/180.0);
// minimum roof area (1 mm^2), area smaller than this value will not have interface
const double minimum_roof_area{SQ(scaled<double>(1.))};
float top_z_distance = 0.0;
bool is_strong = false; bool is_strong = false;
bool is_slim = false; bool is_slim = false;
bool with_infill = false; bool with_infill = false;
@ -447,7 +458,7 @@ private:
* save the resulting support polygons to. * save the resulting support polygons to.
* \param contact_nodes The nodes to draw as support. * \param contact_nodes The nodes to draw as support.
*/ */
void draw_circles(const std::vector<std::vector<Node*>>& contact_nodes); void draw_circles();
/*! /*!
* \brief Drops down the nodes of the tree support towards the build plate. * \brief Drops down the nodes of the tree support towards the build plate.
@ -461,18 +472,16 @@ private:
* dropped down. The nodes are dropped to lower layers inside the same * dropped down. The nodes are dropped to lower layers inside the same
* vector of layers. * vector of layers.
*/ */
void drop_nodes(std::vector<std::vector<Node *>> &contact_nodes); void drop_nodes();
void smooth_nodes(std::vector<std::vector<Node *>> &contact_nodes); void smooth_nodes();
void adjust_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
/*! BBS: MusangKing: maximum layer height /*! BBS: MusangKing: maximum layer height
* \brief Optimize the generation of tree support by pre-planning the layer_heights * \brief Optimize the generation of tree support by pre-planning the layer_heights
* *
*/ */
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes); std::vector<LayerHeightData> plan_layer_heights();
/*! /*!
* \brief Creates points where support contacts the model. * \brief Creates points where support contacts the model.
* *
@ -486,20 +495,27 @@ private:
* \return For each layer, a list of points where the tree should connect * \return For each layer, a list of points where the tree should connect
* with the model. * with the model.
*/ */
void generate_contact_points(std::vector<std::vector<Node*>>& contact_nodes); void generate_contact_points();
/*! /*!
* \brief Add a node to the next layer. * \brief Add a node to the next layer.
* *
* If a node is already at that position in the layer, the nodes are merged. * If a node is already at that position in the layer, the nodes are merged.
*/ */
void insert_dropped_node(std::vector<Node*>& nodes_layer, Node* node); void insert_dropped_node(std::vector<SupportNode*>& nodes_layer, SupportNode* node);
void create_tree_support_layers(); void create_tree_support_layers();
void generate_toolpaths(); void generate_toolpaths();
Polygons spanning_tree_to_polygon(const std::vector<MinimumSpanningTree>& spanning_trees, Polygons layer_contours, int layer_nr); // get unscaled radius of node
Polygons contact_nodes_to_polygon(const std::vector<Node*>& contact_nodes, Polygons layer_contours, int layer_nr, std::vector<double>& radiis, std::vector<bool>& is_interface);
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor); coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor); // get unscaled radius(mm) of node based on the distance mm to top
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true);
coordf_t calc_radius(coordf_t mm_to_top);
coordf_t get_radius(const SupportNode* node);
ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr);
// layer's expolygon expanded by radius+m_xy_distance
ExPolygons get_collision(coordf_t radius, size_t layer_nr);
// get Polygons instead of ExPolygons
Polygons get_collision_polys(coordf_t radius, size_t layer_nr);
// similar to SupportMaterial::trim_support_layers_by_object // similar to SupportMaterial::trim_support_layers_by_object
Polygons get_trim_support_regions( Polygons get_trim_support_regions(

File diff suppressed because it is too large Load diff

View file

@ -47,8 +47,6 @@ struct SlicingParameters;
namespace TreeSupport3D namespace TreeSupport3D
{ {
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
struct AreaIncreaseSettings struct AreaIncreaseSettings
{ {
@ -139,6 +137,10 @@ struct SupportElementStateBits {
struct SupportElementState : public SupportElementStateBits struct SupportElementState : public SupportElementStateBits
{ {
int type = 0;
coordf_t radius = 0;
float print_z = 0;
/*! /*!
* \brief The layer this support elements wants reach * \brief The layer this support elements wants reach
*/ */
@ -306,7 +308,7 @@ void organic_draw_branches(
SupportGeneratorLayersPtr &intermediate_layers, SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage, SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel); std::function<void()> throw_on_cancel);
} // namespace TreeSupport3D } // namespace TreeSupport3D

View file

@ -17,10 +17,10 @@
namespace Slic3r namespace Slic3r
{ {
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
namespace TreeSupport3D namespace TreeSupport3D
{ {
using LayerIndex = int; using LayerIndex = int;
enum class InterfacePreference enum class InterfacePreference
@ -40,18 +40,18 @@ struct TreeSupportMeshGroupSettings {
const PrintObjectConfig &config = print_object.config(); const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters(); const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders(); // const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style. // Support must be enabled and set to Tree style.
assert(config.enable_support || config.enforce_support_layers > 0); //assert(config.support_material);
assert(is_tree(config.support_type)); //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. // Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.; coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) { for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id); const PrintRegion &region = 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()); 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->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.resolution.value); this->resolution = scaled<coord_t>(print_config.resolution.value);
// Arache feature // Arache feature
@ -69,56 +69,28 @@ struct TreeSupportMeshGroupSettings {
0; 0;
this->support_material_buildplate_only = config.support_on_build_plate_only; this->support_material_buildplate_only = config.support_on_build_plate_only;
this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value); this->support_xy_distance = scaled<coord_t>(config.support_object_xy_distance.value);
this->support_xy_distance_1st_layer = scaled<coord_t>(config.support_object_first_layer_gap.value);
// Separation of interfaces, it is likely smaller than support_xy_distance. // 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_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_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support); 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_interface_top_layers.value > 0; this->support_roof_enable = config.support_interface_top_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_interface_top_layers.value : 0; this->support_roof_layers = config.support_interface_top_layers.value;
this->support_floor_enable = config.support_interface_top_layers.value > 0 && config.support_interface_bottom_layers.value > 0; this->support_floor_enable = config.support_interface_bottom_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_interface_bottom_layers.value : 0; this->support_floor_layers = config.support_interface_bottom_layers.value;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_interface_pattern; this->support_roof_pattern = config.support_interface_pattern;
this->support_pattern = config.support_base_pattern; this->support_pattern = config.support_base_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value); this->support_line_spacing = scaled<coord_t>(config.support_base_pattern_spacing.value);
// this->support_bottom_offset = this->support_wall_count = std::max(1, config.tree_support_wall_count.value); // at least 1 wall for organic tree support
// 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_interface_spacing.value) + this->support_roof_line_width; this->support_roof_line_distance = scaled<coord_t>(config.support_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.tree_support_branch_distance_organic.value); this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance_organic.value);
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle_organic * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON); this->support_tree_angle_slow = std::clamp<double>(config.tree_support_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value); this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter_organic.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.tree_support_top_rate.value; // percent this->support_tree_top_rate = config.tree_support_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width; // this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter); this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.tree_support_tip_diameter.value), (coord_t)0, this->support_tree_branch_diameter);
std::cout << "\n---------------\n"
<< "layer_height: " << layer_height << "\nresolution: " << resolution << "\nmin_feature_size: " << min_feature_size
<< "\nsupport_angle: " << support_angle << "\nconfig.support_threshold_angle: " << config.support_threshold_angle << "\nsupport_line_width: " << support_line_width
<< "\nsupport_roof_line_width: " << support_roof_line_width << "\nsupport_bottom_enable: " << support_bottom_enable
<< "\nsupport_bottom_height: " << support_bottom_height
<< "\nsupport_material_buildplate_only: " << support_material_buildplate_only
<< "\nsupport_xy_distance: " << support_xy_distance << "\nsupport_xy_distance_overhang: " << support_xy_distance_overhang
<< "\nsupport_top_distance: " << support_top_distance << "\nsupport_bottom_distance: " << support_bottom_distance
<< "\nsupport_roof_enable: " << support_roof_enable << "\nsupport_roof_layers: " << support_roof_layers
<< "\nsupport_floor_enable: " << support_floor_enable << "\nsupport_floor_layers: " << support_floor_layers
<< "\nsupport_roof_pattern: " << support_roof_pattern << "\nsupport_pattern: " << support_pattern
<< "\nsupport_line_spacing: " << support_line_spacing << "\nsupport_wall_count: " << support_wall_count
<< "\nsupport_roof_line_distance: " << support_roof_line_distance
<< "\nsupport_tree_branch_distance: " << support_tree_branch_distance
<< "\nsupport_tree_angle_slow: " << support_tree_angle_slow
<< "\nsupport_tree_branch_diameter: " << support_tree_branch_diameter
<< "\nsupport_tree_branch_diameter_angle: " << support_tree_branch_diameter_angle
<< "\nsupport_tree_top_rate: " << support_tree_top_rate << "\nsupport_tree_tip_diameter: " << support_tree_tip_diameter
<< "\n---------------\n";
} }
/*********************************************************************/ /*********************************************************************/
@ -158,6 +130,7 @@ struct TreeSupportMeshGroupSettings {
// Distance of the support structure from the print in the X/Y directions. // Distance of the support structure from the print in the X/Y directions.
// minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter // minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter
coord_t support_xy_distance { scaled<coord_t>(0.7) }; coord_t support_xy_distance { scaled<coord_t>(0.7) };
coord_t support_xy_distance_1st_layer { scaled<coord_t>(0.7) };
// Minimum Support X/Y Distance // Minimum Support X/Y Distance
// Distance of the support structure from the overhang in the X/Y directions. // Distance of the support structure from the overhang in the X/Y directions.
// minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance // minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance
@ -766,6 +739,17 @@ private:
std::mutex m_mutex_layer_storage; std::mutex m_mutex_layer_storage;
}; };
enum class LineStatus
{
INVALID,
TO_MODEL,
TO_MODEL_GRACIOUS,
TO_MODEL_GRACIOUS_SAFE,
TO_BP,
TO_BP_SAFE
};
} // namespace TreeSupport3D } // namespace TreeSupport3D
} // namespace Slic3r } // namespace Slic3r

View file

@ -957,7 +957,6 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
} }
slice_facet_with_slabs<true>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top); slice_facet_with_slabs<true>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top);
} }
// BBS: add vertical faces option
if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) { if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) {
Vec3i32 neighbors = face_neighbors[face_idx]; Vec3i32 neighbors = face_neighbors[face_idx];
// Reset neighborship of this triangle in case the other triangle is oriented backwards from this one. // Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
@ -2063,6 +2062,7 @@ void slice_mesh_slabs(
const Transform3d &trafo, const Transform3d &trafo,
std::vector<Polygons> *out_top, std::vector<Polygons> *out_top,
std::vector<Polygons> *out_bottom, std::vector<Polygons> *out_bottom,
std::vector<std::pair<Vec3f, Vec3f>> *vertical_points,
std::function<void()> throw_on_cancel) std::function<void()> throw_on_cancel)
{ {
BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons"; BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons";
@ -2133,6 +2133,11 @@ void slice_mesh_slabs(
// Is the triangle vertical or degenerate? // Is the triangle vertical or degenerate?
assert(d == 0); assert(d == 0);
fo = fa == fb || fa == fc || fb == fc ? FaceOrientation::Degenerate : FaceOrientation::Vertical; fo = fa == fb || fa == fc || fb == fc ? FaceOrientation::Degenerate : FaceOrientation::Vertical;
if(vertical_points && fo==FaceOrientation::Vertical)
{
Vec3f normal = (fb - fa).cross(fc - fa).normalized();
vertical_points->push_back({ (fa + fb + fc) / 3,normal });
}
} }
face_orientation[&tri - mesh.indices.data()] = fo; face_orientation[&tri - mesh.indices.data()] = fo;
} }
@ -2297,7 +2302,7 @@ void project_mesh(
{ {
std::vector<Polygons> top, bottom; std::vector<Polygons> top, bottom;
std::vector<float> zs { -1e10, 1e10 }; std::vector<float> zs { -1e10, 1e10 };
slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, throw_on_cancel); slice_mesh_slabs(mesh, zs, trafo, out_top ? &top : nullptr, out_bottom ? &bottom : nullptr, nullptr, throw_on_cancel);
if (out_top) if (out_top)
*out_top = std::move(top.front()); *out_top = std::move(top.front());
if (out_bottom) if (out_bottom)
@ -2311,7 +2316,7 @@ Polygons project_mesh(
{ {
std::vector<Polygons> top, bottom; std::vector<Polygons> top, bottom;
std::vector<float> zs { -1e10, 1e10 }; std::vector<float> zs { -1e10, 1e10 };
slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, throw_on_cancel); slice_mesh_slabs(mesh, zs, trafo, &top, &bottom, nullptr, throw_on_cancel);
return union_(top.front(), bottom.back()); return union_(top.front(), bottom.back());
} }

View file

@ -107,6 +107,7 @@ void slice_mesh_slabs(
const Transform3d &trafo, const Transform3d &trafo,
std::vector<Polygons> *out_top, std::vector<Polygons> *out_top,
std::vector<Polygons> *out_bottom, std::vector<Polygons> *out_bottom,
std::vector<std::pair<Vec3f, Vec3f>> *vertical_points,
std::function<void()> throw_on_cancel); std::function<void()> throw_on_cancel);
// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons. // Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.

View file

@ -6,6 +6,7 @@
#include <ctime> #include <ctime>
#include <cstdarg> #include <cstdarg>
#include <stdio.h> #include <stdio.h>
#include <filesystem>
#include "format.hpp" #include "format.hpp"
#include "Platform.hpp" #include "Platform.hpp"
@ -298,12 +299,13 @@ static std::atomic<bool> debug_out_path_called(false);
std::string debug_out_path(const char *name, ...) std::string debug_out_path(const char *name, ...)
{ {
static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/"; //static constexpr const char *SLIC3R_DEBUG_OUT_PATH_PREFIX = "out/";
auto svg_folder = boost::filesystem::path(g_data_dir) / "SVG/";
if (! debug_out_path_called.exchange(true)) { if (! debug_out_path_called.exchange(true)) {
std::string path = boost::filesystem::system_complete(SLIC3R_DEBUG_OUT_PATH_PREFIX).string(); if (!boost::filesystem::exists(svg_folder)) {
if (!boost::filesystem::exists(path)) { boost::filesystem::create_directory(svg_folder);
boost::filesystem::create_directory(path);
} }
std::string path = boost::filesystem::system_complete(svg_folder).string();
printf("Debugging output files will be written to %s\n", path.c_str()); printf("Debugging output files will be written to %s\n", path.c_str());
} }
char buffer[2048]; char buffer[2048];
@ -311,7 +313,13 @@ std::string debug_out_path(const char *name, ...)
va_start(args, name); va_start(args, name);
std::vsprintf(buffer, name, args); std::vsprintf(buffer, name, args);
va_end(args); va_end(args);
return std::string(SLIC3R_DEBUG_OUT_PATH_PREFIX) + std::string(buffer);
std::string buf(buffer);
if (size_t pos = buf.find_first_of('/'); pos != std::string::npos) {
std::string sub_dir = buf.substr(0, pos);
std::filesystem::create_directory(svg_folder.string() + sub_dir);
}
return svg_folder.string() + std::string(buffer);
} }
namespace logging = boost::log; namespace logging = boost::log;

View file

@ -426,7 +426,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
auto support_type = config->opt_enum<SupportType>("support_type"); auto support_type = config->opt_enum<SupportType>("support_type");
auto support_style = config->opt_enum<SupportMaterialStyle>("support_style"); auto support_style = config->opt_enum<SupportMaterialStyle>("support_style");
std::set<int> enum_set_normal = { smsDefault, smsGrid, smsSnug }; std::set<int> enum_set_normal = { smsDefault, smsGrid, smsSnug };
std::set<int> enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic }; std::set<int> enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic };
auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal; auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal;
if (set.find(support_style) == set.end()) { if (set.find(support_style) == set.end()) {
DynamicPrintConfig new_conf = *config; DynamicPrintConfig new_conf = *config;
@ -600,14 +600,14 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
"support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers", "support_interface_pattern", "support_interface_top_layers", "support_interface_bottom_layers",
"bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance", "bridge_no_support", "max_bridge_length", "support_top_z_distance", "support_bottom_z_distance",
"support_type", "support_on_build_plate_only", "support_critical_regions_only","support_interface_not_for_body", "support_type", "support_on_build_plate_only", "support_critical_regions_only","support_interface_not_for_body",
"support_object_xy_distance"/*, "independent_support_layer_height"*/}) "support_object_xy_distance","support_object_first_layer_gap"/*, "independent_support_layer_height"*/})
toggle_field(el, have_support_material); toggle_field(el, have_support_material);
toggle_field("support_threshold_angle", have_support_material && is_auto(support_type)); toggle_field("support_threshold_angle", have_support_material && is_auto(support_type));
toggle_field("support_threshold_overlap", config->opt_int("support_threshold_angle") == 0 && have_support_material && is_auto(support_type)); toggle_field("support_threshold_overlap", config->opt_int("support_threshold_angle") == 0 && have_support_material && is_auto(support_type));
//toggle_field("support_closing_radius", have_support_material && support_style == smsSnug); //toggle_field("support_closing_radius", have_support_material && support_style == smsSnug);
bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type); bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type);
bool support_is_normal_tree = support_is_tree && support_style != smsOrganic && bool support_is_normal_tree = support_is_tree && support_style != smsTreeOrganic &&
// Orca: use organic as default // Orca: use organic as default
support_style != smsDefault; support_style != smsDefault;
bool support_is_organic = support_is_tree && !support_is_normal_tree; bool support_is_organic = support_is_tree && !support_is_normal_tree;
@ -615,10 +615,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter" }) for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter" })
toggle_line(el, support_is_normal_tree); toggle_line(el, support_is_normal_tree);
// settings specific to normal trees // settings specific to normal trees
for (auto el : {"tree_support_wall_count", "tree_support_auto_brim", "tree_support_brim_width", "tree_support_adaptive_layer_height"}) for (auto el : {"tree_support_auto_brim", "tree_support_brim_width", "tree_support_adaptive_layer_height"})
toggle_line(el, support_is_normal_tree); toggle_line(el, support_is_normal_tree);
// settings specific to organic trees // settings specific to organic trees
for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle", "tree_support_branch_diameter_double_wall"}) for (auto el : {"tree_support_branch_angle_organic", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic","tree_support_angle_slow","tree_support_tip_diameter", "tree_support_top_rate", "tree_support_branch_diameter_angle"})
toggle_line(el, support_is_organic); toggle_line(el, support_is_organic);
toggle_field("tree_support_brim_width", support_is_tree && !config->opt_bool("tree_support_auto_brim")); toggle_field("tree_support_brim_width", support_is_tree && !config->opt_bool("tree_support_auto_brim"));

View file

@ -2125,7 +2125,8 @@ bool GUI_App::OnInit()
{ {
try { try {
return on_init_inner(); return on_init_inner();
} catch (const std::exception&) { } catch (const std::exception& e) {
BOOST_LOG_TRIVIAL(fatal) << "OnInit Got Fatal error: " << e.what();
generic_exception_handle(); generic_exception_handle();
return false; return false;
} }

View file

@ -90,10 +90,11 @@ std::map<std::string, std::vector<SimpleSettingData>> SettingsFactory::OBJECT_C
{ L("Support"), {{"brim_type", "",1},{"brim_width", "",2},{"brim_object_gap", "",3}, { L("Support"), {{"brim_type", "",1},{"brim_width", "",2},{"brim_object_gap", "",3},
{"enable_support", "",4},{"support_type", "",5},{"support_threshold_angle", "",6}, {"support_threshold_overlap", "",6}, {"support_on_build_plate_only", "",7}, {"enable_support", "",4},{"support_type", "",5},{"support_threshold_angle", "",6}, {"support_threshold_overlap", "",6}, {"support_on_build_plate_only", "",7},
{"support_filament", "",8},{"support_interface_filament", "",9},{"support_expansion", "",24},{"support_style", "",25}, {"support_filament", "",8},{"support_interface_filament", "",9},{"support_expansion", "",24},{"support_style", "",25},
{"tree_support_brim_width", "",26}, {"tree_support_branch_angle", "",10},{"tree_support_branch_angle_organic","",10}, {"tree_support_wall_count", "",11},//tree support {"tree_support_brim_width", "",26}, {"tree_support_branch_angle", "",10},{"tree_support_branch_angle_organic","",10}, {"tree_support_wall_count", "",11},{"tree_support_branch_diameter_angle", "",11},//tree support
{"support_top_z_distance", "",13},{"support_bottom_z_distance", "",12},{"support_base_pattern", "",14},{"support_base_pattern_spacing", "",15}, {"support_top_z_distance", "",13},{"support_bottom_z_distance", "",12},{"support_base_pattern", "",14},{"support_base_pattern_spacing", "",15},
{"support_interface_top_layers", "",16},{"support_interface_bottom_layers", "",17},{"support_interface_spacing", "",18},{"support_bottom_interface_spacing", "",19}, {"support_interface_top_layers", "",16},{"support_interface_bottom_layers", "",17},{"support_interface_spacing", "",18},{"support_bottom_interface_spacing", "",19},
{"support_object_xy_distance", "",20}, {"bridge_no_support", "",21},{"max_bridge_length", "",22},{"support_critical_regions_only", "",23},{"support_remove_small_overhang","",27} {"support_object_xy_distance", "",20}, {"bridge_no_support", "",21},{"max_bridge_length", "",22},{"support_critical_regions_only", "",23},{"support_remove_small_overhang","",27},
{"support_object_first_layer_gap","",28}
}}, }},
{ L("Speed"), {{"support_speed", "",12}, {"support_interface_speed", "",13} { L("Speed"), {{"support_speed", "",12}, {"support_interface_speed", "",13}
}} }}
@ -155,6 +156,7 @@ std::vector<SimpleSettingData> SettingsFactory::get_visible_options(const std::s
"tree_support_wall_count", "tree_support_wall_count",
//support //support
"support_top_z_distance", "support_base_pattern", "support_base_pattern_spacing", "support_interface_top_layers", "support_interface_bottom_layers", "support_interface_spacing", "support_bottom_interface_spacing", "support_object_xy_distance", "support_top_z_distance", "support_base_pattern", "support_base_pattern_spacing", "support_interface_top_layers", "support_interface_bottom_layers", "support_interface_spacing", "support_bottom_interface_spacing", "support_object_xy_distance",
"support_object_first_layer_gap",
//adhesion //adhesion
"brim_type", "brim_width", "brim_object_gap", "raft_layers" "brim_type", "brim_width", "brim_object_gap", "raft_layers"
};*/ };*/

View file

@ -1496,50 +1496,13 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
m_config_manipulation.apply(m_config, &new_conf); m_config_manipulation.apply(m_config, &new_conf);
} }
// BBS popup a message to ask the user to set optimum parameters for tree support
if (opt_key == "support_type" || opt_key == "support_style") {
if (is_tree_slim(m_config->opt_enum<SupportType>("support_type"), m_config->opt_enum<SupportMaterialStyle>("support_style")) &&
!(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_int("support_interface_top_layers") == 0 && m_config->opt_int("tree_support_wall_count") == 2)) {
wxString msg_text = _L("We have added an experimental style \"Tree Slim\" that features smaller support volume but weaker strength.\n"
"We recommend using it with: 0 interface layers, 0 top distance, 2 walls.");
msg_text += "\n\n" + _L("Change these settings automatically? \n"
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me");
MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config;
if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0));
new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(0));
new_conf.set_key_value("tree_support_wall_count", new ConfigOptionInt(2));
m_config_manipulation.apply(m_config, &new_conf);
}
wxGetApp().plater()->update();
} else if ((m_config->opt_enum<SupportType>("support_type")==stTreeAuto && (m_config->opt_enum<SupportMaterialStyle>("support_style")==smsTreeStrong || m_config->opt_enum<SupportMaterialStyle>("support_style") == smsTreeHybrid)) &&
!((m_config->opt_float("support_top_z_distance") >=0.1 || is_support_filament(m_config->opt_int("support_interface_filament") - 1))
&& m_config->opt_int("support_interface_top_layers") >1) ) {
wxString msg_text = _L("For \"Tree Strong\" and \"Tree Hybrid\" styles, we recommend the following settings: at least 2 interface layers, at least 0.1mm top z distance or using support materials on interface.");
msg_text += "\n\n" + _L("Change these settings automatically? \n"
"Yes - Change these settings automatically\n"
"No - Do not change these settings for me");
MessageDialog dialog(wxGetApp().plater(), msg_text, "Suggestion", wxICON_WARNING | wxYES | wxNO);
DynamicPrintConfig new_conf = *m_config;
if (dialog.ShowModal() == wxID_YES) {
if (!is_support_filament(m_config->opt_int("support_interface_filament") - 1) && m_config->opt_float("support_top_z_distance") < 0.1)
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0.2));
new_conf.set_key_value("support_interface_top_layers", new ConfigOptionInt(2));
m_config_manipulation.apply(m_config, &new_conf);
}
wxGetApp().plater()->update();
}
}
// BBS popup a message to ask the user to set optimum parameters for support interface if support materials are used // BBS popup a message to ask the user to set optimum parameters for support interface if support materials are used
if (opt_key == "support_interface_filament") { if (opt_key == "support_interface_filament") {
int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0 int interface_filament_id = m_config->opt_int("support_interface_filament") - 1; // the displayed id is based from 1, while internal id is based from 0
if (is_support_filament(interface_filament_id) && !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 && if (is_support_filament(interface_filament_id) && !(m_config->opt_float("support_top_z_distance") == 0 && m_config->opt_float("support_interface_spacing") == 0 &&
m_config->opt_enum<SupportMaterialInterfacePattern>("support_interface_pattern") == SupportMaterialInterfacePattern::smipConcentric)) { m_config->opt_enum<SupportMaterialInterfacePattern>("support_interface_pattern") == SupportMaterialInterfacePattern::smipRectilinearInterlaced)) {
wxString msg_text = _L("When using support material for the support interface, We recommend the following settings:\n" wxString msg_text = _L("When using support material for the support interface, We recommend the following settings:\n"
"0 top z distance, 0 interface spacing, concentric pattern and disable independent support layer height"); "0 top z distance, 0 interface spacing, interlaced rectilinear pattern and disable independent support layer height");
msg_text += "\n\n" + _L("Change these settings automatically? \n" msg_text += "\n\n" + _L("Change these settings automatically? \n"
"Yes - Change these settings automatically\n" "Yes - Change these settings automatically\n"
"No - Do not change these settings for me"); "No - Do not change these settings for me");
@ -1548,7 +1511,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
if (dialog.ShowModal() == wxID_YES) { if (dialog.ShowModal() == wxID_YES) {
new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0)); new_conf.set_key_value("support_top_z_distance", new ConfigOptionFloat(0));
new_conf.set_key_value("support_interface_spacing", new ConfigOptionFloat(0)); new_conf.set_key_value("support_interface_spacing", new ConfigOptionFloat(0));
new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum<SupportMaterialInterfacePattern>(SupportMaterialInterfacePattern::smipConcentric)); new_conf.set_key_value("support_interface_pattern", new ConfigOptionEnum<SupportMaterialInterfacePattern>(SupportMaterialInterfacePattern::smipRectilinearInterlaced));
new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false)); new_conf.set_key_value("independent_support_layer_height", new ConfigOptionBool(false));
m_config_manipulation.apply(m_config, &new_conf); m_config_manipulation.apply(m_config, &new_conf);
} }
@ -2262,6 +2225,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced"); optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
optgroup->append_single_option_line("support_top_z_distance", "support#top-z-distance"); optgroup->append_single_option_line("support_top_z_distance", "support#top-z-distance");
optgroup->append_single_option_line("support_bottom_z_distance", "support#bottom-z-distance"); optgroup->append_single_option_line("support_bottom_z_distance", "support#bottom-z-distance");
optgroup->append_single_option_line("tree_support_wall_count");
optgroup->append_single_option_line("support_base_pattern", "support#base-pattern"); optgroup->append_single_option_line("support_base_pattern", "support#base-pattern");
optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern"); optgroup->append_single_option_line("support_base_pattern_spacing", "support#base-pattern");
optgroup->append_single_option_line("support_angle"); optgroup->append_single_option_line("support_angle");
@ -2274,8 +2238,9 @@ void TabPrint::build()
//optgroup->append_single_option_line("support_interface_loop_pattern"); //optgroup->append_single_option_line("support_interface_loop_pattern");
optgroup->append_single_option_line("support_object_xy_distance", "support"); optgroup->append_single_option_line("support_object_xy_distance", "support");
optgroup->append_single_option_line("support_object_first_layer_gap", "support");
optgroup->append_single_option_line("bridge_no_support", "support#base-pattern"); optgroup->append_single_option_line("bridge_no_support", "support#base-pattern");
optgroup->append_single_option_line("max_bridge_length", "support#base-pattern"); optgroup->append_single_option_line("max_bridge_length", "support#tree-support-only-options");
optgroup->append_single_option_line("independent_support_layer_height", "support"); optgroup->append_single_option_line("independent_support_layer_height", "support");
optgroup = page->new_optgroup(L("Tree supports"), L"param_support_tree"); optgroup = page->new_optgroup(L("Tree supports"), L"param_support_tree");
@ -2289,8 +2254,6 @@ void TabPrint::build()
optgroup->append_single_option_line("tree_support_branch_angle", "support#tree-support-only-options"); optgroup->append_single_option_line("tree_support_branch_angle", "support#tree-support-only-options");
optgroup->append_single_option_line("tree_support_branch_angle_organic", "support#tree-support-only-options"); optgroup->append_single_option_line("tree_support_branch_angle_organic", "support#tree-support-only-options");
optgroup->append_single_option_line("tree_support_angle_slow"); optgroup->append_single_option_line("tree_support_angle_slow");
optgroup->append_single_option_line("tree_support_branch_diameter_double_wall");
optgroup->append_single_option_line("tree_support_wall_count");
optgroup->append_single_option_line("tree_support_adaptive_layer_height"); optgroup->append_single_option_line("tree_support_adaptive_layer_height");
optgroup->append_single_option_line("tree_support_auto_brim"); optgroup->append_single_option_line("tree_support_auto_brim");
optgroup->append_single_option_line("tree_support_brim_width"); optgroup->append_single_option_line("tree_support_brim_width");
@ -2458,7 +2421,7 @@ void TabPrint::toggle_options()
if (auto choice = dynamic_cast<Choice*>(field)) { if (auto choice = dynamic_cast<Choice*>(field)) {
auto def = print_config_def.get("support_style"); auto def = print_config_def.get("support_style");
std::vector<int> enum_set_normal = {smsDefault, smsGrid, smsSnug }; std::vector<int> enum_set_normal = {smsDefault, smsGrid, smsSnug };
std::vector<int> enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsOrganic }; std::vector<int> enum_set_tree = { smsDefault, smsTreeSlim, smsTreeStrong, smsTreeHybrid, smsTreeOrganic };
auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal; auto & set = is_tree(support_type) ? enum_set_tree : enum_set_normal;
auto & opt = const_cast<ConfigOptionDef &>(field->m_opt); auto & opt = const_cast<ConfigOptionDef &>(field->m_opt);
auto cb = dynamic_cast<ComboBox *>(choice->window); auto cb = dynamic_cast<ComboBox *>(choice->window);