mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-10 16:27:54 -06:00
ENH: improve tree support
1. add a hook inside tree branches for improved strength 2. fix the issue that interface may fly as a mess (delete the logic where gap nodes can skip dropping down) 3. fix the issue that base nodes may fly as a mess (smoothing should skip polygon nodes, see Jira:STUDIO-4403) Change-Id: Ie9f2039813c2ca3127ed8913304cc455fec8e7ee (cherry picked from commit 83cef5f91d49ff3d275a89ec3df8b5f0fd573f8c) (cherry picked from commit bambulab/BambuStudio@76f876a3c6) Co-authored-by: Arthur <arthur.tang@bambulab.com>
This commit is contained in:
parent
51290a853d
commit
1d1df4b0a5
7 changed files with 268 additions and 310 deletions
|
@ -664,6 +664,8 @@ 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::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)
|
||||||
|
|
|
@ -433,6 +433,7 @@ 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::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);
|
||||||
|
|
|
@ -50,6 +50,9 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||||
const SupportGeneratorLayersPtr &base_layers,
|
const SupportGeneratorLayersPtr &base_layers,
|
||||||
SupportGeneratorLayerStorage &layer_storage);
|
SupportGeneratorLayerStorage &layer_storage);
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
|
@ -152,7 +152,9 @@ struct SupportParameters {
|
||||||
const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter);
|
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 = object_config.support_line_width.get_abs_value(nozzle_diameter);
|
||||||
support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width;
|
support_extrusion_width = support_extrusion_width > 0 ? support_extrusion_width : extrusion_width;
|
||||||
|
|
||||||
|
independent_layer_height = print_config.independent_support_layer_height;
|
||||||
|
|
||||||
tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
|
tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(object_config.tree_support_branch_diameter_double_wall.value)) * M_PI;
|
||||||
|
|
||||||
support_style = object_config.support_style;
|
support_style = object_config.support_style;
|
||||||
|
@ -241,6 +243,7 @@ struct SupportParameters {
|
||||||
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));
|
const double thresh_big_overhang = Slic3r::sqr(scale_(10));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#endif
|
#endif
|
||||||
#define TAU (2.0 * M_PI)
|
#define TAU (2.0 * M_PI)
|
||||||
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
|
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
|
||||||
|
#define USE_SUPPORT_3D 0
|
||||||
|
|
||||||
// #define SUPPORT_TREE_DEBUG_TO_SVG
|
// #define SUPPORT_TREE_DEBUG_TO_SVG
|
||||||
|
|
||||||
|
@ -331,7 +332,8 @@ static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines)
|
||||||
// Move point from inside polygon if distance>0, outside if distance<0.
|
// Move point from inside polygon if distance>0, outside if distance<0.
|
||||||
// Special case: distance=0 means find the nearest point of from on the polygon contour.
|
// Special case: distance=0 means find the nearest point of from on the polygon contour.
|
||||||
// The max move distance should not excceed max_move_distance.
|
// The max move distance should not excceed max_move_distance.
|
||||||
static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, double distance = 0, double max_move_distance = std::numeric_limits<double>::max())
|
// @return success(true) or not(false)
|
||||||
|
static bool move_inside_expoly(const ExPolygon &polygon, Point& from, double distance = 0, double max_move_distance = std::numeric_limits<double>::max())
|
||||||
{
|
{
|
||||||
//TODO: This is copied from the moveInside of Polygons.
|
//TODO: This is copied from the moveInside of Polygons.
|
||||||
/*
|
/*
|
||||||
|
@ -347,7 +349,7 @@ static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, do
|
||||||
|
|
||||||
if (contour.points.size() < 2)
|
if (contour.points.size() < 2)
|
||||||
{
|
{
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
Point p0 = contour.points[polygon.contour.size() - 2];
|
Point p0 = contour.points[polygon.contour.size() - 2];
|
||||||
Point p1 = contour.points.back();
|
Point p1 = contour.points.back();
|
||||||
|
@ -444,12 +446,14 @@ static unsigned int move_inside_expoly(const ExPolygon &polygon, Point& from, do
|
||||||
{
|
{
|
||||||
from = ret;
|
from = ret;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else if (bestDist2 < max_move_distance * max_move_distance)
|
else if (bestDist2 < max_move_distance * max_move_distance)
|
||||||
{
|
{
|
||||||
from = ret;
|
from = ret;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -622,6 +626,7 @@ static bool is_inside_ex(const ExPolygons &polygons, const Point &pt)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// use project_onto which is more accurate but more expensive
|
||||||
static bool move_out_expolys(const ExPolygons& polygons, Point& from, double distance, double max_move_distance)
|
static bool move_out_expolys(const ExPolygons& polygons, Point& from, double distance, double max_move_distance)
|
||||||
{
|
{
|
||||||
Point from0 = from;
|
Point from0 = from;
|
||||||
|
@ -810,6 +815,7 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
if (!is_tree(stype)) return;
|
if (!is_tree(stype)) return;
|
||||||
|
|
||||||
max_cantilever_dist = 0;
|
max_cantilever_dist = 0;
|
||||||
|
m_highest_overhang_layer = 0;
|
||||||
|
|
||||||
// main part of overhang detection can be parallel
|
// main part of overhang detection can be parallel
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_object->layer_count()),
|
||||||
|
@ -1125,7 +1131,10 @@ void TreeSupport::detect_overhangs(bool detect_first_sharp_tail_only)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ts_layer->overhang_areas.empty()) has_overhangs = true;
|
if (!ts_layer->overhang_areas.empty()) {
|
||||||
|
has_overhangs = true;
|
||||||
|
m_highest_overhang_layer = std::max(m_highest_overhang_layer, size_t(layer_nr));
|
||||||
|
}
|
||||||
if (!layer->cantilevers.empty()) has_cantilever = true;
|
if (!layer->cantilevers.empty()) has_cantilever = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1383,7 +1392,7 @@ void TreeSupport::generate_toolpaths()
|
||||||
{
|
{
|
||||||
ExtrusionRole raft_contour_er = m_slicing_params.base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface;
|
ExtrusionRole raft_contour_er = m_slicing_params.base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface;
|
||||||
SupportLayer *ts_layer = m_object->support_layers().front();
|
SupportLayer *ts_layer = m_object->support_layers().front();
|
||||||
Flow flow = m_object->print()->brim_flow();
|
Flow flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||||
|
|
||||||
Polygons loops;
|
Polygons loops;
|
||||||
for (const ExPolygon& expoly : raft_areas) {
|
for (const ExPolygon& expoly : raft_areas) {
|
||||||
|
@ -1399,7 +1408,7 @@ void TreeSupport::generate_toolpaths()
|
||||||
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
||||||
coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.);
|
coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.);
|
||||||
|
|
||||||
Flow support_flow = layer_nr == 0 ? m_object->print()->brim_flow() : Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||||
Fill* filler_interface = Fill::new_from_type(ipRectilinear);
|
Fill* filler_interface = Fill::new_from_type(ipRectilinear);
|
||||||
filler_interface->angle = layer_nr == 0 ? 90 : 0;
|
filler_interface->angle = layer_nr == 0 ? 90 : 0;
|
||||||
filler_interface->spacing = support_extrusion_width;
|
filler_interface->spacing = support_extrusion_width;
|
||||||
|
@ -1517,27 +1526,34 @@ void TreeSupport::generate_toolpaths()
|
||||||
bool need_infill = with_infill;
|
bool need_infill = with_infill;
|
||||||
if(m_object_config->support_base_pattern==smpDefault)
|
if(m_object_config->support_base_pattern==smpDefault)
|
||||||
need_infill &= area_group.need_infill;
|
need_infill &= area_group.need_infill;
|
||||||
if (layer_id>0 && area_group.dist_to_top < 10 && !need_infill && m_support_params.support_style!=smsTreeHybrid) {
|
size_t walls = wall_count;
|
||||||
if (area_group.dist_to_top < 5) // 1 wall at the top <5mm
|
if (layer_id == 0) walls = std::numeric_limits<size_t>::max();
|
||||||
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, 1, flow, erSupportMaterial);
|
else if (area_group.need_extra_wall && walls < 2) walls += 1;
|
||||||
else // at least 2 walls for range [5,10)
|
std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern));
|
||||||
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, std::max(wall_count, size_t(2)), flow, erSupportMaterial);
|
filler_support->set_bounding_box(bbox_object);
|
||||||
}
|
filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines
|
||||||
else if (layer_id > 0 && need_infill && m_support_params.base_fill_pattern != ipLightning) {
|
filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);
|
||||||
std::shared_ptr<Fill> filler_support = std::shared_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
|
if (need_infill && m_support_params.base_fill_pattern != ipLightning) {
|
||||||
filler_support->set_bounding_box(bbox_object);
|
|
||||||
filler_support->spacing = object_config.support_base_pattern_spacing.value * support_density;// constant spacing to align support infill lines
|
|
||||||
filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);
|
|
||||||
|
|
||||||
// allow infill-only mode if support is thick enough (so min_wall_count is 0);
|
// allow infill-only mode if support is thick enough (so min_wall_count is 0);
|
||||||
// otherwise must draw 1 wall
|
// otherwise must draw 1 wall
|
||||||
size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0;
|
size_t min_wall_count = offset(poly, -scale_(support_spacing * 1.5)).empty() ? 1 : 0;
|
||||||
make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, wall_count), flow,
|
make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(min_wall_count, walls), flow,
|
||||||
erSupportMaterial, filler_support.get(), support_density);
|
erSupportMaterial, filler_support.get(), support_density);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly,
|
Polygons loops;
|
||||||
layer_id > 0 ? wall_count : std::numeric_limits<size_t>::max(), flow, erSupportMaterial);
|
loops.emplace_back(poly.contour);
|
||||||
|
if (layer_id == 0) {
|
||||||
|
float density = float(m_object_config->raft_first_layer_density.value * 0.01);
|
||||||
|
fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, filler_support.get(), density, erSupportMaterial, flow, m_support_params, true, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (size_t i = 1; i < walls; i++) {
|
||||||
|
Polygons contour_new = offset(poly.contour, -(i-0.5f) * flow.scaled_spacing(), jtSquare);
|
||||||
|
loops.insert(loops.end(), contour_new.begin(), contour_new.end());
|
||||||
|
}
|
||||||
|
fill_expolygons_with_sheath_generate_paths(ts_layer->support_fills.entities, loops, nullptr, 0, erSupportMaterial, flow, m_support_params, true, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1611,8 +1627,6 @@ void TreeSupport::generate()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<SupportNode*>> contact_nodes(m_object->layers().size());
|
|
||||||
|
|
||||||
profiler.stage_start(STAGE_total);
|
profiler.stage_start(STAGE_total);
|
||||||
|
|
||||||
// Generate overhang areas
|
// Generate overhang areas
|
||||||
|
@ -1627,23 +1641,22 @@ void TreeSupport::generate()
|
||||||
m_ts_data->is_slim = is_slim;
|
m_ts_data->is_slim = is_slim;
|
||||||
|
|
||||||
// Generate contact points of tree support
|
// Generate contact points of tree support
|
||||||
|
std::vector<std::vector<SupportNode*>> contact_nodes(m_object->layers().size());
|
||||||
|
|
||||||
|
std::vector<TreeSupport3D::SupportElements> move_bounds(m_highest_overhang_layer + 1);
|
||||||
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
|
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
|
||||||
m_object->print()->set_status(56, _L("Support: generate contact points"));
|
m_object->print()->set_status(56, _L("Support: generate contact points"));
|
||||||
generate_contact_points(contact_nodes);
|
generate_contact_points(contact_nodes, move_bounds);
|
||||||
profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES);
|
profiler.stage_finish(STAGE_GENERATE_CONTACT_NODES);
|
||||||
|
|
||||||
|
|
||||||
//Drop nodes to lower layers.
|
//Drop nodes to lower layers.
|
||||||
profiler.stage_start(STAGE_DROP_DOWN_NODES);
|
profiler.stage_start(STAGE_DROP_DOWN_NODES);
|
||||||
m_object->print()->set_status(60, _L("Support: propagate branches"));
|
m_object->print()->set_status(60, _L("Support: propagate branches"));
|
||||||
drop_nodes(contact_nodes);
|
drop_nodes(contact_nodes);
|
||||||
profiler.stage_finish(STAGE_DROP_DOWN_NODES);
|
profiler.stage_finish(STAGE_DROP_DOWN_NODES);
|
||||||
|
|
||||||
smooth_nodes(contact_nodes);
|
smooth_nodes(contact_nodes); // , tree_support_3d_config);
|
||||||
|
|
||||||
if (!m_object->config().tree_support_adaptive_layer_height)
|
|
||||||
// Adjust support layer heights
|
|
||||||
adjust_layer_heights(contact_nodes);
|
|
||||||
|
|
||||||
|
|
||||||
//Generate support areas.
|
//Generate support areas.
|
||||||
profiler.stage_start(STAGE_DRAW_CIRCLES);
|
profiler.stage_start(STAGE_DRAW_CIRCLES);
|
||||||
|
@ -1691,7 +1704,7 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, size_t layers_to_
|
||||||
return radius;
|
return radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor)
|
coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance)
|
||||||
{
|
{
|
||||||
double radius;
|
double radius;
|
||||||
coordf_t tip_height = base_radius;// this is a 45 degree tip
|
coordf_t tip_height = base_radius;// this is a 45 degree tip
|
||||||
|
@ -1712,11 +1725,63 @@ coordf_t TreeSupport::calc_branch_radius(coordf_t base_radius, coordf_t mm_to_to
|
||||||
return radius;
|
return radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExPolygons TreeSupport::get_avoidance(coordf_t radius, size_t obj_layer_nr)
|
||||||
|
{
|
||||||
|
#if USE_SUPPORT_3D
|
||||||
|
if (m_model_volumes) {
|
||||||
|
bool on_build_plate = m_object_config->support_on_build_plate_only.value;
|
||||||
|
const Polygons& avoid_polys = m_model_volumes->getAvoidance(radius, obj_layer_nr, TreeSupport3D::TreeModelVolumes::AvoidanceType::Fast, on_build_plate, true);
|
||||||
|
ExPolygons expolys;
|
||||||
|
for (auto& poly : avoid_polys)
|
||||||
|
expolys.emplace_back(std::move(poly));
|
||||||
|
return expolys;
|
||||||
|
}
|
||||||
|
return ExPolygons();
|
||||||
|
#else
|
||||||
|
return m_ts_data->get_avoidance(radius, obj_layer_nr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
ExPolygons TreeSupport::get_collision(coordf_t radius, size_t layer_nr)
|
||||||
|
{
|
||||||
|
#if USE_SUPPORT_3D
|
||||||
|
if (m_model_volumes) {
|
||||||
|
bool on_build_plate = m_object_config->support_on_build_plate_only.value;
|
||||||
|
const Polygons& collision_polys = m_model_volumes->getCollision(radius, layer_nr, true);
|
||||||
|
ExPolygons expolys;
|
||||||
|
for (auto& poly : collision_polys)
|
||||||
|
expolys.emplace_back(std::move(poly));
|
||||||
|
return expolys;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return m_ts_data->get_collision(radius, layer_nr);
|
||||||
|
#endif
|
||||||
|
return ExPolygons();
|
||||||
|
}
|
||||||
|
Polygons TreeSupport::get_collision_polys(coordf_t radius, size_t layer_nr)
|
||||||
|
{
|
||||||
|
#if USE_SUPPORT_3D
|
||||||
|
if (m_model_volumes) {
|
||||||
|
bool on_build_plate = m_object_config->support_on_build_plate_only.value;
|
||||||
|
const Polygons& collision_polys = m_model_volumes->getCollision(radius, layer_nr, true);
|
||||||
|
return collision_polys;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ExPolygons expolys = m_ts_data->get_collision(radius, layer_nr);
|
||||||
|
Polygons polys;
|
||||||
|
for (auto& expoly : expolys)
|
||||||
|
polys.emplace_back(std::move(expoly.contour));
|
||||||
|
return polys;
|
||||||
|
#endif
|
||||||
|
return Polygons();
|
||||||
|
}
|
||||||
|
|
||||||
template<typename RegionType> // RegionType could be ExPolygons or Polygons
|
template<typename RegionType> // RegionType could be ExPolygons or Polygons
|
||||||
ExPolygons avoid_object_remove_extra_small_parts(ExPolygons &expolys, const RegionType&avoid_region) {
|
ExPolygons avoid_object_remove_extra_small_parts(ExPolygons &expolys, const RegionType&avoid_region) {
|
||||||
ExPolygons expolys_out;
|
ExPolygons expolys_out;
|
||||||
|
if(expolys.empty()) return expolys_out;
|
||||||
|
auto clipped_avoid_region=ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region, get_extents(expolys));
|
||||||
for (auto expoly : expolys) {
|
for (auto expoly : expolys) {
|
||||||
auto expolys_avoid = diff_ex(expoly, avoid_region);
|
auto expolys_avoid = diff_ex(expoly, clipped_avoid_region);
|
||||||
int idx_max_area = -1;
|
int idx_max_area = -1;
|
||||||
float max_area = 0;
|
float max_area = 0;
|
||||||
for (int i = 0; i < expolys_avoid.size(); ++i) {
|
for (int i = 0; i < expolys_avoid.size(); ++i) {
|
||||||
|
@ -1815,7 +1880,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
int bottom_gap_layers = round(m_slicing_params.gap_object_support / m_slicing_params.layer_height);
|
int bottom_gap_layers = round(m_slicing_params.gap_object_support / m_slicing_params.layer_height);
|
||||||
const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
|
const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
|
||||||
const coordf_t branch_radius_scaled = scale_(branch_radius);
|
const coordf_t branch_radius_scaled = scale_(branch_radius);
|
||||||
bool on_buildplate_only = config.support_on_build_plate_only.value;
|
bool on_buildplate_only = m_object_config->support_on_build_plate_only.value;
|
||||||
Polygon branch_circle; //Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
|
Polygon branch_circle; //Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
|
||||||
|
|
||||||
// Use square support if there are too many nodes per layer because circle support needs much longer time to compute
|
// Use square support if there are too many nodes per layer because circle support needs much longer time to compute
|
||||||
|
@ -1861,7 +1926,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
|
|
||||||
// coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map
|
// coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
tbb::blocked_range<size_t>(0, m_object->layer_count()),
|
tbb::blocked_range<size_t>(0, m_object->layer_count(), m_object->layer_count()),
|
||||||
[&](const tbb::blocked_range<size_t>& range)
|
[&](const tbb::blocked_range<size_t>& range)
|
||||||
{
|
{
|
||||||
for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++)
|
for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++)
|
||||||
|
@ -1912,10 +1977,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
// 1) node is a normal part of hybrid support
|
// 1) node is a normal part of hybrid support
|
||||||
// 2) node is virtual
|
// 2) node is virtual
|
||||||
if (node.type == ePolygon || node.distance_to_top<0) {
|
if (node.type == ePolygon || node.distance_to_top<0) {
|
||||||
if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1)
|
if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1)
|
||||||
area.emplace_back(*node.overhang);
|
area.emplace_back(node.overhang);
|
||||||
else {
|
else {
|
||||||
area = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance));
|
area = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance));
|
||||||
}
|
}
|
||||||
if (node.type == ePolygon)
|
if (node.type == ePolygon)
|
||||||
has_polygon_node = true;
|
has_polygon_node = true;
|
||||||
|
@ -1923,7 +1988,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
else {
|
else {
|
||||||
Polygon circle;
|
Polygon circle;
|
||||||
size_t layers_to_top = node.distance_to_top;
|
size_t layers_to_top = node.distance_to_top;
|
||||||
double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor) / branch_radius;
|
double scale = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor, false) / branch_radius;
|
||||||
|
|
||||||
if (/*is_slim*/1) { // draw ellipse along movement direction
|
if (/*is_slim*/1) { // draw ellipse along movement direction
|
||||||
double moveX = node.movement.x() / (scale * branch_radius_scaled);
|
double moveX = node.movement.x() / (scale * branch_radius_scaled);
|
||||||
|
@ -1949,18 +2014,20 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
? layers_to_top * layer_height /
|
? layers_to_top * layer_height /
|
||||||
(scale * branch_radius) * 0.5
|
(scale * branch_radius) * 0.5
|
||||||
: config.tree_support_brim_width;
|
: config.tree_support_brim_width;
|
||||||
circle = offset(circle, scale_(brim_width))[0];
|
auto tmp=offset(circle, scale_(brim_width));
|
||||||
|
if(!tmp.empty())
|
||||||
|
circle = tmp[0];
|
||||||
}
|
}
|
||||||
area.emplace_back(ExPolygon(circle));
|
area.emplace_back(ExPolygon(circle));
|
||||||
// merge overhang to get a smoother interface surface
|
// merge overhang to get a smoother interface surface
|
||||||
// Do not merge when buildplate_only is on, because some underneath nodes may have been deleted.
|
// Do not merge when buildplate_only is on, because some underneath nodes may have been deleted.
|
||||||
if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only) {
|
if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only) {
|
||||||
ExPolygons overhang_expanded;
|
ExPolygons overhang_expanded;
|
||||||
if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1)
|
if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1)
|
||||||
overhang_expanded.emplace_back(*node.overhang);
|
overhang_expanded.emplace_back(node.overhang);
|
||||||
else {
|
else {
|
||||||
// 对于有缺陷的模型,overhang膨胀以后可能是空的!
|
// 对于有缺陷的模型,overhang膨胀以后可能是空的!
|
||||||
overhang_expanded = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance));
|
overhang_expanded = offset_ex({ node.overhang }, scale_(m_ts_data->m_xy_distance));
|
||||||
}
|
}
|
||||||
append(area, overhang_expanded);
|
append(area, overhang_expanded);
|
||||||
}
|
}
|
||||||
|
@ -2013,21 +2080,21 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface);
|
roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface));
|
roof_areas = diff_ex(roof_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region_interface, get_extents(roof_areas)));
|
||||||
roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface));
|
roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(avoid_region_interface, get_extents(roof_1st_layer)));
|
||||||
}
|
}
|
||||||
roof_areas = intersection_ex(roof_areas, m_machine_border);
|
roof_areas = intersection_ex(roof_areas, m_machine_border);
|
||||||
|
|
||||||
// roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
|
// roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
|
||||||
roof_1st_layer = std::move(diff_ex(roof_1st_layer, roof_areas));
|
roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas,get_extents(roof_1st_layer)));
|
||||||
roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border);
|
roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border);
|
||||||
|
|
||||||
// let supports touch objects when brim is on
|
// let supports touch objects when brim is on
|
||||||
auto avoid_region = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr);
|
auto avoid_region = get_collision_polys((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr);
|
||||||
base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region);
|
//base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region);
|
||||||
base_areas = std::move(diff_ex(base_areas, roof_areas));
|
base_areas = diff_clipped(base_areas, avoid_region);
|
||||||
base_areas = std::move(diff_ex(base_areas, roof_1st_layer));
|
ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas);
|
||||||
base_areas = std::move(diff_ex(base_areas, roof_gap_areas));
|
base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas)));
|
||||||
base_areas = intersection_ex(base_areas, m_machine_border);
|
base_areas = intersection_ex(base_areas, m_machine_border);
|
||||||
|
|
||||||
if (SQUARE_SUPPORT) {
|
if (SQUARE_SUPPORT) {
|
||||||
|
@ -2255,7 +2322,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<SupportNode*>>& con
|
||||||
// make sure 1) roof1 and object 2) roof1 and roof, won't intersect
|
// make sure 1) roof1 and object 2) roof1 and roof, won't intersect
|
||||||
// Note: We can't replace roof1 directly, as we have recorded its address.
|
// Note: We can't replace roof1 directly, as we have recorded its address.
|
||||||
// So instead we need to replace its members one by one.
|
// So instead we need to replace its members one by one.
|
||||||
auto tmp1 = diff_ex(roof1, m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr));
|
auto tmp1 = diff_ex(roof1, get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr));
|
||||||
tmp1 = diff_ex(tmp1, ts_layer->roof_areas);
|
tmp1 = diff_ex(tmp1, ts_layer->roof_areas);
|
||||||
if (!tmp1.empty()) {
|
if (!tmp1.empty()) {
|
||||||
roof1.contour = std::move(tmp1[0].contour);
|
roof1.contour = std::move(tmp1[0].contour);
|
||||||
|
@ -2327,8 +2394,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS)
|
if (angle > 30.0 && node->radius > MIN_BRANCH_RADIUS)
|
||||||
angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0;
|
angle = (node->radius - MIN_BRANCH_RADIUS) / (MAX_BRANCH_RADIUS - MIN_BRANCH_RADIUS) * (config.tree_support_branch_angle.value - 30.0) + 30.0;
|
||||||
double tan_angle = tan(angle * M_PI / 180);
|
double tan_angle = tan(angle * M_PI / 180);
|
||||||
int wall_count_ = node->radius > 2 * support_line_width ? wall_count : 1;
|
node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) : std::numeric_limits<coordf_t>::max();
|
||||||
node->max_move_dist = (angle < 90) ? (coordf_t) (tan_angle * node->height) * wall_count_ : std::numeric_limits<coordf_t>::max();
|
|
||||||
node->max_move_dist = std::min(node->max_move_dist, support_extrusion_width);
|
node->max_move_dist = std::min(node->max_move_dist, support_extrusion_width);
|
||||||
move_dist = node->max_move_dist;
|
move_dist = node->max_move_dist;
|
||||||
}
|
}
|
||||||
|
@ -2344,50 +2410,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
m_spanning_trees.resize(contact_nodes.size());
|
m_spanning_trees.resize(contact_nodes.size());
|
||||||
//m_mst_line_x_layer_contour_caches.resize(contact_nodes.size());
|
//m_mst_line_x_layer_contour_caches.resize(contact_nodes.size());
|
||||||
|
|
||||||
if (0)
|
|
||||||
{// get outlines below and avoidance area using tbb
|
|
||||||
// This part only takes very little time, so we disable it.
|
|
||||||
typedef std::chrono::high_resolution_clock clock_;
|
|
||||||
typedef std::chrono::duration<double, std::ratio<1> > second_;
|
|
||||||
std::chrono::time_point<clock_> t0{ clock_::now() };
|
|
||||||
|
|
||||||
// get all the possible radiis
|
|
||||||
std::vector<std::set<coordf_t> > all_layer_radius(m_highest_overhang_layer+1);
|
|
||||||
std::vector<std::set<coordf_t>> all_layer_node_dist(m_highest_overhang_layer + 1);
|
|
||||||
for (size_t layer_nr = m_highest_overhang_layer; layer_nr > 0; layer_nr--)
|
|
||||||
{
|
|
||||||
if (layer_heights[layer_nr].height < EPSILON) continue;
|
|
||||||
auto& layer_radius = all_layer_radius[layer_nr];
|
|
||||||
auto& layer_node_dist = all_layer_node_dist[layer_nr];
|
|
||||||
for (SupportNode *p_node : contact_nodes[layer_nr]) {
|
|
||||||
layer_node_dist.emplace(p_node->dist_mm_to_top);
|
|
||||||
}
|
|
||||||
size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr;
|
|
||||||
if (layer_nr_next <= m_highest_overhang_layer && layer_nr_next>0) {
|
|
||||||
for (auto node_dist : layer_node_dist)
|
|
||||||
all_layer_node_dist[layer_nr_next].emplace(node_dist + layer_heights[layer_nr].height);
|
|
||||||
}
|
|
||||||
for (auto node_dist : layer_node_dist) {
|
|
||||||
layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// parallel pre-compute avoidance
|
|
||||||
//tbb::parallel_for(tbb::blocked_range<size_t>(1, m_highest_overhang_layer), [&](const tbb::blocked_range<size_t> &range) {
|
|
||||||
//for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) {
|
|
||||||
for (size_t layer_nr = 0; layer_nr < all_layer_radius.size(); layer_nr++) {
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "pre calculate_avoidance layer=" << layer_nr;
|
|
||||||
for (auto node_radius : all_layer_radius[layer_nr]) {
|
|
||||||
m_ts_data->get_avoidance(0, layer_nr);
|
|
||||||
m_ts_data->get_avoidance(node_radius, layer_nr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//});
|
|
||||||
|
|
||||||
double duration{ std::chrono::duration_cast<second_>(clock_::now() - t0).count() };
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "before m_avoidance_cache.size()=" << m_ts_data->m_avoidance_cache.size()
|
|
||||||
<< ", takes " << duration << " secs.";
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) // Skip layer 0, since we can't drop down the vertices there.
|
for (size_t layer_nr = contact_nodes.size() - 1; layer_nr > 0; layer_nr--) // Skip layer 0, since we can't drop down the vertices there.
|
||||||
{
|
{
|
||||||
if (m_object->print()->canceled())
|
if (m_object->print()->canceled())
|
||||||
|
@ -2436,15 +2458,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
{
|
{
|
||||||
const SupportNode& node = *p_node;
|
const SupportNode& node = *p_node;
|
||||||
|
|
||||||
if (node.distance_to_top < 0) {
|
|
||||||
// gap nodes do not merge or move
|
|
||||||
SupportNode* next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node,
|
|
||||||
print_z_next, height_next);
|
|
||||||
get_max_move_dist(next_node);
|
|
||||||
next_node->is_merged = false;
|
|
||||||
contact_nodes[layer_nr_next].emplace_back(next_node);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported.
|
if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported.
|
||||||
{
|
{
|
||||||
unsupported_branch_leaves.push_front({ layer_nr, p_node });
|
unsupported_branch_leaves.push_front({ layer_nr, p_node });
|
||||||
|
@ -2530,14 +2543,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor));
|
coord_t neighbour_radius = scale_(calc_branch_radius(branch_radius, neighbour_node->dist_mm_to_top, diameter_angle_scale_factor));
|
||||||
Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius),
|
Point pt_north = neighbour + Point(0, neighbour_radius), pt_south = neighbour - Point(0, neighbour_radius),
|
||||||
pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0);
|
pt_west = neighbour - Point(neighbour_radius, 0), pt_east = neighbour + Point(neighbour_radius, 0);
|
||||||
if (is_inside_ex(*node.overhang, neighbour) && is_inside_ex(*node.overhang, pt_north) && is_inside_ex(*node.overhang, pt_south)
|
if (is_inside_ex(node.overhang, neighbour) && is_inside_ex(node.overhang, pt_north) && is_inside_ex(node.overhang, pt_south)
|
||||||
&& is_inside_ex(*node.overhang, pt_west) && is_inside_ex(*node.overhang, pt_east)){
|
&& is_inside_ex(node.overhang, pt_west) && is_inside_ex(node.overhang, pt_east)){
|
||||||
node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top);
|
node.distance_to_top = std::max(node.distance_to_top, neighbour_node->distance_to_top);
|
||||||
node.support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour_node->support_roof_layers_below);
|
node.support_roof_layers_below = std::max(node.support_roof_layers_below, neighbour_node->support_roof_layers_below);
|
||||||
node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top);
|
node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top);
|
||||||
node.merged_neighbours.push_front(neighbour_node);
|
node.merged_neighbours.push_front(neighbour_node);
|
||||||
node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
|
node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
|
||||||
node.is_merged = true;
|
|
||||||
to_delete.insert(neighbour_node);
|
to_delete.insert(neighbour_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2550,7 +2562,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
|
|
||||||
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor);
|
const coordf_t branch_radius_node = calc_branch_radius(branch_radius, node.dist_mm_to_top, diameter_angle_scale_factor);
|
||||||
|
|
||||||
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next);
|
auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next);
|
||||||
if (group_index == 0)
|
if (group_index == 0)
|
||||||
{
|
{
|
||||||
//Avoid collisions.
|
//Avoid collisions.
|
||||||
|
@ -2566,12 +2578,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
node_ = p_node->parent ? p_node : neighbour;
|
node_ = p_node->parent ? p_node : neighbour;
|
||||||
// Make sure the next pass doesn't drop down either of these (since that already happened).
|
// Make sure the next pass doesn't drop down either of these (since that already happened).
|
||||||
node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node);
|
node_->merged_neighbours.push_front(node_ == p_node ? neighbour : p_node);
|
||||||
const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position);
|
//const bool to_buildplate = !is_inside_ex(get_collision(0, layer_nr_next), next_position);
|
||||||
|
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr_next], next_position);
|
||||||
SupportNode * next_node = new SupportNode(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_,
|
SupportNode * next_node = new SupportNode(next_position, node_->distance_to_top + 1, layer_nr_next, node_->support_roof_layers_below-1, to_buildplate, node_,
|
||||||
print_z_next, height_next);
|
print_z_next, height_next);
|
||||||
next_node->movement = next_position - node.position;
|
next_node->movement = next_position - node.position;
|
||||||
get_max_move_dist(next_node);
|
get_max_move_dist(next_node);
|
||||||
next_node->is_merged = true;
|
|
||||||
contact_nodes[layer_nr_next].push_back(next_node);
|
contact_nodes[layer_nr_next].push_back(next_node);
|
||||||
|
|
||||||
|
|
||||||
|
@ -2593,7 +2605,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top);
|
node.dist_mm_to_top = std::max(node.dist_mm_to_top, neighbour_node->dist_mm_to_top);
|
||||||
node.merged_neighbours.push_front(neighbour_node);
|
node.merged_neighbours.push_front(neighbour_node);
|
||||||
node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
|
node.merged_neighbours.insert(node.merged_neighbours.end(), neighbour_node->merged_neighbours.begin(), neighbour_node->merged_neighbours.end());
|
||||||
node.is_merged = true;
|
|
||||||
to_delete.insert(neighbour_node);
|
to_delete.insert(neighbour_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2615,7 +2626,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
SupportNode * next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate,
|
SupportNode * next_node = new SupportNode(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate,
|
||||||
p_node, print_z_next, height_next);
|
p_node, print_z_next, height_next);
|
||||||
next_node->max_move_dist = 0;
|
next_node->max_move_dist = 0;
|
||||||
next_node->is_merged = false;
|
|
||||||
contact_nodes[layer_nr_next].emplace_back(next_node);
|
contact_nodes[layer_nr_next].emplace_back(next_node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2633,9 +2643,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
unsupported_branch_leaves.push_front({ layer_nr, p_node });
|
unsupported_branch_leaves.push_front({ layer_nr, p_node });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SupportNode* pn = p_node;
|
|
||||||
for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent)
|
|
||||||
pn->support_floor_layers_above = bottom_interface_layers - i + 1; // +1 so the parent node has support_floor_layers_above=2
|
|
||||||
to_delete.insert(p_node);
|
to_delete.insert(p_node);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -2643,9 +2650,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
// if the link between parent and current is cut by contours, mark current as bottom contact node
|
// if the link between parent and current is cut by contours, mark current as bottom contact node
|
||||||
if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false)
|
if (p_node->parent && intersection_ln({p_node->position, p_node->parent->position}, layer_contours).empty()==false)
|
||||||
{
|
{
|
||||||
SupportNode* pn = p_node->parent;
|
|
||||||
for (int i = 0; i <= bottom_interface_layers && pn; i++, pn = pn->parent)
|
|
||||||
pn->support_floor_layers_above = bottom_interface_layers - i + 1;
|
|
||||||
to_delete.insert(p_node);
|
to_delete.insert(p_node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2703,7 +2707,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
branch_radius_temp = branch_radius_node;
|
branch_radius_temp = branch_radius_node;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr_next);
|
auto avoid_layer = get_avoidance(branch_radius_node, layer_nr_next);
|
||||||
|
|
||||||
Point to_outside = projection_onto(avoid_layer, node.position);
|
Point to_outside = projection_onto(avoid_layer, node.position);
|
||||||
Point direction_to_outer = to_outside - node.position;
|
Point direction_to_outer = to_outside - node.position;
|
||||||
|
@ -2755,27 +2759,26 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex);
|
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);
|
||||||
SupportNode * next_node = new SupportNode(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node,
|
SupportNode * next_node = new SupportNode(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node,
|
||||||
print_z_next, height_next);
|
print_z_next, height_next);
|
||||||
next_node->movement = movement;
|
next_node->movement = movement;
|
||||||
get_max_move_dist(next_node);
|
get_max_move_dist(next_node);
|
||||||
next_node->is_merged = false;
|
|
||||||
contact_nodes[layer_nr_next].push_back(next_node);
|
contact_nodes[layer_nr_next].push_back(next_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||||
if (contact_nodes[layer_nr].empty() == false) {
|
if (contact_nodes[layer_nr].empty() == false) {
|
||||||
draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), m_ts_data->get_avoidance(0, layer_nr),
|
draw_contours_and_nodes_to_svg((boost::format("%.2f") % contact_nodes[layer_nr][0]->print_z).str(), get_avoidance(0, layer_nr),
|
||||||
m_ts_data->get_avoidance(branch_radius_temp, layer_nr),
|
get_avoidance(branch_radius_temp, layer_nr),
|
||||||
m_ts_data->m_layer_outlines_below[layer_nr],
|
m_ts_data->m_layer_outlines_below[layer_nr],
|
||||||
contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" });
|
contact_nodes[layer_nr], contact_nodes[layer_nr_next], "contact_points", { "overhang","avoid","outline" }, { "blue","red","yellow" });
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z;
|
BOOST_LOG_TRIVIAL(debug) << "drop_nodes layer " << layer_nr << ", print_z=" << ts_layer->print_z;
|
||||||
for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) {
|
for (size_t i = 0; i < std::min(size_t(5), contact_nodes[layer_nr].size()); i++) {
|
||||||
auto &node = contact_nodes[layer_nr][i];
|
auto &node = contact_nodes[layer_nr][i];
|
||||||
BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement << ", is_merged=" << node->is_merged;
|
BOOST_LOG_TRIVIAL(debug) << "\t node " << i << ", pos=" << node->position << ", move = " << node->movement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -2792,10 +2795,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<SupportNode*>>& contact_nod
|
||||||
if (to_erase != contact_nodes[i_layer].end())
|
if (to_erase != contact_nodes[i_layer].end())
|
||||||
{
|
{
|
||||||
// update the parent-child chain
|
// update the parent-child chain
|
||||||
if(i_node->parent)
|
for (SupportNode* parent : i_node->parents) {
|
||||||
i_node->parent->child = i_node->child;
|
if (parent->child==i_node)
|
||||||
if(i_node->child)
|
parent->child = i_node->child;
|
||||||
i_node->child->parent = i_node->parent;
|
}
|
||||||
|
if (i_node->child) {
|
||||||
|
i_node->child->parents= i_node->parents;
|
||||||
|
}
|
||||||
contact_nodes[i_layer].erase(to_erase);
|
contact_nodes[i_layer].erase(to_erase);
|
||||||
to_free_node_set.insert(i_node);
|
to_free_node_set.insert(i_node);
|
||||||
|
|
||||||
|
@ -2824,47 +2830,66 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_
|
||||||
if (curr_layer_nodes.empty()) continue;
|
if (curr_layer_nodes.empty()) continue;
|
||||||
for (SupportNode *node : curr_layer_nodes) {
|
for (SupportNode *node : curr_layer_nodes) {
|
||||||
node->is_processed = false;
|
node->is_processed = false;
|
||||||
if (layer_nr == 0) node->is_merged = true; // nodes on plate are also merged nodes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float max_move = scale_(m_object_config->support_line_width / 2);
|
||||||
for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) {
|
for (int layer_nr = 0; layer_nr< contact_nodes.size(); layer_nr++) {
|
||||||
std::vector<SupportNode *> &curr_layer_nodes = contact_nodes[layer_nr];
|
std::vector<SupportNode *> &curr_layer_nodes = contact_nodes[layer_nr];
|
||||||
if (curr_layer_nodes.empty()) continue;
|
if (curr_layer_nodes.empty()) continue;
|
||||||
for (SupportNode *node : curr_layer_nodes) {
|
for (SupportNode *node : curr_layer_nodes) {
|
||||||
if (!node->is_processed) {
|
if (!node->is_processed) {
|
||||||
std::vector<Point> pts;
|
std::vector<Point> pts;
|
||||||
|
std::vector<double> radii;
|
||||||
std::vector<SupportNode *> branch;
|
std::vector<SupportNode *> branch;
|
||||||
SupportNode * p_node = node;
|
SupportNode * p_node = node;
|
||||||
// add a fixed head
|
// add a fixed head if it's not a polygon node, see STUDIO-4403
|
||||||
if (node->child) {
|
// Polygon node can't be added because the move distance might be huge, making the nodes in between jump and dangling
|
||||||
|
if (node->child && node->child->type!=ePolygon) {
|
||||||
pts.push_back(p_node->child->position);
|
pts.push_back(p_node->child->position);
|
||||||
|
radii.push_back(p_node->child->radius);
|
||||||
branch.push_back(p_node->child);
|
branch.push_back(p_node->child);
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
pts.push_back(p_node->position);
|
pts.push_back(p_node->position);
|
||||||
|
radii.push_back(p_node->radius);
|
||||||
branch.push_back(p_node);
|
branch.push_back(p_node);
|
||||||
p_node = p_node->parent;
|
p_node = p_node->parent;
|
||||||
} while (p_node && !p_node->is_processed);
|
} while (p_node && !p_node->is_processed);
|
||||||
if (pts.size() < 3) continue;
|
if (pts.size() < 3) continue;
|
||||||
|
|
||||||
std::vector<Point> pts1 = pts;
|
std::vector<Point> pts1 = pts;
|
||||||
|
std::vector<double> radii1 = radii;
|
||||||
// TODO here we assume layer height gap is constant. If not true, need to consider height jump
|
// TODO here we assume layer height gap is constant. If not true, need to consider height jump
|
||||||
const int iterations = 100;
|
const int iterations = 100;
|
||||||
for (size_t k = 0; k < iterations; k++) {
|
for (size_t k = 0; k < iterations; k++) {
|
||||||
for (size_t i = 1; i < pts.size() - 1; i++) {
|
for (size_t i = 1; i < pts.size() - 1; i++) {
|
||||||
size_t i2 = i >= 2 ? i - 2 : 0;
|
size_t i2 = i >= 2 ? i - 2 : 0;
|
||||||
size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1;
|
size_t i3 = i < pts.size() - 2 ? i + 2 : pts.size() - 1;
|
||||||
Point pt = (pts[i2] + pts[i - 1] + pts[i] + pts[i + 1] + pts[i3]) / 5;
|
Point pt = ( pts[i - 1] + pts[i] + pts[i + 1] ) / 3;
|
||||||
pts1[i] = pt;
|
pts1[i] = pt;
|
||||||
|
radii1[i] = (radii[i - 1] + radii[i] + radii[i + 1] ) / 3;
|
||||||
if (k == iterations - 1) {
|
if (k == iterations - 1) {
|
||||||
branch[i]->position = pt;
|
branch[i]->position = pt;
|
||||||
|
branch[i]->radius = radii1[i];
|
||||||
branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2;
|
branch[i]->movement = (pts[i + 1] - pts[i - 1]) / 2;
|
||||||
branch[i]->is_processed = true;
|
branch[i]->is_processed = true;
|
||||||
|
if (branch[i]->parents.size()>1 || (branch[i]->movement.x() > max_move || branch[i]->movement.y() > max_move))
|
||||||
|
branch[i]->need_extra_wall = true;
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "smooth_nodes: layer_nr=" << layer_nr << ", i=" << i << ", pt=" << pt << ", movement=" << branch[i]->movement << ", radius=" << branch[i]->radius;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (k < iterations - 1)
|
if (k < iterations - 1) {
|
||||||
std::swap(pts, pts1);
|
std::swap(pts, pts1);
|
||||||
|
std::swap(radii, radii1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// interpolate need_extra_wall in the end
|
||||||
|
for (size_t i = 1; i < branch.size() - 1; i++) {
|
||||||
|
if (branch[i - 1]->need_extra_wall && branch[i + 1]->need_extra_wall)
|
||||||
|
branch[i]->need_extra_wall = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2905,116 +2930,22 @@ void TreeSupport::smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeSupport::adjust_layer_heights(std::vector<std::vector<SupportNode*>>& contact_nodes)
|
|
||||||
{
|
|
||||||
if (contact_nodes.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const PrintConfig& print_config = m_object->print()->config();
|
|
||||||
const PrintObjectConfig& config = m_object->config();
|
|
||||||
// don't merge layers for Vine support, or the branches will be unsmooth
|
|
||||||
// TODO can we merge layers in a way that guaranttees smoothness?
|
|
||||||
if (!print_config.independent_support_layer_height || is_slim) {
|
|
||||||
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
|
|
||||||
std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
|
|
||||||
for (SupportNode* node : curr_layer_nodes) {
|
|
||||||
node->print_z = m_object->get_layer(layer_nr)->print_z;
|
|
||||||
node->height = m_object->get_layer(layer_nr)->height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extreme layer_id
|
|
||||||
std::vector<int> extremes;
|
|
||||||
const coordf_t layer_height = config.layer_height.value;
|
|
||||||
const coordf_t max_layer_height = m_slicing_params.max_layer_height;
|
|
||||||
const size_t bot_intf_layers = config.support_interface_bottom_layers.value;
|
|
||||||
const size_t top_intf_layers = config.support_interface_top_layers.value;
|
|
||||||
|
|
||||||
// if already using max layer height, no need to adjust
|
|
||||||
if (layer_height == max_layer_height) return;
|
|
||||||
|
|
||||||
extremes.push_back(0);
|
|
||||||
for (SupportNode* node : contact_nodes[0]) {
|
|
||||||
node->print_z = m_object->get_layer(0)->print_z;
|
|
||||||
node->height = m_object->get_layer(0)->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++) {
|
|
||||||
std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
|
|
||||||
for (SupportNode* node : curr_layer_nodes) {
|
|
||||||
if (node->support_roof_layers_below >0 || node->support_floor_layers_above == bot_intf_layers) {
|
|
||||||
extremes.push_back(layer_nr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (extremes.back() == layer_nr) {
|
|
||||||
// contact layer use the same print_z and layer height with object layer
|
|
||||||
for (SupportNode* node : curr_layer_nodes) {
|
|
||||||
node->print_z = m_object->get_layer(layer_nr)->print_z;
|
|
||||||
node->height = m_object->get_layer(layer_nr)->height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// schedule new layer heights and print_z
|
|
||||||
for (size_t idx_extreme = 0; idx_extreme < extremes.size(); idx_extreme++) {
|
|
||||||
int extr2_layer_nr = extremes[idx_extreme];
|
|
||||||
coordf_t extr2z = m_object->get_layer(extr2_layer_nr)->bottom_z();
|
|
||||||
int extr1_layer_nr = idx_extreme == 0 ? -1 : extremes[idx_extreme - 1];
|
|
||||||
coordf_t extr1z = idx_extreme == 0 ? 0.f : m_object->get_layer(extr1_layer_nr)->print_z;
|
|
||||||
coordf_t dist = extr2z - extr1z;
|
|
||||||
|
|
||||||
// Insert intermediate layers.
|
|
||||||
size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height));
|
|
||||||
if (n_layers_extra <= 1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
coordf_t step = dist / coordf_t(n_layers_extra);
|
|
||||||
coordf_t print_z = extr1z + step;
|
|
||||||
assert(step >= layer_height - EPSILON);
|
|
||||||
for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) {
|
|
||||||
std::vector<SupportNode*>& curr_layer_nodes = contact_nodes[layer_nr];
|
|
||||||
if (curr_layer_nodes.empty()) continue;
|
|
||||||
|
|
||||||
if (std::abs(print_z - curr_layer_nodes[0]->print_z) < step / 2 + EPSILON) {
|
|
||||||
for (SupportNode* node : curr_layer_nodes) {
|
|
||||||
node->print_z = print_z;
|
|
||||||
node->height = step;
|
|
||||||
}
|
|
||||||
print_z += step;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// can't clear curr_layer_nodes, or the model will have empty layers
|
|
||||||
for (SupportNode* node : curr_layer_nodes) {
|
|
||||||
node->print_z = 0.0;
|
|
||||||
node->height = 0.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::vector<SupportNode *>> &contact_nodes)
|
std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::vector<SupportNode *>> &contact_nodes)
|
||||||
{
|
{
|
||||||
const PrintObjectConfig& config = m_object->config();
|
|
||||||
const PrintConfig & print_config = m_object->print()->config();
|
|
||||||
const coordf_t max_layer_height = m_slicing_params.max_layer_height;
|
const coordf_t max_layer_height = m_slicing_params.max_layer_height;
|
||||||
const coordf_t layer_height = config.layer_height.value;
|
const coordf_t layer_height = m_object_config->layer_height.value;
|
||||||
coordf_t z_distance_top = m_slicing_params.gap_support_object;
|
coordf_t z_distance_top = m_slicing_params.gap_support_object;
|
||||||
// BBS: add extra distance if thick bridge is enabled
|
// BBS: add extra distance if thick bridge is enabled
|
||||||
// Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height
|
// Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height
|
||||||
if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) {
|
if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) {
|
||||||
z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height;
|
z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(*m_print_config) - layer_height;
|
||||||
}
|
}
|
||||||
const size_t support_roof_layers = config.support_interface_top_layers.value;
|
const size_t support_roof_layers = m_object_config->support_interface_top_layers.value;
|
||||||
const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1;
|
const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1;
|
||||||
std::vector<LayerHeightData> layer_heights(contact_nodes.size());
|
std::vector<LayerHeightData> layer_heights(contact_nodes.size());
|
||||||
std::vector<int> bounds;
|
std::vector<int> bounds;
|
||||||
|
|
||||||
if (!config.tree_support_adaptive_layer_height || layer_height == max_layer_height || !print_config.independent_support_layer_height) {
|
if (!m_object_config->tree_support_adaptive_layer_height || layer_height == max_layer_height || !m_support_params.independent_layer_height) {
|
||||||
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
|
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
|
||||||
layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0};
|
layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0};
|
||||||
}
|
}
|
||||||
|
@ -3096,7 +3027,7 @@ std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::ve
|
||||||
return layer_heights;
|
return layer_heights;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes)
|
void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes, const std::vector<TreeSupport3D::SupportElements>& move_bounds)
|
||||||
{
|
{
|
||||||
const PrintObjectConfig &config = m_object->config();
|
const PrintObjectConfig &config = m_object->config();
|
||||||
const coordf_t point_spread = scale_(config.tree_support_branch_distance.value);
|
const coordf_t point_spread = scale_(config.tree_support_branch_distance.value);
|
||||||
|
@ -3144,7 +3075,6 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
|
||||||
if (m_object->layers().size() <= z_distance_top_layers + 1)
|
if (m_object->layers().size() <= z_distance_top_layers + 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_highest_overhang_layer = 0;
|
|
||||||
int nonempty_layers = 0;
|
int nonempty_layers = 0;
|
||||||
std::vector<Slic3r::Vec3f> all_nodes;
|
std::vector<Slic3r::Vec3f> all_nodes;
|
||||||
for (size_t layer_nr = 1; layer_nr < m_object->layers().size(); layer_nr++)
|
for (size_t layer_nr = 1; layer_nr < m_object->layers().size(); layer_nr++)
|
||||||
|
@ -3157,7 +3087,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
|
||||||
if (overhang.empty())
|
if (overhang.empty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
m_highest_overhang_layer = std::max(m_highest_overhang_layer, layer_nr);
|
std::unordered_set<Point, PointHash> already_inserted;
|
||||||
auto print_z = m_object->get_layer(layer_nr)->print_z;
|
auto print_z = m_object->get_layer(layer_nr)->print_z;
|
||||||
auto height = m_object->get_layer(layer_nr)->height;
|
auto height = m_object->get_layer(layer_nr)->height;
|
||||||
|
|
||||||
|
@ -3172,92 +3102,90 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
|
||||||
SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z,
|
SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z,
|
||||||
height, z_distance_top);
|
height, z_distance_top);
|
||||||
contact_node->type = ePolygon;
|
contact_node->type = ePolygon;
|
||||||
contact_node->overhang = &overhang_part;
|
contact_node->overhang = overhang_part;
|
||||||
curr_nodes.emplace_back(contact_node);
|
curr_nodes.emplace_back(contact_node);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
overhang_bounds.inflated(half_overhang_distance);
|
|
||||||
bool added = false; //Did we add a point this way?
|
|
||||||
for (Point candidate : grid_points)
|
|
||||||
{
|
|
||||||
if (overhang_bounds.contains(candidate))
|
|
||||||
{
|
|
||||||
// BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported!
|
|
||||||
bool is_inside = is_inside_ex(overhang_part, candidate);
|
|
||||||
if (!is_inside) {
|
|
||||||
constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that
|
|
||||||
// fall between overhang areas on constant surfaces.
|
|
||||||
move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance);
|
|
||||||
is_inside = is_inside_ex(overhang_part, candidate);
|
|
||||||
}
|
|
||||||
if (is_inside)
|
|
||||||
{
|
|
||||||
// collision radius has to be 0 or the supports are too few at curved slopes
|
|
||||||
//if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate))
|
|
||||||
{
|
|
||||||
constexpr bool to_buildplate = true;
|
|
||||||
SupportNode * contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate,
|
|
||||||
SupportNode::NO_PARENT, print_z, height, z_distance_top);
|
|
||||||
contact_node->overhang = &overhang_part;
|
|
||||||
curr_nodes.emplace_back(contact_node);
|
|
||||||
added = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported.
|
|
||||||
{
|
|
||||||
auto bbox = overhang_part.contour.bounding_box();
|
|
||||||
Points candidates;
|
|
||||||
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected)
|
|
||||||
candidates = {bbox.min, bounding_box_middle(bbox), bbox.max};
|
|
||||||
else
|
|
||||||
candidates = {bounding_box_middle(bbox)};
|
|
||||||
|
|
||||||
for (Point candidate : candidates) {
|
|
||||||
if (!overhang_part.contains(candidate))
|
|
||||||
move_inside_expoly(overhang_part, candidate);
|
|
||||||
constexpr bool to_buildplate = true;
|
|
||||||
SupportNode *contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, SupportNode::NO_PARENT,
|
|
||||||
print_z, height, z_distance_top);
|
|
||||||
contact_node->overhang = &overhang_part;
|
|
||||||
curr_nodes.emplace_back(contact_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add supports at corners for both auto and manual overhangs, github #2008
|
// add supports at corners for both auto and manual overhangs, github #2008
|
||||||
if (/*ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected*/1) {
|
if (/*ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected*/1) {
|
||||||
// add points at corners
|
// add points at corners
|
||||||
auto &points = overhang_part.contour.points;
|
auto& points = overhang_part.contour.points;
|
||||||
int nSize = points.size();
|
int nSize = points.size();
|
||||||
for (int i = 0; i < nSize; i++) {
|
for (int i = 0; i < nSize; i++) {
|
||||||
auto pt = points[i];
|
auto pt = points[i];
|
||||||
auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast<double>().normalized();
|
auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast<double>().normalized();
|
||||||
auto v2 = (pt - points[(i + 1) % nSize]).cast<double>().normalized();
|
auto v2 = (pt - points[(i + 1) % nSize]).cast<double>().normalized();
|
||||||
if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees
|
if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees
|
||||||
SupportNode *contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z,
|
SupportNode* contact_node = new SupportNode(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, SupportNode::NO_PARENT, print_z,
|
||||||
height, z_distance_top);
|
height, z_distance_top);
|
||||||
contact_node->overhang = &overhang_part;
|
contact_node->overhang = overhang_part;
|
||||||
contact_node->is_corner = true;
|
contact_node->is_corner = true;
|
||||||
curr_nodes.emplace_back(contact_node);
|
curr_nodes.emplace_back(contact_node);
|
||||||
|
|
||||||
|
Point hash_pos = pt / ((m_min_radius + 1) / 1);
|
||||||
|
already_inserted.emplace(hash_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto& move_bounds_layer = move_bounds[layer_nr];
|
||||||
|
if (move_bounds_layer.empty()) {
|
||||||
|
overhang_bounds.inflated(half_overhang_distance);
|
||||||
|
bool added = false; //Did we add a point this way?
|
||||||
|
for (Point candidate : grid_points) {
|
||||||
|
if (overhang_bounds.contains(candidate)) {
|
||||||
|
// BBS: move_inside_expoly shouldn't be used if candidate is already inside, as it moves point to boundary and the inside is not well supported!
|
||||||
|
bool is_inside = is_inside_ex(overhang_part, candidate);
|
||||||
|
if (!is_inside) {
|
||||||
|
constexpr coordf_t distance_inside = 0; // Move point towards the border of the polygon if it is closer than half the overhang distance: Catch points that
|
||||||
|
// fall between overhang areas on constant surfaces.
|
||||||
|
is_inside = move_inside_expoly(overhang_part, candidate, distance_inside, half_overhang_distance);
|
||||||
|
}
|
||||||
|
if (is_inside) {
|
||||||
|
// normalize the point a bit to also catch points which are so close that inserting it would achieve nothing
|
||||||
|
Point hash_pos = candidate / ((m_min_radius + 1) / 1);
|
||||||
|
if (!already_inserted.count(hash_pos)) {
|
||||||
|
already_inserted.emplace(hash_pos);
|
||||||
|
constexpr bool to_buildplate = true;
|
||||||
|
SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate,
|
||||||
|
SupportNode::NO_PARENT, print_z, height, z_distance_top);
|
||||||
|
contact_node->overhang=overhang_part;
|
||||||
|
curr_nodes.emplace_back(contact_node);
|
||||||
|
added = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported.
|
||||||
|
{
|
||||||
|
auto bbox = overhang_part.contour.bounding_box();
|
||||||
|
Points candidates;
|
||||||
|
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected)
|
||||||
|
candidates = { bbox.min, bounding_box_middle(bbox), bbox.max };
|
||||||
|
else
|
||||||
|
candidates = { bounding_box_middle(bbox) };
|
||||||
|
for (Point candidate : candidates) {
|
||||||
|
if (!overhang_part.contains(candidate))
|
||||||
|
move_inside_expoly(overhang_part, candidate);
|
||||||
|
constexpr bool to_buildplate = true;
|
||||||
|
SupportNode* contact_node = new SupportNode(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, SupportNode::NO_PARENT,
|
||||||
|
print_z, height, z_distance_top);
|
||||||
|
contact_node->overhang=overhang_part;
|
||||||
|
curr_nodes.emplace_back(contact_node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim) {
|
||||||
if(ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim){
|
// remove close points
|
||||||
// remove close points in Enforcers
|
|
||||||
// auto above_nodes = contact_nodes[layer_nr - 1];
|
|
||||||
if (!curr_nodes.empty() /*&& !above_nodes.empty()*/) {
|
|
||||||
for (auto it = curr_nodes.begin(); it != curr_nodes.end();) {
|
for (auto it = curr_nodes.begin(); it != curr_nodes.end();) {
|
||||||
bool is_duplicate = false;
|
bool is_duplicate = false;
|
||||||
if (!(*it)->is_corner) {
|
if (!(*it)->is_corner) {
|
||||||
Slic3r::Vec3f curr_pt((*it)->position(0), (*it)->position(1), scale_((*it)->print_z));
|
Slic3r::Vec3f curr_pt((*it)->position(0), (*it)->position(1), scale_((*it)->print_z));
|
||||||
for (auto &pt : all_nodes) {
|
for (auto& pt : all_nodes) {
|
||||||
auto dif = curr_pt - pt;
|
auto dif = curr_pt - pt;
|
||||||
if (dif.norm() < point_spread / 2) {
|
if (dif.norm() < point_spread / 2) {
|
||||||
delete (*it);
|
delete (*it);
|
||||||
it = curr_nodes.erase(it);
|
it = curr_nodes.erase(it);
|
||||||
is_duplicate = true;
|
is_duplicate = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3267,6 +3195,14 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<SupportNode*>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
for (auto& elem:move_bounds_layer) {
|
||||||
|
SupportNode* contact_node = new SupportNode(elem.state.result_on_layer, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, elem.state.to_buildplate,
|
||||||
|
SupportNode::NO_PARENT, print_z, height, z_distance_top);
|
||||||
|
contact_node->overhang = ExPolygon(elem.influence_area.front());
|
||||||
|
curr_nodes.emplace_back(contact_node);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!curr_nodes.empty()) nonempty_layers++;
|
if (!curr_nodes.empty()) nonempty_layers++;
|
||||||
for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); }
|
for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); }
|
||||||
|
@ -3395,6 +3331,7 @@ const ExPolygons& TreeSupportData::calculate_collision(const RadiusLayerPair& ke
|
||||||
assert(key.layer_nr < m_layer_outlines.size());
|
assert(key.layer_nr < m_layer_outlines.size());
|
||||||
|
|
||||||
ExPolygons collision_areas = offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius));
|
ExPolygons collision_areas = offset_ex(m_layer_outlines[key.layer_nr], scale_(key.radius));
|
||||||
|
collision_areas = expolygons_simplify(collision_areas, scale_(m_radius_sample_resolution));
|
||||||
const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) });
|
const auto ret = m_collision_cache.insert({ key, std::move(collision_areas) });
|
||||||
return ret.first->second;
|
return ret.first->second;
|
||||||
}
|
}
|
||||||
|
@ -3434,7 +3371,7 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke
|
||||||
//assert(ret.second);
|
//assert(ret.second);
|
||||||
return ret.first->second;
|
return ret.first->second;
|
||||||
} else {
|
} else {
|
||||||
ExPolygons avoidance_areas = offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius));
|
ExPolygons avoidance_areas = get_collision(radius, layer_nr);// std::move(offset_ex(m_layer_outlines_below[layer_nr], scale_(m_xy_distance + radius)));
|
||||||
auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) });
|
auto ret = m_avoidance_cache.insert({ key, std::move(avoidance_areas) });
|
||||||
assert(ret.second);
|
assert(ret.second);
|
||||||
return ret.first->second;
|
return ret.first->second;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#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"
|
#include "TreeSupport3D.hpp"
|
||||||
|
|
||||||
#ifndef SQ
|
#ifndef SQ
|
||||||
|
@ -60,7 +61,6 @@ struct SupportNode
|
||||||
, position(Point(0, 0))
|
, position(Point(0, 0))
|
||||||
, obj_layer_nr(0)
|
, obj_layer_nr(0)
|
||||||
, support_roof_layers_below(0)
|
, support_roof_layers_below(0)
|
||||||
, support_floor_layers_above(0)
|
|
||||||
, to_buildplate(true)
|
, to_buildplate(true)
|
||||||
, parent(nullptr)
|
, parent(nullptr)
|
||||||
, print_z(0.0)
|
, print_z(0.0)
|
||||||
|
@ -74,7 +74,6 @@ struct SupportNode
|
||||||
, position(position)
|
, position(position)
|
||||||
, obj_layer_nr(obj_layer_nr)
|
, obj_layer_nr(obj_layer_nr)
|
||||||
, support_roof_layers_below(support_roof_layers_below)
|
, support_roof_layers_below(support_roof_layers_below)
|
||||||
, support_floor_layers_above(0)
|
|
||||||
, to_buildplate(to_buildplate)
|
, to_buildplate(to_buildplate)
|
||||||
, parent(parent)
|
, parent(parent)
|
||||||
, print_z(print_z_)
|
, print_z(print_z_)
|
||||||
|
@ -82,13 +81,16 @@ struct SupportNode
|
||||||
, dist_mm_to_top(dist_mm_to_top_)
|
, dist_mm_to_top(dist_mm_to_top_)
|
||||||
{
|
{
|
||||||
if (parent) {
|
if (parent) {
|
||||||
|
parents.push_back(parent);
|
||||||
type = parent->type;
|
type = parent->type;
|
||||||
overhang = parent->overhang;
|
overhang = parent->overhang;
|
||||||
if (dist_mm_to_top==0)
|
if (dist_mm_to_top==0)
|
||||||
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
|
dist_mm_to_top = parent->dist_mm_to_top + parent->height;
|
||||||
parent->child = this;
|
parent->child = this;
|
||||||
for (auto& neighbor : parent->merged_neighbours)
|
for (auto& neighbor : parent->merged_neighbours) {
|
||||||
neighbor->child = this;
|
neighbor->child = this;
|
||||||
|
parents.push_back(neighbor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,10 +117,10 @@ struct SupportNode
|
||||||
mutable double radius = 0.0;
|
mutable double radius = 0.0;
|
||||||
mutable double max_move_dist = 0.0;
|
mutable double max_move_dist = 0.0;
|
||||||
TreeNodeType type = eCircle;
|
TreeNodeType type = eCircle;
|
||||||
bool is_merged = false; // this node is generated by merging upper nodes
|
|
||||||
bool is_corner = false;
|
bool is_corner = false;
|
||||||
bool is_processed = false;
|
bool is_processed = false;
|
||||||
const ExPolygon *overhang = nullptr; // when type==ePolygon, set this value to get original overhang area
|
bool need_extra_wall = false;
|
||||||
|
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.
|
* \brief The direction of the skin lines above the tip of the branch.
|
||||||
|
@ -137,7 +139,6 @@ struct SupportNode
|
||||||
* how far we need to extend that support roof downwards.
|
* how far we need to extend that support roof downwards.
|
||||||
*/
|
*/
|
||||||
int support_roof_layers_below;
|
int support_roof_layers_below;
|
||||||
int support_floor_layers_above;
|
|
||||||
int obj_layer_nr;
|
int obj_layer_nr;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -157,6 +158,7 @@ struct SupportNode
|
||||||
* the entire branch needs to be known.
|
* the entire branch needs to be known.
|
||||||
*/
|
*/
|
||||||
SupportNode* parent;
|
SupportNode* parent;
|
||||||
|
std::vector<SupportNode*> parents;
|
||||||
SupportNode* child = nullptr;
|
SupportNode* child = nullptr;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -391,6 +393,8 @@ private:
|
||||||
* \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::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;
|
||||||
|
@ -400,10 +404,12 @@ private:
|
||||||
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;
|
||||||
|
float DO_NOT_MOVER_UNDER_MM = 0.0;
|
||||||
coordf_t MAX_BRANCH_RADIUS = 10.0;
|
coordf_t MAX_BRANCH_RADIUS = 10.0;
|
||||||
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
|
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
|
||||||
coordf_t MIN_BRANCH_RADIUS = 0.5;
|
coordf_t MIN_BRANCH_RADIUS = 0.5;
|
||||||
float tree_support_branch_diameter_angle = 5.0;
|
float tree_support_branch_diameter_angle = 5.0;
|
||||||
|
coord_t m_min_radius = scale_(1); // in mm
|
||||||
bool is_strong = false;
|
bool is_strong = false;
|
||||||
bool is_slim = false;
|
bool is_slim = false;
|
||||||
bool with_infill = false;
|
bool with_infill = false;
|
||||||
|
@ -438,8 +444,6 @@ private:
|
||||||
|
|
||||||
void smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_nodes);
|
void smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_nodes);
|
||||||
|
|
||||||
void adjust_layer_heights(std::vector<std::vector<SupportNode*>>& 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
|
||||||
*
|
*
|
||||||
|
@ -459,7 +463,7 @@ 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<SupportNode*>>& contact_nodes);
|
void generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes, const std::vector<TreeSupport3D::SupportElements>& move_bounds);
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Add a node to the next layer.
|
* \brief Add a node to the next layer.
|
||||||
|
@ -470,7 +474,11 @@ private:
|
||||||
void create_tree_support_layers();
|
void create_tree_support_layers();
|
||||||
void generate_toolpaths();
|
void generate_toolpaths();
|
||||||
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);
|
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true);
|
||||||
|
ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr);
|
||||||
|
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(
|
||||||
|
|
|
@ -139,6 +139,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
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue