WIP Tree Supports: Extracted Cura configs into a structure,

replaced Cura AABB with BoundingBox,
headers should be compilable now.
This commit is contained in:
Vojtech Bubnik 2022-07-20 11:21:53 +02:00
parent 6e1e4fcca2
commit 23099c83dc
5 changed files with 176 additions and 114 deletions

View File

@ -511,6 +511,13 @@ inline Point align_to_grid(Point coord, Point spacing, Point base)
} // namespace Slic3r
namespace std {
template <> struct hash<Slic3r::Point> {
size_t operator()(const Slic3r::Point &p) const
{ return (89 * 31 + p.x()) * 31 + p.y(); }
};
}
// start Boost
#include <boost/version.hpp>
#include <boost/polygon/polygon.hpp>

View File

@ -52,7 +52,7 @@ TreeModelVolumes::TreeModelVolumes(const SliceDataStorage& storage, const coord_
current_outline_idx = mesh_to_layeroutline_idx[current_mesh_idx];
TreeSupport::TreeSupportSettings config(layer_outlines_[current_outline_idx].first);
if (config.support_overrides == SupportDistPriority::Z_OVERRIDES_XY)
if (! config.support_xy_overrides_z)
{
current_min_xy_dist = config.xy_min_distance;
@ -76,7 +76,7 @@ TreeModelVolumes::TreeModelVolumes(const SliceDataStorage& storage, const coord_
tbb::parallel_for(tbb::blocked_range<size_t>(0, layer_outlines_[mesh_to_layeroutline_idx[mesh_idx]].second.size()),
[&](const tbb::blocked_range<size_t> &range) {
for (const size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
if (mesh.layer_nr_max_filled_layer < layer_idx)
{
return; // cant break as parallel_for wont allow it, this is equivalent to a continue
@ -89,7 +89,7 @@ TreeModelVolumes::TreeModelVolumes(const SliceDataStorage& storage, const coord_
tbb::parallel_for(tbb::blocked_range<size_t>(0, anti_overhang_.size()),
[&](const tbb::blocked_range<size_t> &range) {
for (const size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
if (layer_idx < coord_t(additional_excluded_areas.size()))
{
anti_overhang_[layer_idx].add(additional_excluded_areas[layer_idx]);
@ -501,7 +501,7 @@ coord_t TreeModelVolumes::getRadiusNextCeil(coord_t radius, bool min_xy_dist) co
return ceiled_radius;
}
bool TreeModelVolumes::checkSettingsEquality(const Settings& me, const Settings& other) const
bool TreeModelVolumes::checkSettingsEquality(const TreeSupportMeshGroupSettings& me, const TreeSupportMeshGroupSettings& other) const
{
return TreeSupport::TreeSupportSettings(me) == TreeSupport::TreeSupportSettings(other);
}

View File

@ -13,9 +13,58 @@
#include <unordered_map>
#include <unordered_set>
#include "Polygon.hpp"
#include "PrintConfig.hpp"
namespace Slic3r
{
using LayerIndex = size_t;
using AngleRadians = double;
//FIXME
class SliceDataStorage;
class SliceMeshStorage;
struct TreeSupportMeshGroupSettings {
AngleRadians support_tree_angle;
AngleRadians support_tree_angle_slow;
AngleRadians support_tree_branch_diameter_angle;
coord_t support_tree_bp_diameter;
coord_t support_tree_max_diameter_increase_by_merges_when_support_to_model;
coord_t support_tree_min_height_to_model;
coord_t support_line_width;
coord_t layer_height;
coord_t support_tree_branch_diameter;
coord_t support_tree_tip_diameter;
bool support_bottom_enable;
coord_t support_bottom_height;
bool support_material_buildplate_only;
bool support_xy_overrides_z;
coord_t support_xy_distance;
coord_t support_xy_distance_overhang;
coord_t support_top_distance;
coord_t support_bottom_distance;
coord_t support_interface_skip_height;
std::vector<AngleRadians> support_infill_angles;
std::vector<AngleRadians> support_roof_angles;
SupportMaterialInterfacePattern support_roof_pattern;
SupportMaterialPattern support_pattern;
coord_t support_roof_line_width;
coord_t support_line_distance;
coord_t support_bottom_offset;
int support_wall_count;
coord_t meshfix_maximum_deviation;
coord_t meshfix_maximum_resolution;
coord_t support_roof_line_distance;
coord_t min_feature_size;
};
inline coord_t round_up_divide(const coord_t dividend, const coord_t divisor) //!< Return dividend divided by divisor rounded to the nearest integer
{
return (dividend + divisor - 1) / divisor;
}
class TreeModelVolumes
{
public:
@ -208,7 +257,7 @@ class TreeModelVolumes
*/
void calculateAvoidance(RadiusLayerPair key)
{
calculateAvoidance(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) }).wait();
calculateAvoidance(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) });
}
/*!
@ -218,7 +267,7 @@ class TreeModelVolumes
*/
void calculatePlaceables(RadiusLayerPair key)
{
calculatePlaceables(std::deque<RadiusLayerPair>{ key }).wait();
calculatePlaceables(std::deque<RadiusLayerPair>{ key });
}
/*!
@ -246,7 +295,7 @@ class TreeModelVolumes
*/
void calculateAvoidanceToModel(RadiusLayerPair key)
{
calculateAvoidanceToModel(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) }).wait();
calculateAvoidanceToModel(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) });
}
/*!
* \brief Creates the areas that can not be passed when expanding an area downwards. As such these areas are an somewhat abstract representation of a wall (as in a printed object).
@ -264,7 +313,7 @@ class TreeModelVolumes
*/
void calculateWallRestrictions(RadiusLayerPair key)
{
calculateWallRestrictions(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) }).wait();
calculateWallRestrictions(std::deque<RadiusLayerPair>{ RadiusLayerPair(key) });
}
/*!
@ -274,7 +323,7 @@ class TreeModelVolumes
*/
template <typename KEY>
const std::optional<std::reference_wrapper<const Polygons>> getArea(const std::unordered_map<KEY, Polygons>& cache, const KEY key) const;
bool checkSettingsEquality(const Settings& me, const Settings& other) const;
bool checkSettingsEquality(const TreeSupportMeshGroupSettings& me, const TreeSupportMeshGroupSettings& other) const;
/*!
* \brief Get the highest already calculated layer in the cache.
* \param radius The radius for which the highest already calculated layer has to be found.
@ -349,7 +398,7 @@ class TreeModelVolumes
/*!
* \brief Storage for layer outlines and the corresponding settings of the meshes grouped by meshes with identical setting.
*/
std::vector<std::pair<Settings, std::vector<Polygons>>> layer_outlines_;
std::vector<std::pair<TreeSupportMeshGroupSettings, std::vector<Polygons>>> layer_outlines_;
/*!
* \brief Storage for areas that should be avoided, like support blocker or previous generated trees.
*/

View File

@ -97,7 +97,7 @@ std::string getPolygonAsString(const Polygons& poly)
{
if (ret != "")
ret += ",";
ret += "(" + std::to_string(p.X) + "," + std::to_string(p.Y) + ")";
ret += "(" + std::to_string(p.x()) + "," + std::to_string(p.y()) + ")";
}
}
return ret;
@ -248,7 +248,7 @@ void TreeSupport::precalculate(const SliceDataStorage& storage, std::vector<size
std::vector<TreeSupport::LineInformation> TreeSupport::convertLinesToInternal(Polygons polylines, LayerIndex layer_idx)
{
const bool xy_overrides = config.support_overrides == SupportDistPriority::XY_OVERRIDES_Z;
const bool xy_overrides = config.support_xy_overrides_z;
std::vector<LineInformation> result;
// Also checks if the position is valid, if it is NOT, it deletes that point
@ -315,7 +315,7 @@ Polygons TreeSupport::convertInternalToLines(std::vector<TreeSupport::LineInform
std::function<bool(std::pair<Point, TreeSupport::LineStatus>)> TreeSupport::getEvaluatePointForNextLayerFunction(size_t current_layer)
{
const bool xy_overrides = config.support_overrides == SupportDistPriority::XY_OVERRIDES_Z;
const bool xy_overrides = config.support_xy_overrides_z;
std::function<bool(std::pair<Point, LineStatus>)> evaluatePoint = [=](std::pair<Point, LineStatus> p)
{
if (!volumes_.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_BP_SAFE ? AvoidanceType::FAST_SAFE : AvoidanceType::FAST, false, !xy_overrides).inside(p.first, true))
@ -563,7 +563,7 @@ Polygons TreeSupport::generateSupportInfillLines(const Polygons& area, bool roof
const EFillMethod pattern = roof ? config.roof_pattern : config.support_pattern;
const bool zig_zaggify_infill = roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support;
// const bool zig_zaggify_infill = roof ? pattern == EFillMethod::ZIG_ZAG : config.zig_zaggify_support;
const bool connect_polygons = false;
constexpr coord_t support_roof_overlap = 0;
constexpr size_t infill_multiplier = 1;
@ -574,17 +574,16 @@ Polygons TreeSupport::generateSupportInfillLines(const Polygons& area, bool roof
constexpr Polygons* perimeter_gaps = nullptr;
constexpr bool use_endpieces = true;
const bool connected_zigzags = roof ? false : config.connect_zigzags;
const bool skip_some_zags = roof ? false : config.skip_some_zags;
const size_t zag_skip_count = roof ? 0 : config.zag_skip_count;
constexpr coord_t pocket_size = 0;
std::vector<AngleDegrees> angles = roof ? config.support_roof_angles : config.support_infill_angles;
std::vector<AngleRadians> angles = roof ? config.support_roof_angles : config.support_infill_angles;
std::vector<VariableWidthLines> toolpaths;
const coord_t z = config.getActualZ(layer_idx);
int divisor = static_cast<int>(angles.size());
int index = ((layer_idx % divisor) + divisor) % divisor;
const AngleDegrees fill_angle = angles[index];
Infill roof_computation(pattern, zig_zaggify_infill, connect_polygons, area, roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier, fill_angle, z, support_shift, config.maximum_resolution, config.maximum_deviation, wall_line_count, infill_origin, perimeter_gaps, connected_zigzags, use_endpieces, skip_some_zags, zag_skip_count, pocket_size);
const AngleRadians fill_angle = angles[index];
Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, area, roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier, fill_angle, z, support_shift, config.maximum_resolution, config.maximum_deviation, wall_line_count, infill_origin, perimeter_gaps, connected_zigzags, use_endpieces, false /* skip_some_zags */, zag_skip_count, pocket_size);
Polygons areas;
Polygons lines;
roof_computation.generate(toolpaths, areas, lines, config.settings, cross_fill_provider);
@ -670,18 +669,18 @@ void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, std::vector
const int base_radius = 10;
for (unsigned int i = 0; i < SUPPORT_TREE_CIRCLE_RESOLUTION; i++)
{
const AngleRadians angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * TAU;
const AngleRadians angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI);
base_circle.emplace_back(coord_t(cos(angle) * base_radius), coord_t(sin(angle) * base_radius));
}
TreeSupportSettings mesh_config(mesh.settings);
const size_t z_distance_delta = mesh_config.z_distance_top_layers + 1; // To ensure z_distance_top_layers are left empty between the overhang (zeroth empty layer), the support has to be added z_distance_top_layers+1 layers below
const bool xy_overrides = mesh_config.support_overrides == SupportDistPriority::XY_OVERRIDES_Z;
const bool xy_overrides = mesh_config.support_xy_overrides_z;
const coord_t support_roof_line_distance = mesh.settings.get<coord_t>("support_roof_line_distance");
const double minimum_roof_area = mesh.settings.get<double>("minimum_roof_area");
const double minimum_support_area = mesh.settings.get<double>("minimum_support_area");
const size_t support_roof_layers = mesh.settings.get<bool>("support_roof_enable") ? round_divide(mesh.settings.get<coord_t>("support_roof_height"), mesh_config.layer_height) : 0;
const size_t support_roof_layers = mesh.settings.get<bool>("support_roof_enable") ? (mesh.settings.get<coord_t>("support_roof_height") + mesh_config.layer_height / 2) / mesh_config.layer_height : 0;
const bool roof_enabled = support_roof_layers != 0;
const bool only_gracious = SUPPORT_TREE_ONLY_GRACIOUS_TO_MODEL;
const EFillMethod support_pattern = mesh.settings.get<EFillMethod>("support_pattern");
@ -693,7 +692,7 @@ void TreeSupport::generateInitialAreas(const SliceMeshStorage& mesh, std::vector
const coord_t extra_outset = std::max(coord_t(0), mesh_config.min_radius - mesh_config.support_line_width) + (xy_overrides ? 0 : mesh_config.support_line_width / 2); // extra support offset to compensate for larger tip radiis. Also outset a bit more when z overwrites xy, because supporting something with a part of a support line is better than not supporting it at all.
const bool force_tip_to_roof = (mesh_config.min_radius * mesh_config.min_radius * M_PI > minimum_roof_area * (1000 * 1000)) && roof_enabled;
const double support_overhang_angle = mesh.settings.get<AngleRadians>("support_angle");
const coord_t max_overhang_speed = (support_overhang_angle < TAU / 4) ? (coord_t)(tan(support_overhang_angle) * mesh_config.layer_height) : std::numeric_limits<coord_t>::max();
const coord_t max_overhang_speed = (support_overhang_angle < (2.0 * M_PI) / 4) ? (coord_t)(tan(support_overhang_angle) * mesh_config.layer_height) : std::numeric_limits<coord_t>::max();
const size_t max_overhang_insert_lag = std::max((size_t)round_up_divide(mesh_config.xy_distance, max_overhang_speed / 2), 2 * mesh_config.z_distance_top_layers); // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. The 2*z_distance_delta is only a catch for when the support angle is very high.
SierpinskiFillProvider* cross_fill_provider = generateCrossFillProvider(mesh, support_tree_branch_distance, mesh_config.support_line_width);
if (mesh.overhang_areas.size() <= z_distance_delta)
@ -1062,18 +1061,18 @@ Polygons TreeSupport::safeOffsetInc(const Polygons& me, coord_t distance, const
}
void TreeSupport::mergeHelper(std::map<SupportElement, AABB>& reduced_aabb, std::map<SupportElement, AABB>& input_aabb, const std::unordered_map<SupportElement, Polygons>& to_bp_areas, const std::unordered_map<SupportElement, Polygons>& to_model_areas, const std::map<SupportElement, Polygons>& influence_areas, std::unordered_map<SupportElement, Polygons>& insert_bp_areas, std::unordered_map<SupportElement, Polygons>& insert_model_areas, std::unordered_map<SupportElement, Polygons>& insert_influence, std::vector<SupportElement>& erase, const LayerIndex layer_idx)
void TreeSupport::mergeHelper(std::map<SupportElement, BoundingBox>& reduced_aabb, std::map<SupportElement, BoundingBox>& input_aabb, const std::unordered_map<SupportElement, Polygons>& to_bp_areas, const std::unordered_map<SupportElement, Polygons>& to_model_areas, const std::map<SupportElement, Polygons>& influence_areas, std::unordered_map<SupportElement, Polygons>& insert_bp_areas, std::unordered_map<SupportElement, Polygons>& insert_model_areas, std::unordered_map<SupportElement, Polygons>& insert_influence, std::vector<SupportElement>& erase, const LayerIndex layer_idx)
{
const bool first_merge_iteration = reduced_aabb.empty(); // If this is the first iteration, all elements in input have to be merged with each other
for (std::map<SupportElement, AABB>::iterator influence_iter = input_aabb.begin(); influence_iter != input_aabb.end(); influence_iter++)
for (std::map<SupportElement, BoundingBox>::iterator influence_iter = input_aabb.begin(); influence_iter != input_aabb.end(); influence_iter++)
{
bool merged = false;
AABB influence_aabb = influence_iter->second;
for (std::map<SupportElement, AABB>::iterator reduced_check_iter = reduced_aabb.begin(); reduced_check_iter != reduced_aabb.end(); reduced_check_iter++)
BoundingBox influence_aabb = influence_iter->second;
for (std::map<SupportElement, BoundingBox>::iterator reduced_check_iter = reduced_aabb.begin(); reduced_check_iter != reduced_aabb.end(); reduced_check_iter++)
{
// As every area has to be checked for overlaps with other areas, some fast heuristic is needed to abort early if clearly possible
// This is so performance critical that using a map lookup instead of the direct access of the cached AABBs can have a surprisingly large performance impact
AABB aabb = reduced_check_iter->second;
BoundingBox aabb = reduced_check_iter->second;
if (aabb.hit(influence_aabb))
{
if (!first_merge_iteration && input_aabb.count(reduced_check_iter->first))
@ -1223,7 +1222,7 @@ void TreeSupport::mergeHelper(std::map<SupportElement, AABB>& reduced_aabb, std:
Polygons merge = intersect.unionPolygons(intersect_sec).offset(config.getRadius(key), ClipperLib::jtRound).difference(volumes_.getCollision(0, layer_idx - 1)); // regular union should be preferable here as Polygons tend to only become smaller through rounding errors (smaller!=has smaller area as holes have a negative area.). And if this area disappears because of rounding errors, the only downside is that it can not merge again on this layer.
reduced_aabb.erase(reduced_check_iter->first); // this invalidates reduced_check_iter
reduced_aabb.emplace(key, AABB(merge));
reduced_aabb.emplace(key, BoundingBox(merge));
merged = true;
break;
@ -1268,7 +1267,7 @@ void TreeSupport::mergeInfluenceAreas(std::unordered_map<SupportElement, Polygon
// an extra empty bucket is created for each bucket, and the elements are merged into the empty one.
// Each thread will then process two buckets by merging all elements in the second bucket into the first one as mergeHelper will disable not trying to merge elements from the same bucket in this case.
std::vector<std::map<SupportElement, Polygons>> buckets_area(2 * bucket_count);
std::vector<std::map<SupportElement, AABB>> buckets_aabb(2 * bucket_count);
std::vector<std::map<SupportElement, BoundingBox>> buckets_aabb(2 * bucket_count);
size_t position = 0, counter = 0;
@ -1298,7 +1297,7 @@ void TreeSupport::mergeInfluenceAreas(std::unordered_map<SupportElement, Polygon
size_t bucket_idx = 2 * idx + 1;
for (const std::pair<SupportElement, Polygons>& input_pair : buckets_area[bucket_idx])
{
AABB outer_support_wall_aabb = AABB(input_pair.second);
BoundingBox outer_support_wall_aabb = BoundingBox(input_pair.second);
outer_support_wall_aabb.expand(config.getRadius(input_pair.first));
buckets_aabb[bucket_idx].emplace(input_pair.first, outer_support_wall_aabb);
}
@ -1352,7 +1351,7 @@ void TreeSupport::mergeInfluenceAreas(std::unordered_map<SupportElement, Polygon
auto position_rem = std::remove_if(buckets_area.begin(), buckets_area.end(), [&](const std::map<SupportElement, Polygons> x) mutable { return x.empty(); });
buckets_area.erase(position_rem, buckets_area.end());
auto position_aabb = std::remove_if(buckets_aabb.begin(), buckets_aabb.end(), [&](const std::map<SupportElement, AABB> x) mutable { return x.empty(); });
auto position_aabb = std::remove_if(buckets_aabb.begin(), buckets_aabb.end(), [&](const std::map<SupportElement, BoundingBox> x) mutable { return x.empty(); });
buckets_aabb.erase(position_aabb, buckets_aabb.end());
}
}
@ -1963,7 +1962,7 @@ bool TreeSupport::setToModelContact(std::vector<std::set<SupportElement*>>& move
}
checked[last_successfull_layer - layer_idx]->result_on_layer = best;
logDebug("Added gracious Support On Model Point (%lld,%lld). The current layer is %lld\n", best.X, best.Y, last_successfull_layer);
logDebug("Added gracious Support On Model Point (%lld,%lld). The current layer is %lld\n", best.x(), best.y(), last_successfull_layer);
return last_successfull_layer != layer_idx;
}
@ -1976,7 +1975,7 @@ bool TreeSupport::setToModelContact(std::vector<std::set<SupportElement*>>& move
}
first_elem->result_on_layer = best;
first_elem->to_model_gracious = false;
logDebug("Added NON gracious Support On Model Point (%lld,%lld). The current layer is %lld\n", best.X, best.Y, layer_idx);
logDebug("Added NON gracious Support On Model Point (%lld,%lld). The current layer is %lld\n", best.x(), best.y(), layer_idx);
return false;
}
}
@ -2010,7 +2009,7 @@ void TreeSupport::createNodesFromArea(std::vector<std::set<SupportElement*>>& mo
{
if (elem->to_buildplate)
{
logError("Uninitialized Influence area targeting (%lld,%lld) at target_height: %lld layer: %lld\n", elem->target_position.X, elem->target_position.Y, elem->target_height, layer_idx);
logError("Uninitialized Influence area targeting (%lld,%lld) at target_height: %lld layer: %lld\n", elem->target_position.x(), elem->target_position.y(), elem->target_height, layer_idx);
TreeSupport::showError("Uninitialized support element! A branch could be missing or exist partially.", true);
}
remove.emplace(elem); // we dont need to remove yet the parents as they will have a lower dtt and also no result_on_layer set
@ -2056,7 +2055,7 @@ void TreeSupport::generateBranchAreas(std::vector<std::pair<LayerIndex, SupportE
Polygon branch_circle; // Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
for (unsigned int i = 0; i < SUPPORT_TREE_CIRCLE_RESOLUTION; i++)
{
const AngleRadians angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * TAU;
const AngleRadians angle = static_cast<double>(i) / SUPPORT_TREE_CIRCLE_RESOLUTION * (2.0 * M_PI);
branch_circle.emplace_back(coord_t(cos(angle) * config.branch_radius), coord_t(sin(angle) * config.branch_radius));
}
@ -2103,8 +2102,8 @@ void TreeSupport::generateBranchAreas(std::vector<std::pair<LayerIndex, SupportE
// Ovalizes the circle to an ellipse, that contains both old center and new target position.
double used_scale = (movement.second + offset) / (1.0 * config.branch_radius);
Point center_position = elem->result_on_layer + movement.first / 2;
const double moveX = movement.first.X / (used_scale * config.branch_radius);
const double moveY = movement.first.Y / (used_scale * config.branch_radius);
const double moveX = movement.first.x() / (used_scale * config.branch_radius);
const double moveY = movement.first.y() / (used_scale * config.branch_radius);
const double vsize_inv = 0.5 / (0.01 + std::sqrt(moveX * moveX + moveY * moveY));
double matrix[] = {
@ -2116,7 +2115,7 @@ void TreeSupport::generateBranchAreas(std::vector<std::pair<LayerIndex, SupportE
Polygon circle;
for (Point vertex : branch_circle)
{
vertex = Point(matrix[0] * vertex.X + matrix[1] * vertex.Y, matrix[2] * vertex.X + matrix[3] * vertex.Y);
vertex = Point(matrix[0] * vertex.x() + matrix[1] * vertex.y(), matrix[2] * vertex.x() + matrix[3] * vertex.y());
circle.add(center_position + vertex);
}
poly.add(circle.offset(0));

View File

@ -10,8 +10,10 @@
#define slic3r_TreeSupport_hpp
#include "TreeModelVolumes.hpp"
#include <boost/functional/hash.hpp> // For combining hashes
#include "BoundingBox.hpp"
#define SUPPORT_TREE_CIRCLE_RESOLUTION 25 // The number of vertices in each circle.
@ -41,6 +43,11 @@
namespace Slic3r
{
using LayerIndex = size_t;
//FIXME
class SierpinskiFillProvider;
/*!
* \brief Generates a tree structure to support your models.
@ -323,7 +330,7 @@ class TreeSupport
return other.target_height < target_height;
}
return other.target_position.X == target_position.X ? other.target_position.Y < target_position.Y : other.target_position.X < target_position.X;
return other.target_position.x() == target_position.x() ? other.target_position.y() < target_position.y() : other.target_position.x() < target_position.x();
}
void AddParents(const std::vector<SupportElement*>& adding)
@ -342,48 +349,44 @@ class TreeSupport
{
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupport class.
TreeSupportSettings(const Settings& mesh_group_settings)
: angle(mesh_group_settings.get<AngleRadians>("support_tree_angle")),
angle_slow(mesh_group_settings.get<AngleRadians>("support_tree_angle_slow")),
support_line_width(mesh_group_settings.get<coord_t>("support_line_width")),
layer_height(mesh_group_settings.get<coord_t>("layer_height")),
branch_radius(mesh_group_settings.get<coord_t>("support_tree_branch_diameter") / 2),
min_radius(mesh_group_settings.get<coord_t>("support_tree_tip_diameter") / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((angle < TAU / 4) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((angle_slow < TAU / 4) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.get<bool>("support_bottom_enable") ? round_divide(mesh_group_settings.get<coord_t>("support_bottom_height"), layer_height) : 0),
TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings)
: angle(mesh_group_settings.support_tree_angle),
angle_slow(mesh_group_settings.support_tree_angle_slow),
support_line_width(mesh_group_settings.support_line_width),
layer_height(mesh_group_settings.layer_height),
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
diameter_angle_scale_factor(sin(mesh_group_settings.get<AngleRadians>("support_tree_branch_diameter_angle")) * layer_height / branch_radius),
max_to_model_radius_increase(mesh_group_settings.get<coord_t>("support_tree_max_diameter_increase_by_merges_when_support_to_model") / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.get<coord_t>("support_tree_min_height_to_model"), layer_height)),
increase_radius_until_radius(mesh_group_settings.get<coord_t>("support_tree_branch_diameter") / 2),
diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / (branch_radius * diameter_angle_scale_factor)),
support_rests_on_model(mesh_group_settings.get<ESupportType>("support_type") == ESupportType::EVERYWHERE),
xy_distance(mesh_group_settings.get<coord_t>("support_xy_distance")),
bp_radius(mesh_group_settings.get<coord_t>("support_tree_bp_diameter") / 2),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller.
support_overrides(mesh_group_settings.get<SupportDistPriority>("support_xy_overrides_z")),
xy_min_distance(support_overrides == SupportDistPriority::Z_OVERRIDES_XY ? mesh_group_settings.get<coord_t>("support_xy_distance_overhang") : xy_distance),
z_distance_top_layers(round_up_divide(mesh_group_settings.get<coord_t>("support_top_distance"), layer_height)),
z_distance_bottom_layers(round_up_divide(mesh_group_settings.get<coord_t>("support_bottom_distance"), layer_height)),
performance_interface_skip_layers(round_up_divide(mesh_group_settings.get<coord_t>("support_interface_skip_height"), layer_height)),
support_infill_angles(mesh_group_settings.get<std::vector<AngleDegrees>>("support_infill_angles")),
support_roof_angles(mesh_group_settings.get<std::vector<AngleDegrees>>("support_roof_angles")),
roof_pattern(mesh_group_settings.get<EFillMethod>("support_roof_pattern")),
support_pattern(mesh_group_settings.get<EFillMethod>("support_pattern")),
support_roof_line_width(mesh_group_settings.get<coord_t>("support_roof_line_width")),
support_line_distance(mesh_group_settings.get<coord_t>("support_line_distance")),
support_bottom_offset(mesh_group_settings.get<coord_t>("support_bottom_offset")),
support_wall_count(mesh_group_settings.get<int>("support_wall_count")),
zig_zaggify_support(mesh_group_settings.get<bool>("zig_zaggify_support")),
maximum_deviation(mesh_group_settings.get<coord_t>("meshfix_maximum_deviation")),
maximum_resolution(mesh_group_settings.get<coord_t>("meshfix_maximum_resolution")),
support_roof_line_distance(mesh_group_settings.get<coord_t>("support_roof_line_distance")), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
skip_some_zags(mesh_group_settings.get<bool>("support_skip_some_zags")),
zag_skip_count(mesh_group_settings.get<size_t>("support_zag_skip_count")),
connect_zigzags(mesh_group_settings.get<bool>("support_connect_zigzags")),
support_xy_overrides_z(mesh_group_settings.support_xy_overrides_z),
xy_min_distance(support_xy_overrides_z ? xy_distance : mesh_group_settings.support_xy_distance_overhang),
z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)),
z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)),
performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, layer_height)),
support_infill_angles(mesh_group_settings.support_infill_angles),
support_roof_angles(mesh_group_settings.support_roof_angles),
roof_pattern(mesh_group_settings.support_roof_pattern),
support_pattern(mesh_group_settings.support_pattern),
support_roof_line_width(mesh_group_settings.support_roof_line_width),
support_line_distance(mesh_group_settings.support_line_distance),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
maximum_deviation(mesh_group_settings.meshfix_maximum_deviation),
maximum_resolution(mesh_group_settings.meshfix_maximum_resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.get<coord_t>("min_feature_size"))
min_feature_size(mesh_group_settings.min_feature_size)
{
@ -399,38 +402,42 @@ class TreeSupport
xy_distance = std::max(xy_distance, xy_min_distance);
std::function<void(std::vector<AngleDegrees>&, EFillMethod)> getInterfaceAngles = [&](std::vector<AngleDegrees>& angles, EFillMethod pattern) { // (logic) from getInterfaceAngles in FFFGcodeWriter.
std::function<void(std::vector<AngleRadians>&, SupportMaterialInterfacePattern)> getInterfaceAngles = [&](std::vector<AngleRadians>& angles, SupportMaterialInterfacePattern pattern) { // (logic) from getInterfaceAngles in FFFGcodeWriter.
if (angles.empty())
{
if (pattern == EFillMethod::CONCENTRIC)
if (pattern == SupportMaterialInterfacePattern::smipConcentric)
{
angles.push_back(0); // Concentric has no rotation.
}
else if (pattern == EFillMethod::TRIANGLES)
/*
else if (pattern == SupportMaterialInterfacePattern::TRIANGLES)
{
angles.push_back(90); // Triangular support interface shouldn't alternate every layer.
}
*/
else
{
if (TreeSupportSettings::some_model_contains_thick_roof)
{
// Some roofs are quite thick.
// Alternate between the two kinds of diagonal: / and \ .
angles.push_back(45);
angles.push_back(135);
angles.push_back(M_PI / 4.);
angles.push_back(3. * M_PI / 4.);
}
if (angles.empty())
{
angles.push_back(90); // Perpendicular to support lines.
angles.push_back(M_PI / 2.); // Perpendicular to support lines.
}
}
}
};
getInterfaceAngles(support_infill_angles, support_pattern);
//getInterfaceAngles(support_infill_angles, support_pattern);
support_infill_angles = { M_PI / 2. };
getInterfaceAngles(support_roof_angles, roof_pattern);
const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE }, { "interface_area_overwrite_support_area", InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT }, { "support_lines_overwrite_interface_area", InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE }, { "interface_lines_overwrite_support_area", InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT }, { "nothing", InterfacePreference::NOTHING } };
interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE }, { "interface_area_overwrite_support_area", InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT }, { "support_lines_overwrite_interface_area", InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE }, { "interface_lines_overwrite_support_area", InterfacePreference::INTERFACE_LINES_OVERWRITE_SUPPORT }, { "nothing", InterfacePreference::NOTHING } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
interface_preference = InterfacePreference::SUPPORT_LINES_OVERWRITE_INTERFACE;
}
private:
@ -518,7 +525,7 @@ class TreeSupport
/*!
* \brief Should Z distance override X/Y distance, or the other way around.
*/
SupportDistPriority support_overrides;
bool support_xy_overrides_z;
/*!
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
*/
@ -538,19 +545,19 @@ class TreeSupport
/*!
* \brief User specified angles for the support infill.
*/
std::vector<AngleDegrees> support_infill_angles;
std::vector<AngleRadians> support_infill_angles;
/*!
* \brief User specified angles for the support roof infill.
*/
std::vector<AngleDegrees> support_roof_angles;
std::vector<AngleRadians> support_roof_angles;
/*!
* \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled.
*/
EFillMethod roof_pattern;
SupportMaterialInterfacePattern roof_pattern;
/*!
* \brief Pattern used in the support infill.
*/
EFillMethod support_pattern;
SupportMaterialPattern support_pattern;
/*!
* \brief Line width of the support roof.
*/
@ -567,10 +574,6 @@ class TreeSupport
* \brief Amount of walls the support area will have.
*/
int support_wall_count;
/*
* \brief Whether support infill lines will be connected. Only required to calculate infill patterns.
*/
bool zig_zaggify_support;
/*
* \brief Maximum allowed deviation when simplifying.
*/
@ -583,18 +586,6 @@ class TreeSupport
* \brief Distance between the lines of the roof.
*/
coord_t support_roof_line_distance;
/*
* \brief Only relevant for zigzag pattern. Only required to calculate infill patterns.
*/
bool skip_some_zags;
/*
* \brief Only relevant for zigzag pattern. Only required to calculate infill patterns.
*/
size_t zag_skip_count;
/*
* \brief Only relevant for zigzag pattern. Only required to calculate infill patterns.
*/
bool connect_zigzags;
/*
* \brief How overlaps of an interface area with a support area should be handled.
*/
@ -603,7 +594,7 @@ class TreeSupport
/*
* \brief The infill class wants a settings object. This one will be the correct one for all settings it uses.
*/
Settings settings;
TreeSupportMeshGroupSettings settings;
/*
* \brief Minimum thickness of any model features.
@ -615,15 +606,31 @@ class TreeSupport
{
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && diameter_angle_scale_factor == other.diameter_angle_scale_factor && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius && diameter_scale_bp_radius == other.diameter_scale_bp_radius && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance && // as a recalculation of the collision areas is required to set a new min_radius.
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width && support_overrides == other.support_overrides && support_line_distance == other.support_line_distance && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
support_xy_overrides_z == other.support_xy_overrides_z && support_line_distance == other.support_line_distance && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless.
support_roof_angles == other.support_roof_angles && support_infill_angles == other.support_infill_angles && increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height && z_distance_top_layers == other.z_distance_top_layers && maximum_deviation == other.maximum_deviation && // Infill generation depends on deviation and resolution.
maximum_resolution == other.maximum_resolution && support_roof_line_distance == other.support_roof_line_distance && skip_some_zags == other.skip_some_zags && zag_skip_count == other.zag_skip_count && connect_zigzags == other.connect_zigzags && interface_preference == other.interface_preference
maximum_resolution == other.maximum_resolution && support_roof_line_distance == other.support_roof_line_distance && interface_preference == other.interface_preference
&& min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof.
// The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry
&& (interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || interface_preference == InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE
|| (settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") && settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") && settings.get<AngleRadians>("wall_transition_angle") == other.settings.get<AngleRadians>("wall_transition_angle") && settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") && settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") && settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") && settings.get<int>("wall_distribution_count") == other.settings.get<int>("wall_distribution_count") && settings.get<coord_t>("wall_transition_filter_distance") == other.settings.get<coord_t>("wall_transition_filter_distance") && settings.get<coord_t>("wall_transition_filter_deviation") == other.settings.get<coord_t>("wall_transition_filter_deviation") && settings.get<coord_t>("wall_line_width_x") == other.settings.get<coord_t>("wall_line_width_x")
&& settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation")));
#if 0
&& (interface_preference == InterfacePreference::INTERFACE_AREA_OVERWRITES_SUPPORT || interface_preference == InterfacePreference::SUPPORT_AREA_OVERWRITES_INTERFACE
// Perimeter generator parameters
||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") &&
settings.get<AngleRadians>("wall_transition_angle") == other.settings.get<AngleRadians>("wall_transition_angle") &&
settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") &&
settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") &&
settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") &&
settings.get<int>("wall_distribution_count") == other.settings.get<int>("wall_distribution_count") &&
settings.get<coord_t>("wall_transition_filter_distance") == other.settings.get<coord_t>("wall_transition_filter_distance") &&
settings.get<coord_t>("wall_transition_filter_deviation") == other.settings.get<coord_t>("wall_transition_filter_deviation") &&
settings.get<coord_t>("wall_line_width_x") == other.settings.get<coord_t>("wall_line_width_x") &&
settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation"))
)
#endif
;
}
@ -848,7 +855,7 @@ class TreeSupport
* \param erase[out] Elements that should be deleted from the above dictionaries.
* \param layer_idx[in] The Index of the current Layer.
*/
void mergeHelper(std::map<SupportElement, AABB>& reduced_aabb, std::map<SupportElement, AABB>& input_aabb, const std::unordered_map<SupportElement, Polygons>& to_bp_areas, const std::unordered_map<SupportElement, Polygons>& to_model_areas, const std::map<SupportElement, Polygons>& influence_areas, std::unordered_map<SupportElement, Polygons>& insert_bp_areas, std::unordered_map<SupportElement, Polygons>& insert_model_areas, std::unordered_map<SupportElement, Polygons>& insert_influence, std::vector<SupportElement>& erase, const LayerIndex layer_idx);
void mergeHelper(std::map<SupportElement, BoundingBox>& reduced_aabb, std::map<SupportElement, BoundingBox>& input_aabb, const std::unordered_map<SupportElement, Polygons>& to_bp_areas, const std::unordered_map<SupportElement, Polygons>& to_model_areas, const std::map<SupportElement, Polygons>& influence_areas, std::unordered_map<SupportElement, Polygons>& insert_bp_areas, std::unordered_map<SupportElement, Polygons>& insert_model_areas, std::unordered_map<SupportElement, Polygons>& insert_influence, std::vector<SupportElement>& erase, const LayerIndex layer_idx);
/*!
* \brief Merges Influence Areas if possible.
*