WIP TreeSupports: Now it is possible to switch between the normal
and the "organic" supports.
This commit is contained in:
parent
009fe1cab4
commit
2365b3a8dd
6 changed files with 216 additions and 51 deletions
|
@ -138,7 +138,8 @@ CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern)
|
||||||
static const t_config_enum_values s_keys_map_SupportMaterialStyle {
|
static const t_config_enum_values s_keys_map_SupportMaterialStyle {
|
||||||
{ "grid", smsGrid },
|
{ "grid", smsGrid },
|
||||||
{ "snug", smsSnug },
|
{ "snug", smsSnug },
|
||||||
{ "tree", smsTree }
|
{ "tree", smsTree },
|
||||||
|
{ "organic", smsOrganic }
|
||||||
};
|
};
|
||||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
|
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialStyle)
|
||||||
|
|
||||||
|
@ -2782,12 +2783,12 @@ void PrintConfigDef::init_fff_params()
|
||||||
"will create more stable supports, while snug support towers will save material and reduce "
|
"will create more stable supports, while snug support towers will save material and reduce "
|
||||||
"object scarring.");
|
"object scarring.");
|
||||||
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values();
|
||||||
def->enum_values.push_back("grid");
|
def->set_enum_values({
|
||||||
def->enum_values.push_back("snug");
|
{ "grid", L("Grid") },
|
||||||
def->enum_values.push_back("tree");
|
{ "snug", L("Snug") },
|
||||||
def->enum_labels.push_back(L("Grid"));
|
{ "tree", L("Tree") },
|
||||||
def->enum_labels.push_back(L("Snug"));
|
{ "organic", L("Organic") }
|
||||||
def->enum_labels.push_back(L("Tree"));
|
});
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
|
def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid));
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ enum SupportMaterialPattern {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SupportMaterialStyle {
|
enum SupportMaterialStyle {
|
||||||
smsGrid, smsSnug, smsTree,
|
smsGrid, smsSnug, smsTree, smsOrganic,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SupportMaterialInterfacePattern {
|
enum SupportMaterialInterfacePattern {
|
||||||
|
|
|
@ -2197,7 +2197,7 @@ void PrintObject::combine_infill()
|
||||||
|
|
||||||
void PrintObject::_generate_support_material()
|
void PrintObject::_generate_support_material()
|
||||||
{
|
{
|
||||||
if (m_config.support_material_style == smsTree) {
|
if (m_config.support_material_style == smsTree || m_config.support_material_style == smsOrganic) {
|
||||||
fff_tree_support_generate(*this, std::function<void()>([this](){ this->throw_if_canceled(); }));
|
fff_tree_support_generate(*this, std::function<void()>([this](){ this->throw_if_canceled(); }));
|
||||||
} else {
|
} else {
|
||||||
PrintObjectSupportMaterial support_material(this, m_slicing_params);
|
PrintObjectSupportMaterial support_material(this, m_slicing_params);
|
||||||
|
|
|
@ -800,6 +800,7 @@ public:
|
||||||
{
|
{
|
||||||
switch (m_style) {
|
switch (m_style) {
|
||||||
case smsTree:
|
case smsTree:
|
||||||
|
case smsOrganic:
|
||||||
assert(false);
|
assert(false);
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case smsGrid:
|
case smsGrid:
|
||||||
|
|
|
@ -39,7 +39,7 @@ TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &pr
|
||||||
|
|
||||||
// Support must be enabled and set to Tree style.
|
// Support must be enabled and set to Tree style.
|
||||||
assert(config.support_material);
|
assert(config.support_material);
|
||||||
assert(config.support_material_style == smsTree);
|
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.;
|
||||||
|
|
|
@ -211,7 +211,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
// Support must be enabled and set to Tree style.
|
// Support must be enabled and set to Tree style.
|
||||||
assert(object_config.support_material);
|
assert(object_config.support_material);
|
||||||
assert(object_config.support_material_style == smsTree);
|
assert(object_config.support_material_style == smsTree || object_config.support_material_style == smsOrganic);
|
||||||
|
|
||||||
bool found_existing_group = false;
|
bool found_existing_group = false;
|
||||||
TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object } };
|
TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object } };
|
||||||
|
@ -330,18 +330,39 @@ void tree_supports_show_error(std::string message, bool critical)
|
||||||
if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty()))
|
if (! (enforced_layer || blockers_layers.empty() || blockers_layers[layer_id].empty()))
|
||||||
overhangs = diff(overhangs, blockers_layers[layer_id], ApplySafetyOffset::Yes);
|
overhangs = diff(overhangs, blockers_layers[layer_id], ApplySafetyOffset::Yes);
|
||||||
}
|
}
|
||||||
if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty())
|
//check_self_intersections(overhangs, "generate_overhangs1");
|
||||||
|
if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) {
|
||||||
// Has some support enforcers at this layer, apply them to the overhangs, don't apply the support threshold angle.
|
// Has some support enforcers at this layer, apply them to the overhangs, don't apply the support threshold angle.
|
||||||
if (Polygons enforced_overhangs = intersection(raw_overhangs_calculated ? raw_overhangs : diff(current_layer.lslices, lower_layer.lslices), enforcers_layers[layer_id]);
|
//enforcers_layers[layer_id] = union_(enforcers_layers[layer_id]);
|
||||||
|
//check_self_intersections(enforcers_layers[layer_id], "generate_overhangs - enforcers");
|
||||||
|
//check_self_intersections(to_polygons(lower_layer.lslices), "generate_overhangs - lowerlayers");
|
||||||
|
if (Polygons enforced_overhangs = intersection(raw_overhangs_calculated ? raw_overhangs : diff(current_layer.lslices, lower_layer.lslices), enforcers_layers[layer_id] /*, ApplySafetyOffset::Yes */);
|
||||||
! enforced_overhangs.empty()) {
|
! enforced_overhangs.empty()) {
|
||||||
//FIXME this is a hack to make enforcers work on steep overhangs.
|
//FIXME this is a hack to make enforcers work on steep overhangs.
|
||||||
enforced_overhangs = diff(offset(enforced_overhangs,
|
//check_self_intersections(enforced_overhangs, "generate_overhangs - enforced overhangs1");
|
||||||
|
//Polygons enforced_overhangs_prev = enforced_overhangs;
|
||||||
|
//check_self_intersections(to_polygons(union_ex(enforced_overhangs)), "generate_overhangs - enforced overhangs11");
|
||||||
|
//check_self_intersections(offset(union_ex(enforced_overhangs),
|
||||||
|
//FIXME this is a fudge constant!
|
||||||
|
// scaled<float>(0.4)), "generate_overhangs - enforced overhangs12");
|
||||||
|
enforced_overhangs = diff(offset(union_ex(enforced_overhangs),
|
||||||
//FIXME this is a fudge constant!
|
//FIXME this is a fudge constant!
|
||||||
scaled<float>(0.4)),
|
scaled<float>(0.4)),
|
||||||
lower_layer.lslices);
|
lower_layer.lslices);
|
||||||
|
#ifdef TREESUPPORT_DEBUG_SVG
|
||||||
|
if (! intersecting_edges(enforced_overhangs).empty()) {
|
||||||
|
static int irun = 0;
|
||||||
|
SVG::export_expolygons(debug_out_path("treesupport-self-intersections-%d.svg", ++irun),
|
||||||
|
{ { { union_ex(enforced_overhangs_prev) }, { "prev", "yellow", 0.5f } },
|
||||||
|
{ { lower_layer.lslices }, { "lower_layer.lslices", "gray", 0.5f } },
|
||||||
|
{ { union_ex(enforced_overhangs) }, { "enforced_overhangs", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||||
|
}
|
||||||
|
#endif // TREESUPPORT_DEBUG_SVG
|
||||||
|
//check_self_intersections(enforced_overhangs, "generate_overhangs - enforced overhangs2");
|
||||||
overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
|
overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
|
||||||
|
//check_self_intersections(overhangs, "generate_overhangs - enforcers");
|
||||||
}
|
}
|
||||||
check_self_intersections(overhangs, "generate_overhangs");
|
}
|
||||||
out[layer_id] = std::move(overhangs);
|
out[layer_id] = std::move(overhangs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1106,7 +1127,7 @@ static void generate_initial_areas(
|
||||||
overhang_regular = mesh_group_settings.support_offset == 0 ?
|
overhang_regular = mesh_group_settings.support_offset == 0 ?
|
||||||
overhang_raw :
|
overhang_raw :
|
||||||
safe_offset_inc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, mesh_config.min_radius * 1.75 + mesh_config.xy_min_distance, 0, 1);
|
safe_offset_inc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, mesh_config.min_radius * 1.75 + mesh_config.xy_min_distance, 0, 1);
|
||||||
check_self_intersections(overhang_regular, "overhang_regular1");
|
//check_self_intersections(overhang_regular, "overhang_regular1");
|
||||||
|
|
||||||
// offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang
|
// offset ensures that areas that could be supported by a part of a support line, are not considered unsupported overhang
|
||||||
Polygons remaining_overhang = intersection(
|
Polygons remaining_overhang = intersection(
|
||||||
|
@ -1134,7 +1155,7 @@ static void generate_initial_areas(
|
||||||
remaining_overhang = diff(remaining_overhang, safe_offset_inc(overhang_regular, 1.5 * extra_total_offset_acc, raw_collision, offset_step, 0, 1));
|
remaining_overhang = diff(remaining_overhang, safe_offset_inc(overhang_regular, 1.5 * extra_total_offset_acc, raw_collision, offset_step, 0, 1));
|
||||||
// Extending the overhangs by the inflated remaining overhangs.
|
// Extending the overhangs by the inflated remaining overhangs.
|
||||||
overhang_regular = union_(overhang_regular, diff(safe_offset_inc(remaining_overhang, extra_total_offset_acc, raw_collision, offset_step, 0, 1), relevant_forbidden));
|
overhang_regular = union_(overhang_regular, diff(safe_offset_inc(remaining_overhang, extra_total_offset_acc, raw_collision, offset_step, 0, 1), relevant_forbidden));
|
||||||
check_self_intersections(overhang_regular, "overhang_regular2");
|
//check_self_intersections(overhang_regular, "overhang_regular2");
|
||||||
}
|
}
|
||||||
// If the xy distance overrides the z distance, some support needs to be inserted further down.
|
// If the xy distance overrides the z distance, some support needs to be inserted further down.
|
||||||
//=> Analyze which support points do not fit on this layer and check if they will fit a few layers down (while adding them an infinite amount of layers down would technically be closer the the setting description, it would not produce reasonable results. )
|
//=> Analyze which support points do not fit on this layer and check if they will fit a few layers down (while adding them an infinite amount of layers down would technically be closer the the setting description, it would not produce reasonable results. )
|
||||||
|
@ -1186,7 +1207,7 @@ static void generate_initial_areas(
|
||||||
if (mesh_group_settings.minimum_support_area > 0)
|
if (mesh_group_settings.minimum_support_area > 0)
|
||||||
remove_small(overhang_roofs, mesh_group_settings.minimum_roof_area);
|
remove_small(overhang_roofs, mesh_group_settings.minimum_roof_area);
|
||||||
overhang_regular = diff(overhang_regular, overhang_roofs, ApplySafetyOffset::Yes);
|
overhang_regular = diff(overhang_regular, overhang_roofs, ApplySafetyOffset::Yes);
|
||||||
check_self_intersections(overhang_regular, "overhang_regular3");
|
//check_self_intersections(overhang_regular, "overhang_regular3");
|
||||||
for (ExPolygon &roof_part : union_ex(overhang_roofs))
|
for (ExPolygon &roof_part : union_ex(overhang_roofs))
|
||||||
overhang_processing.emplace_back(std::move(roof_part), true);
|
overhang_processing.emplace_back(std::move(roof_part), true);
|
||||||
}
|
}
|
||||||
|
@ -2397,6 +2418,8 @@ static void set_points_on_areas(const SupportElement &elem, SupportElements *lay
|
||||||
next_elem.state.result_on_layer = move_inside_if_outside(next_elem.influence_area, elem.state.result_on_layer);
|
next_elem.state.result_on_layer = move_inside_if_outside(next_elem.influence_area, elem.state.result_on_layer);
|
||||||
// do not call recursive because then amount of layers would be restricted by the stack size
|
// do not call recursive because then amount of layers would be restricted by the stack size
|
||||||
}
|
}
|
||||||
|
// Mark the parent element as accessed from a valid child element.
|
||||||
|
next_elem.state.marked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2515,15 +2538,23 @@ static void create_nodes_from_area(
|
||||||
{
|
{
|
||||||
// Initialize points on layer 0, with a "random" point in the influence area.
|
// Initialize points on layer 0, with a "random" point in the influence area.
|
||||||
// Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result.
|
// Point is chosen based on an inaccurate estimate where the branches will split into two, but every point inside the influence area would produce a valid result.
|
||||||
for (SupportElement &init : move_bounds.front()) {
|
{
|
||||||
init.state.result_on_layer = move_inside_if_outside(init.influence_area, init.state.next_position);
|
SupportElements *layer_above = move_bounds.size() > 1 ? &move_bounds[1] : nullptr;
|
||||||
// Also set the parent nodes, as these will be required for the first iteration of the loop below.
|
for (SupportElement &elem : *layer_above)
|
||||||
set_points_on_areas(init, move_bounds.size() > 1 ? &move_bounds[1] : nullptr);
|
elem.state.marked = false;
|
||||||
|
for (SupportElement &init : move_bounds.front()) {
|
||||||
|
init.state.result_on_layer = move_inside_if_outside(init.influence_area, init.state.next_position);
|
||||||
|
// Also set the parent nodes, as these will be required for the first iteration of the loop below and mark the parent nodes.
|
||||||
|
set_points_on_areas(init, layer_above);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
for (LayerIndex layer_idx = 1; layer_idx < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||||
auto &layer = move_bounds[layer_idx];
|
auto &layer = move_bounds[layer_idx];
|
||||||
auto *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr;
|
auto *layer_above = layer_idx + 1 < move_bounds.size() ? &move_bounds[layer_idx + 1] : nullptr;
|
||||||
|
if (layer_above)
|
||||||
|
for (SupportElement &elem : *layer_above)
|
||||||
|
elem.state.marked = false;
|
||||||
for (SupportElement &elem : layer) {
|
for (SupportElement &elem : layer) {
|
||||||
assert(! elem.state.deleted);
|
assert(! elem.state.deleted);
|
||||||
assert(elem.state.layer_idx == layer_idx);
|
assert(elem.state.layer_idx == layer_idx);
|
||||||
|
@ -2537,11 +2568,6 @@ static void create_nodes_from_area(
|
||||||
}
|
}
|
||||||
// we dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set
|
// we dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set
|
||||||
elem.state.deleted = true;
|
elem.state.deleted = true;
|
||||||
for (int32_t parent_idx : elem.parents)
|
|
||||||
// When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set.
|
|
||||||
// As this branch needs to be removed => all parents result_on_layer have to be invalidated.
|
|
||||||
(*layer_above)[parent_idx].state.result_on_layer_reset();
|
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
// set the point where the branch will be placed on the model
|
// set the point where the branch will be placed on the model
|
||||||
if (elem.state.to_model_gracious)
|
if (elem.state.to_model_gracious)
|
||||||
|
@ -2550,13 +2576,67 @@ static void create_nodes_from_area(
|
||||||
set_to_model_contact_simple(elem);
|
set_to_model_contact_simple(elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! elem.state.deleted)
|
if (! elem.state.deleted && ! elem.state.marked && elem.state.target_height == layer_idx)
|
||||||
// element is valid now setting points in the layer above
|
// Just a tip surface with no supporting element.
|
||||||
|
elem.state.deleted = true;
|
||||||
|
if (elem.state.deleted) {
|
||||||
|
for (int32_t parent_idx : elem.parents)
|
||||||
|
// When the roof was not able to generate downwards enough, the top elements may have not moved, and have result_on_layer already set.
|
||||||
|
// As this branch needs to be removed => all parents result_on_layer have to be invalidated.
|
||||||
|
(*layer_above)[parent_idx].state.result_on_layer_reset();
|
||||||
|
}
|
||||||
|
if (! elem.state.deleted) {
|
||||||
|
// Element is valid now setting points in the layer above and mark the parent nodes.
|
||||||
set_points_on_areas(elem, layer_above);
|
set_points_on_areas(elem, layer_above);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Verify the tree connectivity including the branch slopes.
|
||||||
|
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||||
|
auto &layer = move_bounds[layer_idx];
|
||||||
|
auto &above = move_bounds[layer_idx + 1];
|
||||||
|
for (SupportElement &elem : layer)
|
||||||
|
if (! elem.state.deleted) {
|
||||||
|
for (int32_t iparent : elem.parents) {
|
||||||
|
SupportElement &parent = above[iparent];
|
||||||
|
assert(! parent.state.deleted);
|
||||||
|
assert(elem.state.result_on_layer_is_set() == parent.state.result_on_layer_is_set());
|
||||||
|
if (elem.state.result_on_layer_is_set()) {
|
||||||
|
double radius_increase = config.getRadius(elem.state) - config.getRadius(parent.state);
|
||||||
|
assert(radius_increase >= 0);
|
||||||
|
double shift = (elem.state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
|
||||||
|
assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
remove_deleted_elements(move_bounds);
|
remove_deleted_elements(move_bounds);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Verify the tree connectivity including the branch slopes.
|
||||||
|
for (LayerIndex layer_idx = 0; layer_idx + 1 < LayerIndex(move_bounds.size()); ++ layer_idx) {
|
||||||
|
auto &layer = move_bounds[layer_idx];
|
||||||
|
auto &above = move_bounds[layer_idx + 1];
|
||||||
|
for (SupportElement &elem : layer) {
|
||||||
|
assert(! elem.state.deleted);
|
||||||
|
for (int32_t iparent : elem.parents) {
|
||||||
|
SupportElement &parent = above[iparent];
|
||||||
|
assert(! parent.state.deleted);
|
||||||
|
assert(elem.state.result_on_layer_is_set() == parent.state.result_on_layer_is_set());
|
||||||
|
if (elem.state.result_on_layer_is_set()) {
|
||||||
|
double radius_increase = config.getRadius(elem.state) - config.getRadius(parent.state);
|
||||||
|
assert(radius_increase >= 0);
|
||||||
|
double shift = (elem.state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
|
||||||
|
assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NDEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
// For producing circular / elliptical areas from SupportElements (one DrawArea per one SupportElement)
|
// For producing circular / elliptical areas from SupportElements (one DrawArea per one SupportElement)
|
||||||
|
@ -2677,7 +2757,8 @@ static void generate_branch_areas(const TreeModelVolumes &volumes, const TreeSup
|
||||||
polygons_with_correct_center.emplace_back(std::move(part));
|
polygons_with_correct_center.emplace_back(std::move(part));
|
||||||
}
|
}
|
||||||
// Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above.
|
// Increase the area again, to ensure the nozzle path when calculated later is very similar to the one assumed above.
|
||||||
polygons = diff_clipped(offset(polygons_with_correct_center, config.support_line_width / 2, jtMiter, 1.2),
|
assert(contains(polygons, draw_area.element->state.result_on_layer));
|
||||||
|
polygons = diff_clipped(offset(polygons_with_correct_center, config.support_line_width / 2, jtMiter, 1.2),
|
||||||
//FIXME Vojtech: Clipping may split the region into multiple pieces again, reversing the fixing effort.
|
//FIXME Vojtech: Clipping may split the region into multiple pieces again, reversing the fixing effort.
|
||||||
collision);
|
collision);
|
||||||
}
|
}
|
||||||
|
@ -2723,10 +2804,12 @@ static void smooth_branch_areas(
|
||||||
[&](const tbb::blocked_range<size_t> &range) {
|
[&](const tbb::blocked_range<size_t> &range) {
|
||||||
for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) {
|
for (size_t processing_idx = range.begin(); processing_idx < range.end(); ++ processing_idx) {
|
||||||
DrawArea &draw_area = linear_data[processing_base + processing_idx];
|
DrawArea &draw_area = linear_data[processing_base + processing_idx];
|
||||||
|
assert(draw_area.element->state.layer_idx == layer_idx);
|
||||||
double max_outer_wall_distance = 0;
|
double max_outer_wall_distance = 0;
|
||||||
bool do_something = false;
|
bool do_something = false;
|
||||||
for (int32_t parent_idx : draw_area.element->parents) {
|
for (int32_t parent_idx : draw_area.element->parents) {
|
||||||
const SupportElement &parent = layer_above[parent_idx];
|
const SupportElement &parent = layer_above[parent_idx];
|
||||||
|
assert(parent.state.layer_idx == layer_idx + 1);
|
||||||
if (config.getRadius(parent.state) != config.getCollisionRadius(parent.state)) {
|
if (config.getRadius(parent.state) != config.getCollisionRadius(parent.state)) {
|
||||||
do_something = true;
|
do_something = true;
|
||||||
max_outer_wall_distance = std::max(max_outer_wall_distance, (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm() - (config.getRadius(*draw_area.element) - config.getRadius(parent)));
|
max_outer_wall_distance = std::max(max_outer_wall_distance, (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm() - (config.getRadius(*draw_area.element) - config.getRadius(parent)));
|
||||||
|
@ -2734,14 +2817,35 @@ static void smooth_branch_areas(
|
||||||
}
|
}
|
||||||
max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers.
|
max_outer_wall_distance += max_radius_change_per_layer; // As this change is a bit larger than what usually appears, lost radius can be slowly reclaimed over the layers.
|
||||||
if (do_something) {
|
if (do_something) {
|
||||||
|
assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
|
||||||
Polygons max_allowed_area = offset(draw_area.polygons, float(max_outer_wall_distance), jtMiter, 1.2);
|
Polygons max_allowed_area = offset(draw_area.polygons, float(max_outer_wall_distance), jtMiter, 1.2);
|
||||||
for (int32_t parent_idx : draw_area.element->parents) {
|
for (int32_t parent_idx : draw_area.element->parents) {
|
||||||
const SupportElement &parent = layer_above[parent_idx];
|
const SupportElement &parent = layer_above[parent_idx];
|
||||||
|
#ifndef NDEBUG
|
||||||
|
assert(parent.state.layer_idx == layer_idx + 1);
|
||||||
|
assert(contains(linear_data[processing_base_above + parent_idx].polygons, parent.state.result_on_layer));
|
||||||
|
double radius_increase = config.getRadius(draw_area.element->state) - config.getRadius(parent.state);
|
||||||
|
assert(radius_increase >= 0);
|
||||||
|
double shift = (draw_area.element->state.result_on_layer - parent.state.result_on_layer).cast<double>().norm();
|
||||||
|
assert(shift < radius_increase + 2. * config.maximum_move_distance_slow);
|
||||||
|
#endif // NDEBUG
|
||||||
if (config.getRadius(parent.state) != config.getCollisionRadius(parent.state)) {
|
if (config.getRadius(parent.state) != config.getCollisionRadius(parent.state)) {
|
||||||
// No other element on this layer than the current one may be connected to &parent,
|
// No other element on this layer than the current one may be connected to &parent,
|
||||||
// thus it is safe to update parent's DrawArea directly.
|
// thus it is safe to update parent's DrawArea directly.
|
||||||
Polygons &dst = linear_data[processing_base_above + parent_idx].polygons;
|
Polygons &dst = linear_data[processing_base_above + parent_idx].polygons;
|
||||||
dst = intersection(dst, max_allowed_area);
|
// Polygons orig = dst;
|
||||||
|
if (! dst.empty()) {
|
||||||
|
dst = intersection(dst, max_allowed_area);
|
||||||
|
#if 0
|
||||||
|
if (dst.empty()) {
|
||||||
|
static int irun = 0;
|
||||||
|
SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-error-%d.svg", irun ++),
|
||||||
|
{ { { union_ex(max_allowed_area) }, { "max_allowed_area", "yellow", 0.5f } },
|
||||||
|
{ { union_ex(orig) }, { "orig", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||||
|
::MessageBoxA(nullptr, "TreeSupport smoothing bug", "Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3010,9 +3114,7 @@ static void draw_areas(
|
||||||
// Only one link points to a node above from below.
|
// Only one link points to a node above from below.
|
||||||
assert(! (++ it != map_downwards_old.end() && it->first == &elem));
|
assert(! (++ it != map_downwards_old.end() && it->first == &elem));
|
||||||
}
|
}
|
||||||
if ((! child && elem.state.target_height == layer_idx) || (child && !child->state.result_on_layer_is_set()))
|
assert(child ? child->state.result_on_layer_is_set() : elem.state.target_height > layer_idx);
|
||||||
// We either come from nowhere at the final layer or we had invalid parents 2. should never happen but just to be sure
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
for (int32_t parent_idx : elem.parents) {
|
for (int32_t parent_idx : elem.parents) {
|
||||||
SupportElement &parent = (*layer_above)[parent_idx];
|
SupportElement &parent = (*layer_above)[parent_idx];
|
||||||
|
@ -3026,12 +3128,66 @@ static void draw_areas(
|
||||||
linear_data_layers.emplace_back(linear_data.size());
|
linear_data_layers.emplace_back(linear_data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
for (size_t i = 0; i < move_bounds.size(); ++ i) {
|
||||||
|
size_t begin = linear_data_layers[i];
|
||||||
|
size_t end = linear_data_layers[i + 1];
|
||||||
|
for (size_t j = begin; j < end; ++ j)
|
||||||
|
assert(linear_data[j].element == &move_bounds[i][j - begin]);
|
||||||
|
}
|
||||||
|
#endif // NDEBUG
|
||||||
|
|
||||||
auto t_start = std::chrono::high_resolution_clock::now();
|
auto t_start = std::chrono::high_resolution_clock::now();
|
||||||
// Generate the circles that will be the branches.
|
// Generate the circles that will be the branches.
|
||||||
generate_branch_areas(volumes, config, move_bounds, linear_data);
|
generate_branch_areas(volumes, config, move_bounds, linear_data);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
assert(linear_data_layers.size() == move_bounds.size() + 1);
|
||||||
|
for (const auto &draw_area : linear_data)
|
||||||
|
assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
|
||||||
|
for (size_t i = 0; i < move_bounds.size(); ++ i) {
|
||||||
|
size_t begin = linear_data_layers[i];
|
||||||
|
size_t end = linear_data_layers[i + 1];
|
||||||
|
for (size_t j = begin; j < end; ++ j) {
|
||||||
|
const auto &draw_area = linear_data[j];
|
||||||
|
assert(draw_area.element == &move_bounds[i][j - begin]);
|
||||||
|
assert(contains(draw_area.polygons, draw_area.element->state.result_on_layer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++ area_layer_idx) {
|
||||||
|
size_t begin = linear_data_layers[area_layer_idx];
|
||||||
|
size_t end = linear_data_layers[area_layer_idx + 1];
|
||||||
|
Polygons polygons;
|
||||||
|
for (size_t area_idx = begin; area_idx < end; ++ area_idx) {
|
||||||
|
DrawArea &area = linear_data[area_idx];
|
||||||
|
append(polygons, area.polygons);
|
||||||
|
}
|
||||||
|
SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-raw-%d.svg", area_layer_idx),
|
||||||
|
{ { { union_ex(polygons) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto t_generate = std::chrono::high_resolution_clock::now();
|
auto t_generate = std::chrono::high_resolution_clock::now();
|
||||||
// In some edgecases a branch may go though a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out.
|
// In some edgecases a branch may go though a hole, where the regular radius does not fit. This can result in an apparent jump in branch radius. As such this cases need to be caught and smoothed out.
|
||||||
smooth_branch_areas(config, move_bounds, linear_data, linear_data_layers);
|
smooth_branch_areas(config, move_bounds, linear_data, linear_data_layers);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
for (size_t area_layer_idx = 0; area_layer_idx + 1 < linear_data_layers.size(); ++area_layer_idx) {
|
||||||
|
size_t begin = linear_data_layers[area_layer_idx];
|
||||||
|
size_t end = linear_data_layers[area_layer_idx + 1];
|
||||||
|
Polygons polygons;
|
||||||
|
for (size_t area_idx = begin; area_idx < end; ++area_idx) {
|
||||||
|
DrawArea& area = linear_data[area_idx];
|
||||||
|
append(polygons, area.polygons);
|
||||||
|
}
|
||||||
|
SVG::export_expolygons(debug_out_path("treesupport-extrude_areas-smooth-%d.svg", area_layer_idx),
|
||||||
|
{ { { union_ex(polygons) }, { "parent", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
auto t_smooth = std::chrono::high_resolution_clock::now();
|
auto t_smooth = std::chrono::high_resolution_clock::now();
|
||||||
// drop down all trees that connect non gracefully with the model
|
// drop down all trees that connect non gracefully with the model
|
||||||
drop_non_gracious_areas(volumes, linear_data, support_layer_storage);
|
drop_non_gracious_areas(volumes, linear_data, support_layer_storage);
|
||||||
|
@ -3256,7 +3412,7 @@ static void extrude_branch(
|
||||||
float angle_step = 2. * acos(1. - eps / radius);
|
float angle_step = 2. * acos(1. - eps / radius);
|
||||||
auto nsteps = int(ceil(M_PI / (2. * angle_step)));
|
auto nsteps = int(ceil(M_PI / (2. * angle_step)));
|
||||||
angle_step = M_PI / (2. * nsteps);
|
angle_step = M_PI / (2. * nsteps);
|
||||||
float angle = M_PI / 2.;
|
auto angle = float(M_PI / 2.);
|
||||||
for (int i = 0; i < nsteps; ++ i, angle -= angle_step) {
|
for (int i = 0; i < nsteps; ++ i, angle -= angle_step) {
|
||||||
std::pair<int, int> strip = discretize_circle((p2 + ncurrent * radius * cos(angle)).cast<float>(), ncurrent.cast<float>(), radius * sin(angle), eps, result.vertices);
|
std::pair<int, int> strip = discretize_circle((p2 + ncurrent * radius * cos(angle)).cast<float>(), ncurrent.cast<float>(), radius * sin(angle), eps, result.vertices);
|
||||||
triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
|
triangulate_strip(result, prev_strip.first, prev_strip.second, strip.first, strip.second);
|
||||||
|
@ -3335,9 +3491,7 @@ static void draw_branches(
|
||||||
assert(!(++it != map_downwards_old.end() && it->first == &elem));
|
assert(!(++it != map_downwards_old.end() && it->first == &elem));
|
||||||
}
|
}
|
||||||
const SupportElement *pchild = child == -1 ? nullptr : &move_bounds[layer_idx - 1][child];
|
const SupportElement *pchild = child == -1 ? nullptr : &move_bounds[layer_idx - 1][child];
|
||||||
if ((! pchild && elem.state.target_height == layer_idx) || (pchild && ! pchild->state.result_on_layer_is_set()))
|
assert(pchild ? pchild->state.result_on_layer_is_set() : elem.state.target_height > layer_idx);
|
||||||
// We either come from nowhere at the final layer or we had invalid parents 2. should never happen but just to be sure
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
for (int32_t parent_idx : elem.parents) {
|
for (int32_t parent_idx : elem.parents) {
|
||||||
SupportElement &parent = (*layer_above)[parent_idx];
|
SupportElement &parent = (*layer_above)[parent_idx];
|
||||||
|
@ -3370,7 +3524,8 @@ static void draw_branches(
|
||||||
const double max_nudge_collision_avoidance = 2. * scale;
|
const double max_nudge_collision_avoidance = 2. * scale;
|
||||||
const double max_nudge_smoothing = 1. * scale;
|
const double max_nudge_smoothing = 1. * scale;
|
||||||
|
|
||||||
for (size_t iter = 0; iter < 1000; ++ iter) {
|
static constexpr const size_t num_iter = 100; // 1000;
|
||||||
|
for (size_t iter = 0; iter < num_iter; ++ iter) {
|
||||||
prev = pts;
|
prev = pts;
|
||||||
projections = pts;
|
projections = pts;
|
||||||
distances.assign(pts.size(), std::numeric_limits<float>::max());
|
distances.assign(pts.size(), std::numeric_limits<float>::max());
|
||||||
|
@ -3404,18 +3559,24 @@ static void draw_branches(
|
||||||
Vec2d avg{ 0, 0 };
|
Vec2d avg{ 0, 0 };
|
||||||
const SupportElements &above = move_bounds[element.state.layer_idx + 1];
|
const SupportElements &above = move_bounds[element.state.layer_idx + 1];
|
||||||
const size_t offset_above = linear_data_layers[element.state.layer_idx + 1];
|
const size_t offset_above = linear_data_layers[element.state.layer_idx + 1];
|
||||||
|
double weight = 0.;
|
||||||
for (auto iparent : element.parents) {
|
for (auto iparent : element.parents) {
|
||||||
avg.x() += prev[offset_above + iparent].x();
|
double w = config.getRadius(above[iparent].state);
|
||||||
avg.y() += prev[offset_above + iparent].y();
|
avg.x() += w * prev[offset_above + iparent].x();
|
||||||
|
avg.y() += w * prev[offset_above + iparent].y();
|
||||||
|
weight += w;
|
||||||
}
|
}
|
||||||
size_t cnt = element.parents.size();
|
size_t cnt = element.parents.size();
|
||||||
if (below != -1) {
|
if (below != -1) {
|
||||||
const size_t offset_below = linear_data_layers[element.state.layer_idx - 1];
|
const size_t offset_below = linear_data_layers[element.state.layer_idx - 1];
|
||||||
avg.x() += prev[offset_below + below].x();
|
const double w = weight; // config.getRadius(move_bounds[element.state.layer_idx - 1][below].state);
|
||||||
avg.y() += prev[offset_below + below].y();
|
avg.x() += w * prev[offset_below + below].x();
|
||||||
|
avg.y() += w * prev[offset_below + below].y();
|
||||||
++ cnt;
|
++ cnt;
|
||||||
|
weight += w;
|
||||||
}
|
}
|
||||||
avg /= double(cnt);
|
//avg /= double(cnt);
|
||||||
|
avg /= weight;
|
||||||
static constexpr const double smoothing_factor = 0.5;
|
static constexpr const double smoothing_factor = 0.5;
|
||||||
Vec2d old_pos{ pts[i].x(), pts[i].y() };
|
Vec2d old_pos{ pts[i].x(), pts[i].y() };
|
||||||
Vec2d new_pos = (1. - smoothing_factor) * old_pos + smoothing_factor * avg;
|
Vec2d new_pos = (1. - smoothing_factor) * old_pos + smoothing_factor * avg;
|
||||||
|
@ -3654,13 +3815,15 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
||||||
auto t_place = std::chrono::high_resolution_clock::now();
|
auto t_place = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
// ### draw these points as circles
|
// ### draw these points as circles
|
||||||
#if 0
|
|
||||||
draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds,
|
if (print_object.config().support_material_style == smsTree)
|
||||||
bottom_contacts, top_contacts, intermediate_layers, layer_storage);
|
draw_areas(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds,
|
||||||
#else
|
bottom_contacts, top_contacts, intermediate_layers, layer_storage);
|
||||||
draw_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds,
|
else {
|
||||||
bottom_contacts, top_contacts, intermediate_layers, layer_storage);
|
assert(print_object.config().support_material_style == smsOrganic);
|
||||||
#endif
|
draw_branches(*print.get_object(processing.second.front()), volumes, config, overhangs, move_bounds,
|
||||||
|
bottom_contacts, top_contacts, intermediate_layers, layer_storage);
|
||||||
|
}
|
||||||
|
|
||||||
auto t_draw = std::chrono::high_resolution_clock::now();
|
auto t_draw = std::chrono::high_resolution_clock::now();
|
||||||
auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
|
auto dur_pre_gen = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_precalc - t_start).count();
|
||||||
|
|
Loading…
Reference in a new issue