mirror of
https://github.com/QIDITECH/QIDIStudio.git
synced 2026-02-07 04:11:50 +03:00
update libslic3r
This commit is contained in:
1952
src/libslic3r/Support/SupportCommon.cpp
Normal file
1952
src/libslic3r/Support/SupportCommon.cpp
Normal file
File diff suppressed because it is too large
Load Diff
153
src/libslic3r/Support/SupportCommon.hpp
Normal file
153
src/libslic3r/Support/SupportCommon.hpp
Normal file
@@ -0,0 +1,153 @@
|
||||
///|/ Copyright (c) Prusa Research 2023 Vojtěch Bubník @bubnikv
|
||||
///|/
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#ifndef slic3r_SupportCommon_hpp_
|
||||
#define slic3r_SupportCommon_hpp_
|
||||
|
||||
#include "../Layer.hpp"
|
||||
#include "../Polygon.hpp"
|
||||
#include "../Print.hpp"
|
||||
#include "SupportLayer.hpp"
|
||||
#include "SupportParameters.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class SupportLayer;
|
||||
|
||||
// Turn some of the base layers into base interface layers.
|
||||
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
|
||||
// extruder to improve adhesion of the soluble filament to the base.
|
||||
// For Organic supports, merge top_interface_layers & top_base_interface_layers with the interfaces
|
||||
// produced by this function.
|
||||
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
|
||||
const PrintObjectConfig &config,
|
||||
const SupportParameters &support_params,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
// Input / output, will be merged with output
|
||||
SupportGeneratorLayersPtr &top_interface_layers,
|
||||
SupportGeneratorLayersPtr &top_base_interface_layers,
|
||||
// Input, will be trimmed with the newly created interface layers.
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage);
|
||||
|
||||
// Generate raft layers, also expand the 1st support layer
|
||||
// in case there is no raft layer to improve support adhesion.
|
||||
SupportGeneratorLayersPtr generate_raft_base(
|
||||
const PrintObject &object,
|
||||
const SupportParameters &support_params,
|
||||
const SlicingParameters &slicing_params,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage);
|
||||
|
||||
void tree_supports_generate_paths(ExtrusionEntitiesPtr &dst, const Polygons &polygons, const Flow &flow, const SupportParameters &support_params);
|
||||
|
||||
void fill_expolygons_with_sheath_generate_paths(
|
||||
ExtrusionEntitiesPtr &dst, const Polygons &polygons, Fill *filler, float density, ExtrusionRole role, const Flow &flow, const SupportParameters& support_params, bool with_sheath, bool no_sort);
|
||||
|
||||
// returns sorted layers
|
||||
SupportGeneratorLayersPtr generate_support_layers(
|
||||
PrintObject &object,
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers);
|
||||
|
||||
// Produce the support G-code.
|
||||
// Used by both classic and tree supports.
|
||||
void generate_support_toolpaths(
|
||||
SupportLayerPtrs &support_layers,
|
||||
const PrintObjectConfig &config,
|
||||
const SupportParameters &support_params,
|
||||
const SlicingParameters &slicing_params,
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers);
|
||||
|
||||
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
|
||||
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
|
||||
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
|
||||
// If the initial idx is size_t(-1), then use binary search.
|
||||
// Otherwise search linearly upwards.
|
||||
template<typename IteratorType, typename IndexType, typename FN_HIGHER_EQUAL>
|
||||
IndexType idx_higher_or_equal(IteratorType begin, IteratorType end, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
|
||||
{
|
||||
auto size = int(end - begin);
|
||||
if (size == 0) {
|
||||
idx = 0;
|
||||
} else if (idx == IndexType(-1)) {
|
||||
// First of the batch of layers per thread pool invocation. Use binary search.
|
||||
int idx_low = 0;
|
||||
int idx_high = std::max(0, size - 1);
|
||||
while (idx_low + 1 < idx_high) {
|
||||
int idx_mid = (idx_low + idx_high) / 2;
|
||||
if (fn_higher_equal(begin[idx_mid]))
|
||||
idx_high = idx_mid;
|
||||
else
|
||||
idx_low = idx_mid;
|
||||
}
|
||||
idx = fn_higher_equal(begin[idx_low]) ? idx_low :
|
||||
(fn_higher_equal(begin[idx_high]) ? idx_high : size);
|
||||
} else {
|
||||
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
|
||||
while (int(idx) < size && ! fn_higher_equal(begin[idx]))
|
||||
++ idx;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
template<typename T, typename IndexType, typename FN_HIGHER_EQUAL>
|
||||
IndexType idx_higher_or_equal(const std::vector<T>& vec, IndexType idx, FN_HIGHER_EQUAL fn_higher_equal)
|
||||
{
|
||||
return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal);
|
||||
}
|
||||
|
||||
// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold.
|
||||
// Find the first item with Z value <= of an internal threshold of fn_lower_equal.
|
||||
// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1.
|
||||
// If the initial idx is < -1, then use binary search.
|
||||
// Otherwise search linearly downwards.
|
||||
template<typename IT, typename FN_LOWER_EQUAL>
|
||||
int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal)
|
||||
{
|
||||
auto size = int(end - begin);
|
||||
if (size == 0) {
|
||||
idx = -1;
|
||||
} else if (idx < -1) {
|
||||
// First of the batch of layers per thread pool invocation. Use binary search.
|
||||
int idx_low = 0;
|
||||
int idx_high = std::max(0, size - 1);
|
||||
while (idx_low + 1 < idx_high) {
|
||||
int idx_mid = (idx_low + idx_high) / 2;
|
||||
if (fn_lower_equal(begin[idx_mid]))
|
||||
idx_low = idx_mid;
|
||||
else
|
||||
idx_high = idx_mid;
|
||||
}
|
||||
idx = fn_lower_equal(begin[idx_high]) ? idx_high :
|
||||
(fn_lower_equal(begin[idx_low ]) ? idx_low : -1);
|
||||
} else {
|
||||
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
|
||||
while (idx >= 0 && ! fn_lower_equal(begin[idx]))
|
||||
-- idx;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
template<typename T, typename FN_LOWER_EQUAL>
|
||||
int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lower_equal)
|
||||
{
|
||||
return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_SupportCommon_hpp_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,14 +19,6 @@ inline double layer_z(const SlicingParameters& slicing_params, const size_t laye
|
||||
{
|
||||
return slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height;
|
||||
}
|
||||
inline LayerIndex layer_idx_ceil(const SlicingParameters& slicing_params, const double z)
|
||||
{
|
||||
return LayerIndex(ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
|
||||
}
|
||||
inline LayerIndex layer_idx_floor(const SlicingParameters& slicing_params, const double z)
|
||||
{
|
||||
return LayerIndex(floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
|
||||
}
|
||||
|
||||
inline SupportGeneratorLayer& layer_initialize(
|
||||
SupportGeneratorLayer& layer_new,
|
||||
@@ -53,67 +45,6 @@ inline SupportGeneratorLayer& layer_allocate(
|
||||
return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx);
|
||||
}
|
||||
|
||||
// Generate raft layers, also expand the 1st support layer
|
||||
// in case there is no raft layer to improve support adhesion.
|
||||
SupportGeneratorLayersPtr generate_raft_base(
|
||||
const PrintObject &object,
|
||||
const SupportParameters &support_params,
|
||||
const SlicingParameters &slicing_params,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage);
|
||||
|
||||
// returns sorted layers
|
||||
SupportGeneratorLayersPtr generate_support_layers(
|
||||
PrintObject &object,
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers);
|
||||
|
||||
// Turn some of the base layers into base interface layers.
|
||||
// For soluble interfaces with non-soluble bases, print maximum two first interface layers with the base
|
||||
// extruder to improve adhesion of the soluble filament to the base.
|
||||
std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interface_layers(
|
||||
const PrintObjectConfig& config,
|
||||
const SupportParameters& support_params,
|
||||
const SupportGeneratorLayersPtr& bottom_contacts,
|
||||
const SupportGeneratorLayersPtr& top_contacts,
|
||||
// Input / output, will be merged with output. Only provided for Organic supports.
|
||||
SupportGeneratorLayersPtr& top_interface_layers,
|
||||
SupportGeneratorLayersPtr& top_base_interface_layers,
|
||||
SupportGeneratorLayersPtr& intermediate_layers,
|
||||
SupportGeneratorLayerStorage& layer_storage);
|
||||
|
||||
// Produce the support G-code.
|
||||
// Used by both classic and tree supports.
|
||||
void generate_support_toolpaths(
|
||||
PrintObject &object,
|
||||
SupportLayerPtrs &support_layers,
|
||||
const PrintObjectConfig &config,
|
||||
const SupportParameters &support_params,
|
||||
const SlicingParameters &slicing_params,
|
||||
const SupportGeneratorLayersPtr &raft_layers,
|
||||
const SupportGeneratorLayersPtr &bottom_contacts,
|
||||
const SupportGeneratorLayersPtr &top_contacts,
|
||||
const SupportGeneratorLayersPtr &intermediate_layers,
|
||||
const SupportGeneratorLayersPtr &interface_layers,
|
||||
const SupportGeneratorLayersPtr &base_interface_layers);
|
||||
|
||||
void fill_expolygons_with_sheath_generate_paths(
|
||||
ExtrusionEntitiesPtr& dst,
|
||||
const Polygons& polygons,
|
||||
Fill* filler,
|
||||
float density,
|
||||
ExtrusionRole role,
|
||||
const Flow& flow,
|
||||
bool with_sheath,
|
||||
bool no_sort);
|
||||
|
||||
void export_print_z_polygons_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers);
|
||||
void export_print_z_polygons_and_extrusions_to_svg(const char *path, SupportGeneratorLayer ** const layers, size_t n_layers, SupportLayer& support_layer);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "../libslic3r.h"
|
||||
#include "../Flow.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
@@ -10,6 +11,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
struct SupportParameters {
|
||||
SupportParameters() = delete;
|
||||
SupportParameters(const PrintObject& object)
|
||||
{
|
||||
const PrintConfig& print_config = object.print()->config();
|
||||
@@ -36,13 +38,19 @@ struct SupportParameters {
|
||||
this->num_top_base_interface_layers = size_t(std::min(int(num_top_interface_layers) / 2, 2));
|
||||
this->num_bottom_base_interface_layers = size_t(std::min(int(num_bottom_interface_layers) / 2, 2));
|
||||
} else {
|
||||
this->num_top_base_interface_layers = 0;
|
||||
this->num_bottom_base_interface_layers = 0;
|
||||
// QDS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion
|
||||
// Note: support materials (such as Supp.W) can't be used as support base now, so support interface and base are still using different filaments even if
|
||||
// support_filament==0
|
||||
bool differnt_support_interface_filament = object_config.support_interface_filament != 0 &&
|
||||
object_config.support_interface_filament != object_config.support_filament;
|
||||
this->num_top_base_interface_layers = differnt_support_interface_filament ? 1 : 0;
|
||||
this->num_bottom_base_interface_layers = differnt_support_interface_filament ? 1 : 0;
|
||||
}
|
||||
}
|
||||
this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height));
|
||||
this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height));
|
||||
this->support_material_interface_flow = Slic3r::support_material_interface_flow(&object, float(slicing_params.layer_height));
|
||||
this->raft_interface_flow = support_material_interface_flow;
|
||||
|
||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||
this->support_layer_height_min = scaled<coord_t>(0.01);
|
||||
@@ -89,6 +97,8 @@ struct SupportParameters {
|
||||
this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.));
|
||||
this->interface_spacing = object_config.support_interface_spacing.value + this->support_material_interface_flow.spacing();
|
||||
this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->interface_spacing);
|
||||
double raft_interface_spacing = object_config.support_interface_spacing.value + this->raft_interface_flow.spacing();
|
||||
this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing);
|
||||
this->support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing();
|
||||
this->support_density = std::min(1., this->support_material_flow.spacing() / this->support_spacing);
|
||||
if (object_config.support_interface_top_layers.value == 0) {
|
||||
@@ -98,11 +108,12 @@ struct SupportParameters {
|
||||
}
|
||||
|
||||
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
|
||||
this->with_sheath = /*is_tree(object_config.support_type) &&*/ object_config.tree_support_wall_count > 0;
|
||||
this->with_sheath = object_config.tree_support_wall_count > 0;
|
||||
this->base_fill_pattern =
|
||||
support_pattern == smpHoneycomb ? ipHoneycomb :
|
||||
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
|
||||
this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||
this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase;
|
||||
if (object_config.support_interface_pattern == smipGrid)
|
||||
this->contact_fill_pattern = ipGrid;
|
||||
else if (object_config.support_interface_pattern == smipRectilinearInterlaced)
|
||||
@@ -113,6 +124,67 @@ struct SupportParameters {
|
||||
object_config.support_interface_pattern == smipConcentric ?
|
||||
ipConcentric :
|
||||
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||
|
||||
this->raft_angle_1st_layer = 0.f;
|
||||
this->raft_angle_base = 0.f;
|
||||
this->raft_angle_interface = 0.f;
|
||||
if (slicing_params.base_raft_layers > 1) {
|
||||
assert(slicing_params.raft_layers() >= 4);
|
||||
// There are all raft layer types (1st layer, base, interface & contact layers) available.
|
||||
this->raft_angle_1st_layer = this->interface_angle;
|
||||
this->raft_angle_base = this->base_angle;
|
||||
this->raft_angle_interface = this->interface_angle;
|
||||
if ((slicing_params.interface_raft_layers & 1) == 0)
|
||||
// Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface.
|
||||
this->raft_angle_interface += float(0.5 * M_PI);
|
||||
} else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) {
|
||||
assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3);
|
||||
// 1st layer, interface & contact layers available.
|
||||
this->raft_angle_1st_layer = this->base_angle;
|
||||
this->raft_angle_interface = this->interface_angle + 0.5 * M_PI;
|
||||
} else if (slicing_params.interface_raft_layers == 1) {
|
||||
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
|
||||
assert(slicing_params.base_raft_layers == 0);
|
||||
assert(slicing_params.interface_raft_layers == 1);
|
||||
assert(slicing_params.raft_layers() == 1);
|
||||
this->raft_angle_1st_layer = float(0.5 * M_PI);
|
||||
this->raft_angle_interface = this->raft_angle_1st_layer;
|
||||
} else {
|
||||
// No raft.
|
||||
assert(slicing_params.base_raft_layers == 0);
|
||||
assert(slicing_params.interface_raft_layers == 0);
|
||||
assert(slicing_params.raft_layers() == 0);
|
||||
}
|
||||
|
||||
support_extrusion_width = object_config.support_line_width.value > 0 ? object_config.support_line_width : object_config.line_width;
|
||||
// Check if set to zero, use default if so.
|
||||
if (support_extrusion_width <= 0.0) {
|
||||
const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1);
|
||||
support_extrusion_width = Flow::auto_extrusion_width(FlowRole::frSupportMaterial, (float) nozzle_diameter);
|
||||
}
|
||||
|
||||
independent_layer_height = print_config.independent_support_layer_height;
|
||||
|
||||
// force double walls everywhere if wall count is larger than 1
|
||||
tree_branch_diameter_double_wall_area_scaled = object_config.tree_support_wall_count.value > 1 ? 0.1 :
|
||||
object_config.tree_support_wall_count.value == 0 ? 0.25 * sqr(scaled<double>(5.0)) * M_PI :
|
||||
std::numeric_limits<double>::max();
|
||||
|
||||
support_style = object_config.support_style;
|
||||
if (support_style == smsDefault) {
|
||||
if (is_tree(object_config.support_type)) {
|
||||
// organic support doesn't work with variable layer heights (including adaptive layer height and height range modifier, see #4313)
|
||||
if (!object.has_variable_layer_heights) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "tree support default to organic support";
|
||||
support_style = smsTreeOrganic;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << "tree support default to hybrid tree due to adaptive layer height";
|
||||
support_style = smsTreeHybrid;
|
||||
}
|
||||
} else {
|
||||
support_style = smsGrid;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Both top / bottom contacts and interfaces are soluble.
|
||||
bool soluble_interface;
|
||||
@@ -142,6 +214,8 @@ struct SupportParameters {
|
||||
Flow support_material_flow;
|
||||
Flow support_material_interface_flow;
|
||||
Flow support_material_bottom_interface_flow;
|
||||
// Flow at raft inteface & contact layers.
|
||||
Flow raft_interface_flow;
|
||||
coordf_t support_extrusion_width;
|
||||
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
|
||||
bool can_merge_support_regions;
|
||||
@@ -157,13 +231,29 @@ struct SupportParameters {
|
||||
coordf_t interface_spacing;
|
||||
coordf_t support_expansion=0;
|
||||
coordf_t interface_density;
|
||||
// Density of the raft interface and contact layers.
|
||||
coordf_t raft_interface_density;
|
||||
coordf_t support_spacing;
|
||||
coordf_t support_density;
|
||||
SupportMaterialStyle support_style = smsDefault;
|
||||
|
||||
InfillPattern base_fill_pattern;
|
||||
InfillPattern interface_fill_pattern;
|
||||
// Pattern of the raft interface and contact layers.
|
||||
InfillPattern raft_interface_fill_pattern;
|
||||
InfillPattern contact_fill_pattern;
|
||||
bool with_sheath;
|
||||
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
|
||||
double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(5.0)) * M_PI;;
|
||||
|
||||
float raft_angle_1st_layer;
|
||||
float raft_angle_base;
|
||||
float raft_angle_interface;
|
||||
|
||||
// Produce a raft interface angle for a given SupportLayer::interface_id()
|
||||
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.)); }
|
||||
|
||||
bool independent_layer_height = false;
|
||||
const double thresh_big_overhang = Slic3r::sqr(scale_(10));
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,9 +28,9 @@ struct LayerHeightData
|
||||
{
|
||||
coordf_t print_z = 0;
|
||||
coordf_t height = 0;
|
||||
size_t next_layer_nr = 0;
|
||||
size_t obj_layer_nr = 0;
|
||||
LayerHeightData() = default;
|
||||
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
|
||||
LayerHeightData(coordf_t z, coordf_t h, size_t obj_layer) : print_z(z), height(h), obj_layer_nr(obj_layer) {}
|
||||
coordf_t bottom_z() {
|
||||
return print_z - height;
|
||||
}
|
||||
@@ -202,7 +202,7 @@ public:
|
||||
* \param radius_sample_resolution Sample size used to round requested node radii.
|
||||
* \param collision_resolution
|
||||
*/
|
||||
TreeSupportData(const PrintObject& object, coordf_t max_move, coordf_t radius_sample_resolution, coordf_t collision_resolution);
|
||||
TreeSupportData(const PrintObject& object, coordf_t radius_sample_resolution, coordf_t collision_resolution);
|
||||
~TreeSupportData() {
|
||||
clear_nodes();
|
||||
}
|
||||
@@ -247,10 +247,10 @@ public:
|
||||
SupportNode* create_node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, SupportNode* parent,
|
||||
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_ = 0, coordf_t radius_ = 0);
|
||||
void clear_nodes();
|
||||
void remove_invalid_nodes();
|
||||
std::vector<LayerHeightData> layer_heights;
|
||||
|
||||
std::vector<SupportNode*> contact_nodes;
|
||||
std::vector<std::unique_ptr<SupportNode>> contact_nodes;
|
||||
// ExPolygon m_machine_border;
|
||||
|
||||
private:
|
||||
/*!
|
||||
@@ -260,7 +260,7 @@ private:
|
||||
coordf_t radius;
|
||||
size_t layer_nr;
|
||||
int recursions;
|
||||
|
||||
|
||||
};
|
||||
struct RadiusLayerPairEquality {
|
||||
constexpr bool operator()(const RadiusLayerPair& _Left, const RadiusLayerPair& _Right) const {
|
||||
@@ -296,7 +296,7 @@ private:
|
||||
*/
|
||||
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
|
||||
|
||||
tbb::spin_mutex m_mutex;
|
||||
tbb::spin_mutex m_mutex;
|
||||
|
||||
public:
|
||||
bool is_slim = false;
|
||||
@@ -305,11 +305,7 @@ public:
|
||||
*/
|
||||
coordf_t m_xy_distance;
|
||||
|
||||
/*!
|
||||
* \brief The maximum distance that the centrepoint of a tree branch may
|
||||
* move in consequtive layers
|
||||
*/
|
||||
coordf_t m_max_move;
|
||||
double branch_scale_factor = 1.0; // tan(45 degrees)
|
||||
|
||||
/*!
|
||||
* \brief Sample resolution for radius values.
|
||||
@@ -328,6 +324,8 @@ public:
|
||||
// union contours of all layers below
|
||||
std::vector<ExPolygons> m_layer_outlines_below;
|
||||
|
||||
std::vector<double> m_max_move_distances;
|
||||
|
||||
/*!
|
||||
* \brief Caches for the collision, avoidance and internal model polygons
|
||||
* at given radius and layer indices.
|
||||
@@ -336,7 +334,7 @@ public:
|
||||
* generally considered OK as the functions are still logically const
|
||||
* (ie there is no difference in behaviour for the user betweeen
|
||||
* calculating the values each time vs caching the results).
|
||||
*
|
||||
*
|
||||
* coconut: previously stl::unordered_map is used which seems problematic with tbb::parallel_for.
|
||||
* So we change to tbb::concurrent_unordered_map
|
||||
*/
|
||||
@@ -366,6 +364,10 @@ public:
|
||||
*/
|
||||
TreeSupport(PrintObject& object, const SlicingParameters &slicing_params);
|
||||
|
||||
void move_bounds_to_contact_nodes(std::vector<TreeSupport3D::SupportElements> &move_bounds,
|
||||
PrintObject &print_object,
|
||||
const TreeSupport3D::TreeSupportSettings &config);
|
||||
|
||||
/*!
|
||||
* \brief Create the areas that need support.
|
||||
*
|
||||
@@ -377,13 +379,26 @@ public:
|
||||
|
||||
void detect_overhangs(bool check_support_necessity = false);
|
||||
|
||||
SupportNode* create_node(const Point position,
|
||||
const int distance_to_top,
|
||||
const int obj_layer_nr,
|
||||
const int support_roof_layers_below,
|
||||
const bool to_buildplate,
|
||||
SupportNode* parent,
|
||||
coordf_t print_z_,
|
||||
coordf_t height_,
|
||||
coordf_t dist_mm_to_top_ = 0,
|
||||
coordf_t radius_ = 0)
|
||||
{
|
||||
return m_ts_data->create_node(position, distance_to_top, obj_layer_nr, support_roof_layers_below, to_buildplate, parent, print_z_, height_, dist_mm_to_top_, radius_);
|
||||
}
|
||||
|
||||
int avg_node_per_layer = 0;
|
||||
float nodes_angle = 0;
|
||||
float nodes_angle = 0;
|
||||
bool has_sharp_tails = false;
|
||||
bool has_cantilever = false;
|
||||
double max_cantilever_dist = 0;
|
||||
SupportType support_type;
|
||||
SupportMaterialStyle support_style;
|
||||
|
||||
std::unique_ptr<FillLightning::Generator> generator;
|
||||
std::unordered_map<double, size_t> printZ_to_lightninglayer;
|
||||
@@ -398,6 +413,8 @@ public:
|
||||
|
||||
enum OverhangType { Detected = 0, Enforced, SharpTail };
|
||||
std::map<const ExPolygon*, OverhangType> overhang_types;
|
||||
std::vector<std::pair<Vec3f, Vec3f>> m_vertical_enforcer_points;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Generator for model collision, avoidance and internal guide volumes
|
||||
@@ -405,6 +422,7 @@ private:
|
||||
* Lazily computes volumes as needed.
|
||||
* \warning This class is NOT currently thread-safe and should not be accessed in OpenMP blocks
|
||||
*/
|
||||
std::vector<std::vector<SupportNode*>> contact_nodes;
|
||||
std::shared_ptr<TreeSupportData> m_ts_data;
|
||||
std::unique_ptr<TreeSupport3D::TreeModelVolumes> m_model_volumes;
|
||||
PrintObject *m_object;
|
||||
@@ -417,14 +435,17 @@ private:
|
||||
size_t m_highest_overhang_layer = 0;
|
||||
std::vector<std::vector<MinimumSpanningTree>> m_spanning_trees;
|
||||
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 MIN_BRANCH_RADIUS = 0.5;
|
||||
coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
|
||||
coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0;
|
||||
float tree_support_branch_diameter_angle = 5.0;
|
||||
coord_t m_min_radius = scale_(1); // in mm
|
||||
coordf_t base_radius = 0.0;
|
||||
const coordf_t MAX_BRANCH_RADIUS = 10.0;
|
||||
const coordf_t MIN_BRANCH_RADIUS = 0.4;
|
||||
const coordf_t MAX_BRANCH_RADIUS_FIRST_LAYER = 12.0;
|
||||
const coordf_t MIN_BRANCH_RADIUS_FIRST_LAYER = 2.0;
|
||||
double diameter_angle_scale_factor = tan(5.0*M_PI/180.0);
|
||||
// minimum roof area (1 mm^2), area smaller than this value will not have interface
|
||||
const double minimum_roof_area{SQ(scaled<double>(1.))};
|
||||
float top_z_distance = 0.0;
|
||||
|
||||
bool is_strong = false;
|
||||
bool is_slim = false;
|
||||
bool with_infill = false;
|
||||
@@ -441,7 +462,7 @@ private:
|
||||
* save the resulting support polygons to.
|
||||
* \param contact_nodes The nodes to draw as support.
|
||||
*/
|
||||
void draw_circles(const std::vector<std::vector<SupportNode*>>& contact_nodes);
|
||||
void draw_circles();
|
||||
|
||||
/*!
|
||||
* \brief Drops down the nodes of the tree support towards the build plate.
|
||||
@@ -455,18 +476,18 @@ private:
|
||||
* dropped down. The nodes are dropped to lower layers inside the same
|
||||
* vector of layers.
|
||||
*/
|
||||
void drop_nodes(std::vector<std::vector<SupportNode *>> &contact_nodes);
|
||||
void drop_nodes();
|
||||
|
||||
void smooth_nodes(std::vector<std::vector<SupportNode *>> &contact_nodes);
|
||||
void smooth_nodes();
|
||||
|
||||
void smooth_nodes(std::vector<std::vector<SupportNode*>>& contact_nodes, const TreeSupport3D::TreeSupportSettings& config);
|
||||
void smooth_nodes(const TreeSupport3D::TreeSupportSettings& config);
|
||||
|
||||
/*! QDS: MusangKing: maximum layer height
|
||||
* \brief Optimize the generation of tree support by pre-planning the layer_heights
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<SupportNode *>> &contact_nodes);
|
||||
std::vector<LayerHeightData> plan_layer_heights();
|
||||
/*!
|
||||
* \brief Creates points where support contacts the model.
|
||||
*
|
||||
@@ -480,7 +501,7 @@ private:
|
||||
* \return For each layer, a list of points where the tree should connect
|
||||
* with the model.
|
||||
*/
|
||||
void generate_contact_points(std::vector<std::vector<SupportNode*>>& contact_nodes);
|
||||
void generate_contact_points();
|
||||
|
||||
/*!
|
||||
* \brief Add a node to the next layer.
|
||||
@@ -494,8 +515,10 @@ private:
|
||||
coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, size_t tip_layers, double diameter_angle_scale_factor);
|
||||
// get unscaled radius(mm) of node based on the distance mm to top
|
||||
coordf_t calc_branch_radius(coordf_t base_radius, coordf_t mm_to_top, double diameter_angle_scale_factor, bool use_min_distance=true);
|
||||
coordf_t get_radius(const SupportNode* node, coordf_t base_radius);
|
||||
coordf_t calc_radius(coordf_t mm_to_top);
|
||||
coordf_t get_radius(const SupportNode* node);
|
||||
ExPolygons get_avoidance(coordf_t radius, size_t obj_layer_nr);
|
||||
// layer's expolygon expanded by radius+m_xy_distance
|
||||
ExPolygons get_collision(coordf_t radius, size_t layer_nr);
|
||||
// get Polygons instead of ExPolygons
|
||||
Polygons get_collision_polys(coordf_t radius, size_t layer_nr);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "Polygon.hpp"
|
||||
#include "Polyline.hpp"
|
||||
#include "MutablePolygon.hpp"
|
||||
#include "SupportMaterial.hpp"
|
||||
#include "SupportCommon.hpp"
|
||||
#include "TriangleMeshSlicer.hpp"
|
||||
#include "TreeSupport.hpp"
|
||||
#include "I18N.hpp"
|
||||
@@ -37,6 +37,7 @@
|
||||
#define TBB_PREVIEW_GLOBAL_CONTROL 1
|
||||
#include <tbb/global_control.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_for_each.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
||||
#if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32)
|
||||
@@ -63,16 +64,6 @@ namespace Slic3r
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
|
||||
enum class LineStatus
|
||||
{
|
||||
INVALID,
|
||||
TO_MODEL,
|
||||
TO_MODEL_GRACIOUS,
|
||||
TO_MODEL_GRACIOUS_SAFE,
|
||||
TO_BP,
|
||||
TO_BP_SAFE
|
||||
};
|
||||
|
||||
using LineInformation = std::vector<std::pair<Point, LineStatus>>;
|
||||
using LineInformations = std::vector<LineInformation>;
|
||||
using namespace std::literals;
|
||||
@@ -80,28 +71,28 @@ using namespace std::literals;
|
||||
static inline void validate_range(const Point &pt)
|
||||
{
|
||||
static constexpr const int32_t hi = 65536 * 16384;
|
||||
if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi)
|
||||
throw ClipperLib::clipperException("Coordinate outside allowed range");
|
||||
if (pt.x() > hi || pt.y() > hi || -pt.x() > hi || -pt.y() > hi)
|
||||
throw ClipperLib::clipperException("Coordinate outside allowed range");
|
||||
}
|
||||
|
||||
static inline void validate_range(const Points &points)
|
||||
static inline void validate_range(const Points &points)
|
||||
{
|
||||
for (const Point &p : points)
|
||||
validate_range(p);
|
||||
}
|
||||
|
||||
static inline void validate_range(const MultiPoint &mp)
|
||||
static inline void validate_range(const MultiPoint &mp)
|
||||
{
|
||||
validate_range(mp.points);
|
||||
}
|
||||
|
||||
static inline void validate_range(const Polygons &polygons)
|
||||
static inline void validate_range(const Polygons &polygons)
|
||||
{
|
||||
for (const Polygon &p : polygons)
|
||||
validate_range(p);
|
||||
}
|
||||
|
||||
static inline void validate_range(const Polylines &polylines)
|
||||
static inline void validate_range(const Polylines &polylines)
|
||||
{
|
||||
for (const Polyline &p : polylines)
|
||||
validate_range(p);
|
||||
@@ -150,7 +141,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
|
||||
size_t largest_printed_mesh_idx = 0;
|
||||
|
||||
// Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group,
|
||||
// Group all meshes that can be processed together. NOTE this is different from mesh-groups! Only one setting object is needed per group,
|
||||
// as different settings in the same group may only occur in the tip, which uses the original settings objects from the meshes.
|
||||
for (size_t object_id : print_object_ids) {
|
||||
const PrintObject &print_object = *print.get_object(object_id);
|
||||
@@ -249,7 +240,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
|
||||
size_t num_overhang_layers = support_auto ? num_object_layers : std::min(num_object_layers, std::max(size_t(support_enforce_layers), enforcers_layers.size()));
|
||||
tbb::parallel_for(tbb::blocked_range<LayerIndex>(1, num_overhang_layers),
|
||||
[&print_object, &config, &print_config, &enforcers_layers, &blockers_layers,
|
||||
[&print_object, &config, &print_config, &enforcers_layers, &blockers_layers,
|
||||
support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, radius_sample_resolution, &throw_on_cancel, &out]
|
||||
(const tbb::blocked_range<LayerIndex> &range) {
|
||||
for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
|
||||
@@ -284,7 +275,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
overhangs = diff(overhangs, offset_ex(union_(blockers_layers[layer_id]), scale_(radius_sample_resolution)), ApplySafetyOffset::Yes);
|
||||
//if (config.bridge_no_support) {
|
||||
// for (const LayerRegion *layerm : current_layer.regions())
|
||||
// remove_bridges_from_contacts(print_config, lower_layer, *layerm,
|
||||
// remove_bridges_from_contacts(print_config, lower_layer, *layerm,
|
||||
// float(layerm->flow(frExternalPerimeter).scaled_width()), overhangs);
|
||||
//}
|
||||
}
|
||||
@@ -305,7 +296,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
enforced_overhangs = diff(offset(union_ex(enforced_overhangs), enforcer_overhang_offset),
|
||||
lower_layer.lslices);
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
// if (! intersecting_edges(enforced_overhangs).empty())
|
||||
// if (! intersecting_edges(enforced_overhangs).empty())
|
||||
{
|
||||
static int irun = 0;
|
||||
SVG::export_expolygons(debug_out_path("treesupport-self-intersections-%d.svg", ++irun),
|
||||
@@ -318,7 +309,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
|
||||
//check_self_intersections(overhangs, "generate_overhangs - enforcers");
|
||||
}
|
||||
}
|
||||
}
|
||||
out[layer_id + num_raft_layers] = std::move(overhangs);
|
||||
throw_on_cancel();
|
||||
}
|
||||
@@ -350,6 +341,28 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
return max_layer;
|
||||
}
|
||||
|
||||
// picked from convert_lines_to_internal()
|
||||
[[nodiscard]] LineStatus get_avoidance_status(const Point& p, coord_t radius, LayerIndex layer_idx,
|
||||
const TreeModelVolumes& volumes, const TreeSupportSettings& config)
|
||||
{
|
||||
const bool min_xy_dist = config.xy_distance > config.xy_min_distance;
|
||||
|
||||
LineStatus type = LineStatus::INVALID;
|
||||
|
||||
if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, false, min_xy_dist), p))
|
||||
type = LineStatus::TO_BP_SAFE;
|
||||
else if (!contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, false, min_xy_dist), p))
|
||||
type = LineStatus::TO_BP;
|
||||
else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::FastSafe, true, min_xy_dist), p))
|
||||
type = LineStatus::TO_MODEL_GRACIOUS_SAFE;
|
||||
else if (config.support_rests_on_model && !contains(volumes.getAvoidance(radius, layer_idx, TreeModelVolumes::AvoidanceType::Fast, true, min_xy_dist), p))
|
||||
type = LineStatus::TO_MODEL_GRACIOUS;
|
||||
else if (config.support_rests_on_model && !contains(volumes.getCollision(radius, layer_idx, min_xy_dist), p))
|
||||
type = LineStatus::TO_MODEL;
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Converts a Polygons object representing a line into the internal format.
|
||||
*
|
||||
@@ -431,7 +444,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
|
||||
return true;
|
||||
if (config.support_rests_on_model && (p.second != LineStatus::TO_BP && p.second != LineStatus::TO_BP_SAFE))
|
||||
return ! contains(
|
||||
p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ?
|
||||
p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ?
|
||||
volumes.getAvoidance(config.getRadius(0), current_layer - 1, p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE ? AvoidanceType::FastSafe : AvoidanceType::Fast, true, min_xy_dist) :
|
||||
volumes.getCollision(config.getRadius(0), current_layer - 1, min_xy_dist),
|
||||
p.first);
|
||||
@@ -503,7 +516,7 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
// Squared distance of "start_pt" from the ray (p0, p1).
|
||||
double l2_from_line = xf.squaredNorm();
|
||||
// Squared distance of an intersection point of a circle with center at the foot point.
|
||||
if (double l2_intersection = dist2 - l2_from_line;
|
||||
if (double l2_intersection = dist2 - l2_from_line;
|
||||
l2_intersection > - SCALED_EPSILON) {
|
||||
// The ray (p0, p1) touches or intersects a circle centered at "start_pt" with radius "dist".
|
||||
// Distance of the circle intersection point from the foot point.
|
||||
@@ -598,7 +611,7 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
} else {
|
||||
if (current_point == next_point->first) {
|
||||
// In case a fixpoint is encountered, better aggressively overcompensate so the code does not become stuck here...
|
||||
BOOST_LOG_TRIVIAL(warning) << "Tree Support: Encountered a fixpoint in polyline_sample_next_point_at_distance. This is expected to happen if the distance (currently " << next_distance <<
|
||||
BOOST_LOG_TRIVIAL(warning) << "Tree Support: Encountered a fixpoint in polyline_sample_next_point_at_distance. This is expected to happen if the distance (currently " << next_distance <<
|
||||
") is smaller than 100";
|
||||
tree_supports_show_error("Encountered issue while placing tips. Some tips may be missing."sv, true);
|
||||
if (next_distance > 2 * current_distance)
|
||||
@@ -663,9 +676,9 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
int divisor = static_cast<int>(angles.size());
|
||||
int index = ((layer_idx % divisor) + divisor) % divisor;
|
||||
const AngleRadians fill_angle = angles[index];
|
||||
Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon,
|
||||
roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier,
|
||||
fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin,
|
||||
Infill roof_computation(pattern, true /* zig_zaggify_infill */, connect_polygons, polygon,
|
||||
roof ? config.support_roof_line_width : config.support_line_width, support_infill_distance, support_roof_overlap, infill_multiplier,
|
||||
fill_angle, z, support_shift, config.resolution, wall_line_count, infill_origin,
|
||||
perimeter_gaps, connected_zigzags, use_endpieces, false /* skip_some_zags */, zag_skip_count, pocket_size);
|
||||
Polygons polygons;
|
||||
Polygons lines;
|
||||
@@ -679,7 +692,7 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
|
||||
filler->layer_id = layer_idx;
|
||||
filler->spacing = flow.spacing();
|
||||
filler->angle = roof ?
|
||||
filler->angle = roof ?
|
||||
//fixme support_layer.interface_id() instead of layer_idx
|
||||
(support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) :
|
||||
support_params.base_angle;
|
||||
@@ -750,7 +763,7 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
result = union_(offset(to_polylines(first), scaled<float>(0.002), jtMiter, 1.2), offset(to_polylines(second), scaled<float>(0.002), jtMiter, 1.2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -767,7 +780,7 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
|
||||
{
|
||||
bool do_final_difference = last_step_offset_without_check == 0;
|
||||
Polygons ret = safe_union(me); // ensure sane input
|
||||
|
||||
|
||||
// Trim the collision polygons with the region of interest for diff() efficiency.
|
||||
Polygons collision_trimmed_buffer;
|
||||
auto collision_trimmed = [&collision_trimmed_buffer, &collision, &ret, distance]() -> const Polygons& {
|
||||
@@ -846,7 +859,7 @@ public:
|
||||
// called by sample_overhang_area()
|
||||
void add_points_along_lines(
|
||||
// Insert points (tree tips or top contact interfaces) along these lines.
|
||||
LineInformations lines,
|
||||
LineInformations lines,
|
||||
// Start at this layer.
|
||||
LayerIndex insert_layer_idx,
|
||||
// Insert this number of interface layers.
|
||||
@@ -886,7 +899,7 @@ public:
|
||||
// add all points that would not be valid
|
||||
for (const LineInformation &line : points)
|
||||
for (const std::pair<Point, LineStatus> &point_data : line)
|
||||
add_point_as_influence_area(point_data, this_layer_idx,
|
||||
add_point_as_influence_area(point_data, this_layer_idx,
|
||||
// don't move until
|
||||
roof_tip_layers - dtt_roof_tip,
|
||||
// supports roof
|
||||
@@ -973,7 +986,7 @@ private:
|
||||
// Temps
|
||||
static constexpr const auto m_base_radius = scaled<int>(0.01);
|
||||
const Polygon m_base_circle { make_circle(m_base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION) };
|
||||
|
||||
|
||||
// Mutexes, guards
|
||||
std::mutex m_mutex_movebounds;
|
||||
std::vector<std::unordered_set<Point, PointHash>> m_already_inserted;
|
||||
@@ -1058,10 +1071,10 @@ void finalize_raft_contact(
|
||||
// Produce
|
||||
// 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
|
||||
// 2) Tree tips supporting either the roof layers or the object itself.
|
||||
// num_support_roof_layers should always be respected:
|
||||
// num_support_roof_layers should always be respected:
|
||||
// If num_support_roof_layers contact layers could not be produced, then the tree tip
|
||||
// is augmented with SupportElementState::missing_roof_layers
|
||||
// and the top "missing_roof_layers" of such particular tree tips are supposed to be coverted to
|
||||
// and the top "missing_roof_layers" of such particular tree tips are supposed to be coverted to
|
||||
// roofs aka interface layers by the tool path generator.
|
||||
void sample_overhang_area(
|
||||
// Area to support
|
||||
@@ -1073,18 +1086,18 @@ void sample_overhang_area(
|
||||
const size_t layer_idx,
|
||||
// Maximum number of roof (contact, interface) layers between the overhang and tree tips to be generated.
|
||||
const size_t num_support_roof_layers,
|
||||
//
|
||||
//
|
||||
const coord_t connect_length,
|
||||
// Configuration classes
|
||||
const TreeSupportMeshGroupSettings& mesh_group_settings,
|
||||
// Configuration & Output
|
||||
RichInterfacePlacer& interface_placer)
|
||||
{
|
||||
// Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area
|
||||
// is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument
|
||||
// Assumption is that roof will support roof further up to avoid a lot of unnecessary branches. Each layer down it is checked whether the roof area
|
||||
// is still large enough to be a roof and aborted as soon as it is not. This part was already reworked a few times, and there could be an argument
|
||||
// made to change it again if there are actual issues encountered regarding supporting roofs.
|
||||
// Main problem is that some patterns change each layer, so just calculating points and checking if they are still valid an layer below is not useful,
|
||||
// as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from
|
||||
// Main problem is that some patterns change each layer, so just calculating points and checking if they are still valid an layer below is not useful,
|
||||
// as the pattern may be different one layer below. Same with calculating which points are now no longer being generated as result from
|
||||
// a decreasing roof, as there is no guarantee that a line will be above these points. Implementing a separate roof support behavior
|
||||
// for each pattern harms maintainability as it very well could be >100 LOC
|
||||
auto generate_roof_lines = [&interface_placer, &mesh_group_settings](const Polygons& area, LayerIndex layer_idx) -> Polylines {
|
||||
@@ -1151,7 +1164,7 @@ void sample_overhang_area(
|
||||
|
||||
if (overhang_lines.empty()) {
|
||||
// support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance, but not only is this the only reasonable choice,
|
||||
// but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour.
|
||||
// but it ensures consistant behaviour as some infill patterns generate each line segment as its own polyline part causing a similar line forming behaviour.
|
||||
// This is not doen when a roof is above as the roof will support the model and the trees only need to support the roof
|
||||
bool supports_roof = dtt_roof > 0;
|
||||
bool continuous_tips = !supports_roof && large_horizontal_roof;
|
||||
@@ -1165,8 +1178,8 @@ void sample_overhang_area(
|
||||
const size_t min_support_points = std::max(coord_t(1), std::min(coord_t(3), coord_t(total_length(overhang_area) / connect_length)));
|
||||
if (point_count <= min_support_points) {
|
||||
// add the outer wall (of the overhang) to ensure it is correct supported instead. Try placing the support points in a way that they fully support the outer wall, instead of just the with half of the the support line width.
|
||||
// I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them
|
||||
// will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60 degrees so there is a fallback,
|
||||
// I assume that even small overhangs are over one line width wide, so lets try to place the support points in a way that the full support area generated from them
|
||||
// will support the overhang (if this is not done it may only be half). This WILL NOT be the case when supporting an angle of about < 60 degrees so there is a fallback,
|
||||
// as some support is better than none.
|
||||
Polygons reduced_overhang_area = offset(union_ex(overhang_area), -interface_placer.config.support_line_width / 2.2, jtMiter, 1.2);
|
||||
polylines = ensure_maximum_distance_polyline(
|
||||
@@ -1201,15 +1214,6 @@ void sample_overhang_area(
|
||||
}
|
||||
}
|
||||
|
||||
inline SupportGeneratorLayer& layer_allocate(
|
||||
SupportGeneratorLayerStorage& layer_storage,
|
||||
SupporLayerType layer_type,
|
||||
const SlicingParameters &slicing_params,
|
||||
size_t layer_idx)
|
||||
{
|
||||
auto& layer = layer_storage.allocate(layer_type);
|
||||
return layer_initialize(layer, layer_type, slicing_params, layer_idx);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Creates the initial influence areas (that can later be propagated down) by placing them below the overhang.
|
||||
@@ -1243,11 +1247,11 @@ void generate_initial_areas(
|
||||
|
||||
const coord_t connect_length = (config.support_line_width * 100. / mesh_group_settings.support_tree_top_rate) + std::max(2. * config.min_radius - 1.0 * config.support_line_width, 0.0);
|
||||
// As r*r=x*x+y*y (circle equation): If a circle with center at (0,0) the top most point is at (0,r) as in y=r.
|
||||
// This calculates how far one has to move on the x-axis so that y=r-support_line_width/2.
|
||||
// This calculates how far one has to move on the x-axis so that y=r-support_line_width/2.
|
||||
// In other words how far does one need to move on the x-axis to be support_line_width/2 away from the circle line.
|
||||
// As a circle is round this length is identical for every axis as long as the 90 degrees angle between both remains.
|
||||
const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ?
|
||||
config.min_radius / 2 :
|
||||
const coord_t circle_length_to_half_linewidth_change = config.min_radius < config.support_line_width ?
|
||||
config.min_radius / 2 :
|
||||
scale_(sqrt(sqr(unscale<double>(config.min_radius)) - sqr(unscale<double>(config.min_radius - 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.
|
||||
//FIXME Vojtech: This is not sufficient for support enforcers to work.
|
||||
@@ -1260,16 +1264,16 @@ void generate_initial_areas(
|
||||
const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers;
|
||||
const bool roof_enabled = num_support_roof_layers > 0;
|
||||
const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr<double>(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area);
|
||||
// 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.
|
||||
// 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.
|
||||
// Used only if not min_xy_dist.
|
||||
coord_t max_overhang_insert_lag = 0;
|
||||
if (config.z_distance_top_layers > 0) {
|
||||
max_overhang_insert_lag = 2 * config.z_distance_top_layers;
|
||||
|
||||
//FIXME
|
||||
//FIXME
|
||||
if (mesh_group_settings.support_angle > EPSILON && mesh_group_settings.support_angle < 0.5 * M_PI - EPSILON) {
|
||||
//FIXME mesh_group_settings.support_angle does not apply to enforcers and also it does not apply to automatic support angle (by half the external perimeter width).
|
||||
// take the least restrictive avoidance possible
|
||||
@@ -1315,12 +1319,12 @@ void generate_initial_areas(
|
||||
relevant_forbidden = offset(union_ex(relevant_forbidden_raw), scaled<float>(0.005), jtMiter, 1.2);
|
||||
}
|
||||
|
||||
// every overhang has saved if a roof should be generated for it. This can NOT be done in the for loop as an area may NOT have a roof
|
||||
// even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and
|
||||
// every overhang has saved if a roof should be generated for it. This can NOT be done in the for loop as an area may NOT have a roof
|
||||
// even if it is larger than the minimum_roof_area when it is only larger because of the support horizontal expansion and
|
||||
// it would not have a roof if the overhang is offset by support roof horizontal expansion instead. (At least this is the current behavior of the regular support)
|
||||
Polygons overhang_regular;
|
||||
{
|
||||
// When support_offset = 0 safe_offset_inc will only be the difference between overhang_raw and relevant_forbidden, that has to be calculated anyway.
|
||||
// When support_offset = 0 safe_offset_inc will only be the difference between overhang_raw and relevant_forbidden, that has to be calculated anyway.
|
||||
overhang_regular = safe_offset_inc(overhang_raw, mesh_group_settings.support_offset, relevant_forbidden, config.min_radius * 1.75 + config.xy_min_distance, 0, 1);
|
||||
//check_self_intersections(overhang_regular, "overhang_regular1");
|
||||
|
||||
@@ -1339,7 +1343,7 @@ void generate_initial_areas(
|
||||
for (coord_t extra_total_offset_acc = 0; ! remaining_overhang.empty() && extra_total_offset_acc + config.support_line_width / 8 < extra_outset; ) {
|
||||
const coord_t offset_current_step = std::min(
|
||||
extra_total_offset_acc + 2 * config.support_line_width > config.min_radius ?
|
||||
config.support_line_width / 8 :
|
||||
config.support_line_width / 8 :
|
||||
circle_length_to_half_linewidth_change,
|
||||
extra_outset - extra_total_offset_acc);
|
||||
extra_total_offset_acc += offset_current_step;
|
||||
@@ -1359,10 +1363,10 @@ void generate_initial_areas(
|
||||
LineInformations overhang_lines;
|
||||
{
|
||||
//Vojtech: Generate support heads at support_tree_branch_distance spacing by producing a zig-zag infill at support_tree_branch_distance spacing,
|
||||
// which is then resmapled
|
||||
// support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance,
|
||||
// mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate
|
||||
// each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that
|
||||
// which is then resmapled
|
||||
// support_line_width to form a line here as otherwise most will be unsupported. Technically this violates branch distance,
|
||||
// mbut not only is this the only reasonable choice, but it ensures consistent behavior as some infill patterns generate
|
||||
// each line segment as its own polyline part causing a similar line forming behavior. Also it is assumed that
|
||||
// the area that is valid a layer below is to small for support roof.
|
||||
Polylines polylines = ensure_maximum_distance_polyline(
|
||||
generate_support_infill_lines(remaining_overhang, support_params, false, layer_idx, mesh_group_settings.support_tree_branch_distance),
|
||||
@@ -1380,7 +1384,7 @@ void generate_initial_areas(
|
||||
}
|
||||
for (size_t lag_ctr = 1; lag_ctr <= max_overhang_insert_lag && !overhang_lines.empty() && layer_idx - coord_t(lag_ctr) >= 1; lag_ctr++) {
|
||||
// get least restricted avoidance for layer_idx-lag_ctr
|
||||
const Polygons &relevant_forbidden_below = config.support_rests_on_model ?
|
||||
const Polygons &relevant_forbidden_below = config.support_rests_on_model ?
|
||||
volumes.getCollision(config.getRadius(0), layer_idx - lag_ctr, min_xy_dist) :
|
||||
volumes.getAvoidance(config.getRadius(0), layer_idx - lag_ctr, AvoidanceType::Fast, false, min_xy_dist);
|
||||
// it is not required to offset the forbidden area here as the points wont change: If points here are not inside the forbidden area neither will they be later when placing these points, as these are the same points.
|
||||
@@ -1419,7 +1423,7 @@ void generate_initial_areas(
|
||||
remove_small(overhang_regular, mesh_group_settings.minimum_support_area);
|
||||
|
||||
for (ExPolygon &support_part : union_ex(overhang_regular)) {
|
||||
sample_overhang_area(to_polygons(std::move(support_part)),
|
||||
sample_overhang_area(to_polygons(std::move(support_part)),
|
||||
false, layer_idx, num_support_roof_layers, connect_length,
|
||||
mesh_group_settings, rich_interface_placer);
|
||||
throw_on_cancel();
|
||||
@@ -1461,7 +1465,7 @@ static unsigned int move_inside(const Polygons &polygons, Point &from, int dista
|
||||
}
|
||||
int64_t dot_prod = ab.dot(ap);
|
||||
if (dot_prod <= 0) { // x is projected to before ab
|
||||
if (projected_p_beyond_prev_segment) {
|
||||
if (projected_p_beyond_prev_segment) {
|
||||
// case which looks like: > .
|
||||
projected_p_beyond_prev_segment = false;
|
||||
Point& x = p1;
|
||||
@@ -1496,7 +1500,7 @@ static unsigned int move_inside(const Polygons &polygons, Point &from, int dista
|
||||
p0 = p1;
|
||||
p1 = p2;
|
||||
continue;
|
||||
} else {
|
||||
} else {
|
||||
// x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
|
||||
projected_p_beyond_prev_segment = false;
|
||||
Point x = a + (ab.cast<double>() * (double(dot_prod) / double(ab_length2))).cast<coord_t>();
|
||||
@@ -1570,7 +1574,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
Polygons &to_bp_data,
|
||||
Polygons &to_model_data,
|
||||
Polygons &increased,
|
||||
const coord_t overspeed,
|
||||
const coord_t overspeed,
|
||||
const bool mergelayer)
|
||||
{
|
||||
SupportElementState current_elem{ SupportElementState::propagate_down(parent.state) };
|
||||
@@ -1582,18 +1586,18 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
if (settings.move) {
|
||||
increased = relevant_offset;
|
||||
if (overspeed > 0) {
|
||||
const coord_t safe_movement_distance =
|
||||
(current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
|
||||
const coord_t safe_movement_distance =
|
||||
(current_elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
|
||||
(std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0);
|
||||
// The difference to ensure that the result not only conforms to wall_restriction, but collision/avoidance is done later.
|
||||
// The higher last_safe_step_movement_distance comes exactly from the fact that the collision will be subtracted later.
|
||||
increased = safe_offset_inc(increased, overspeed, volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist),
|
||||
increased = safe_offset_inc(increased, overspeed, volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist),
|
||||
safe_movement_distance, safe_movement_distance + radius, 1);
|
||||
}
|
||||
if (settings.no_error && settings.move)
|
||||
// as ClipperLib::jtRound has to be used for offsets this simplify is VERY important for performance.
|
||||
polygons_simplify(increased, scaled<float>(0.025));
|
||||
} else
|
||||
} else
|
||||
// if no movement is done the areas keep parent area as no move == offset(0)
|
||||
increased = parent.influence_area;
|
||||
|
||||
@@ -1602,7 +1606,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
if (! current_elem.to_buildplate && area(to_bp_data) > tiny_area_threshold) {
|
||||
// mostly happening in the tip, but with merges one should check every time, just to be sure.
|
||||
current_elem.to_buildplate = true; // sometimes nodes that can reach the buildplate are marked as cant reach, tainting subtrees. This corrects it.
|
||||
BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " <<
|
||||
BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong to model value on layer " << layer_idx - 1 << " targeting " <<
|
||||
current_elem.target_height << " with radius " << radius;
|
||||
}
|
||||
}
|
||||
@@ -1613,7 +1617,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
if (!current_elem.to_model_gracious) {
|
||||
if (mergelayer && area(to_model_data) >= tiny_area_threshold) {
|
||||
current_elem.to_model_gracious = true;
|
||||
BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " <<
|
||||
BOOST_LOG_TRIVIAL(debug) << "Corrected taint leading to a wrong non gracious value on layer " << layer_idx - 1 << " targeting " <<
|
||||
current_elem.target_height << " with radius " << radius;
|
||||
} else
|
||||
to_model_data = safe_union(diff_clipped(increased, volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance)));
|
||||
@@ -1633,8 +1637,8 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
to_bp_data_2 = diff_clipped(increased, volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, false, settings.use_min_distance));
|
||||
Polygons to_model_data_2;
|
||||
if (config.support_rests_on_model && !current_elem.to_buildplate)
|
||||
to_model_data_2 = diff_clipped(increased,
|
||||
current_elem.to_model_gracious ?
|
||||
to_model_data_2 = diff_clipped(increased,
|
||||
current_elem.to_model_gracious ?
|
||||
volumes.getAvoidance(next_radius, layer_idx - 1, settings.type, true, settings.use_min_distance) :
|
||||
volumes.getCollision(next_radius, layer_idx - 1, settings.use_min_distance));
|
||||
Polygons check_layer_data_2 = current_elem.to_buildplate ? to_bp_data_2 : to_model_data_2;
|
||||
@@ -1649,8 +1653,8 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
while (current_ceil_radius < target_radius && validWithRadius(volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance)))
|
||||
current_ceil_radius = volumes.getRadiusNextCeil(current_ceil_radius + 1, settings.use_min_distance);
|
||||
size_t resulting_eff_dtt = current_elem.effective_radius_height;
|
||||
while (resulting_eff_dtt + 1 < current_elem.distance_to_top &&
|
||||
config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius &&
|
||||
while (resulting_eff_dtt + 1 < current_elem.distance_to_top &&
|
||||
config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= current_ceil_radius &&
|
||||
config.getRadius(resulting_eff_dtt + 1, current_elem.elephant_foot_increases) <= support_element_radius(config, current_elem))
|
||||
++ resulting_eff_dtt;
|
||||
current_elem.effective_radius_height = resulting_eff_dtt;
|
||||
@@ -1658,7 +1662,7 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
radius = support_element_collision_radius(config, current_elem);
|
||||
|
||||
const coord_t foot_radius_increase = std::max(config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer, 0.0);
|
||||
// Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius,
|
||||
// Is nearly all of the time 1, but sometimes an increase of 1 could cause the radius to become bigger than recommendedMinRadius,
|
||||
// which could cause the radius to become bigger than precalculated.
|
||||
double planned_foot_increase = std::min(1.0, double(config.recommendedMinRadius(layer_idx - 1) - support_element_radius(config, current_elem)) / foot_radius_increase);
|
||||
//FIXME
|
||||
@@ -1675,14 +1679,14 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
|
||||
if (current_elem.to_buildplate)
|
||||
to_bp_data = safe_union(diff_clipped(increased, volumes.getAvoidance(radius, layer_idx - 1, settings.type, false, settings.use_min_distance)));
|
||||
if (config.support_rests_on_model && (!current_elem.to_buildplate || mergelayer))
|
||||
to_model_data = safe_union(diff_clipped(increased,
|
||||
current_elem.to_model_gracious ?
|
||||
to_model_data = safe_union(diff_clipped(increased,
|
||||
current_elem.to_model_gracious ?
|
||||
volumes.getAvoidance(radius, layer_idx - 1, settings.type, true, settings.use_min_distance) :
|
||||
volumes.getCollision(radius, layer_idx - 1, settings.use_min_distance)
|
||||
));
|
||||
check_layer_data = current_elem.to_buildplate ? to_bp_data : to_model_data;
|
||||
if (area(check_layer_data) < tiny_area_threshold) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " <<
|
||||
BOOST_LOG_TRIVIAL(error) << "Lost area by doing catch up from " << ceil_radius_before << " to radius " <<
|
||||
volumes.ceilRadius(support_element_collision_radius(config, current_elem), settings.use_min_distance);
|
||||
tree_supports_show_error("Area lost catching up radius. May not cause visible malformation."sv, true);
|
||||
}
|
||||
@@ -1720,7 +1724,7 @@ struct SupportElementMerging {
|
||||
|
||||
const Eigen::AlignedBox<coord_t, 2>& bbox() const { return bbox_data;}
|
||||
const Point centroid() const { return (bbox_data.min() + bbox_data.max()) / 2; }
|
||||
void set_bbox(const BoundingBox& abbox)
|
||||
void set_bbox(const BoundingBox& abbox)
|
||||
{ Point eps { coord_t(SCALED_EPSILON), coord_t(SCALED_EPSILON) }; bbox_data = { abbox.min - eps, abbox.max + eps }; }
|
||||
|
||||
// Called by the AABBTree builder to get an index into the vector of source elements.
|
||||
@@ -1752,7 +1756,7 @@ static void increase_areas_one_layer(
|
||||
// New areas at the layer below layer_idx
|
||||
std::vector<SupportElementMerging> &merging_areas,
|
||||
// Layer above merging_areas.
|
||||
const LayerIndex layer_idx,
|
||||
const LayerIndex layer_idx,
|
||||
// Layer elements above merging_areas.
|
||||
SupportElements &layer_elements,
|
||||
// If false, the merging_areas will not be merged for performance reasons.
|
||||
@@ -1768,7 +1772,7 @@ static void increase_areas_one_layer(
|
||||
assert(merging_area.parents.size() == 1);
|
||||
SupportElement &parent = layer_elements[merging_area.parents.front()];
|
||||
SupportElementState elem = SupportElementState::propagate_down(parent.state);
|
||||
const Polygons &wall_restriction =
|
||||
const Polygons &wall_restriction =
|
||||
// Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall.
|
||||
volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist);
|
||||
|
||||
@@ -1799,10 +1803,10 @@ static void increase_areas_one_layer(
|
||||
* layer z-1:dddddxxxxxxxxxx
|
||||
* For more detailed visualisation see calculateWallRestrictions
|
||||
*/
|
||||
const coord_t safe_movement_distance =
|
||||
(elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
|
||||
const coord_t safe_movement_distance =
|
||||
(elem.use_min_xy_dist ? config.xy_min_distance : config.xy_distance) +
|
||||
(std::min(config.z_distance_top_layers, config.z_distance_bottom_layers) > 0 ? config.min_feature_size : 0);
|
||||
if (ceiled_parent_radius == volumes.ceilRadius(projected_radius_increased, parent.state.use_min_xy_dist) ||
|
||||
if (ceiled_parent_radius == volumes.ceilRadius(projected_radius_increased, parent.state.use_min_xy_dist) ||
|
||||
projected_radius_increased < config.increase_radius_until_radius)
|
||||
// If it is guaranteed possible to increase the radius, the maximum movement speed can be increased, as it is assumed that the maximum movement speed is the one of the slower moving wall
|
||||
extra_speed += projected_radius_delta;
|
||||
@@ -1811,7 +1815,7 @@ static void increase_areas_one_layer(
|
||||
// Ensure that the slow movement distance can not become larger than the fast one.
|
||||
extra_slow_speed += std::min(projected_radius_delta, (config.maximum_move_distance + extra_speed) - (config.maximum_move_distance_slow + extra_slow_speed));
|
||||
|
||||
if (config.layer_start_bp_radius > layer_idx &&
|
||||
if (config.layer_start_bp_radius > layer_idx &&
|
||||
config.recommendedMinRadius(layer_idx - 1) < config.getRadius(elem.effective_radius_height + 1, elem.elephant_foot_increases)) {
|
||||
// can guarantee elephant foot radius increase
|
||||
if (ceiled_parent_radius == volumes.ceilRadius(config.getRadius(parent.state.effective_radius_height + 1, parent.state.elephant_foot_increases + 1), parent.state.use_min_xy_dist))
|
||||
@@ -1846,7 +1850,7 @@ static void increase_areas_one_layer(
|
||||
if (elem.last_area_increase.move && elem.last_area_increase.no_error && elem.can_use_safe_radius && !mergelayer &&
|
||||
!avoidance_speed_mismatch && (elem.distance_to_top >= config.tip_layers || parent_moved_slow)) {
|
||||
// assume that the avoidance type that was best for the parent is best for me. Makes this function about 7% faster.
|
||||
insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
|
||||
insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
|
||||
increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true);
|
||||
insertSetting({ elem.last_area_increase.type, elem.last_area_increase.increase_speed < config.maximum_move_distance ? slow_speed : fast_speed,
|
||||
!increase_radius, elem.last_area_increase.no_error, !use_min_radius, elem.last_area_increase.move }, true);
|
||||
@@ -1866,7 +1870,7 @@ static void increase_areas_one_layer(
|
||||
insertSetting({ AvoidanceType::Fast, fast_speed, !increase_radius, no_error, !use_min_radius, move }, true);
|
||||
} else {
|
||||
insertSetting({ AvoidanceType::Slow, slow_speed, increase_radius, no_error, !use_min_radius, move }, true);
|
||||
// while moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement,
|
||||
// while moving fast to be able to increase the radius (b) may seems preferable (over a) this can cause the a sudden skip in movement,
|
||||
// which looks similar to a layer shift and can reduce stability.
|
||||
// as such idx have chosen to only use the user setting for radius increases as a friendly recommendation.
|
||||
insertSetting({ AvoidanceType::Slow, slow_speed, !increase_radius, no_error, !use_min_radius, move }, true); // a
|
||||
@@ -1903,9 +1907,9 @@ static void increase_areas_one_layer(
|
||||
for (const AreaIncreaseSettings &settings : order) {
|
||||
if (settings.move) {
|
||||
if (offset_slow.empty() && (settings.increase_speed == slow_speed || ! offset_independant_faster)) {
|
||||
// offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. At this point one can see that the Polygons class
|
||||
// offsetting in 2 steps makes our offsetted area rounder preventing (rounding) errors created by to pointy areas. At this point one can see that the Polygons class
|
||||
// was never made for precision in the single digit micron range.
|
||||
offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow,
|
||||
offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow,
|
||||
wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2);
|
||||
#ifdef TREESUPPORT_DEBUG_SVG
|
||||
SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-slow-%d-%ld.svg", layer_idx, int(merging_area_idx)),
|
||||
@@ -1915,7 +1919,7 @@ static void increase_areas_one_layer(
|
||||
}
|
||||
if (offset_fast.empty() && settings.increase_speed != slow_speed) {
|
||||
if (offset_independant_faster)
|
||||
offset_fast = safe_offset_inc(parent.influence_area, extra_speed + config.maximum_move_distance,
|
||||
offset_fast = safe_offset_inc(parent.influence_area, extra_speed + config.maximum_move_distance,
|
||||
wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 1);
|
||||
else {
|
||||
const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed);
|
||||
@@ -1930,12 +1934,12 @@ static void increase_areas_one_layer(
|
||||
}
|
||||
std::optional<SupportElementState> result;
|
||||
inc_wo_collision.clear();
|
||||
if (!settings.no_error) {
|
||||
if (!settings.no_error) {
|
||||
// ERROR CASE
|
||||
// if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if if wrongly would be a line, it still actually has an area that can be increased
|
||||
// if the area becomes for whatever reason something that clipper sees as a line, offset would stop working, so ensure that even if it would be a line wrongly, it still actually has an area that can be increased
|
||||
Polygons lines_offset = offset(to_polylines(parent.influence_area), scaled<float>(0.005), jtMiter, 1.2);
|
||||
Polygons base_error_area = union_(parent.influence_area, lines_offset);
|
||||
result = increase_single_area(volumes, config, settings, layer_idx, parent,
|
||||
result = increase_single_area(volumes, config, settings, layer_idx, parent,
|
||||
base_error_area, to_bp_data, to_model_data, inc_wo_collision, (config.maximum_move_distance + extra_speed) * 1.5, mergelayer);
|
||||
#ifdef TREE_SUPPORT_SHOW_ERRORS
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
@@ -1944,7 +1948,7 @@ static void increase_areas_one_layer(
|
||||
#endif // TREE_SUPPORT_SHOW_ERRORS
|
||||
<< "Influence area could not be increased! Data about the Influence area: "
|
||||
"Radius: " << radius << " at layer: " << layer_idx - 1 << " NextTarget: " << elem.layer_idx << " Distance to top: " << elem.distance_to_top <<
|
||||
" Elephant foot increases " << elem.elephant_foot_increases << " use_min_xy_dist " << elem.use_min_xy_dist << " to buildplate " << elem.to_buildplate <<
|
||||
" Elephant foot increases " << elem.elephant_foot_increases << " use_min_xy_dist " << elem.use_min_xy_dist << " to buildplate " << elem.to_buildplate <<
|
||||
" gracious " << elem.to_model_gracious << " safe " << elem.can_use_safe_radius << " until move " << elem.dont_move_until << " \n "
|
||||
"Parent " << &parent << ": Radius: " << support_element_collision_radius(config, parent.state) << " at layer: " << layer_idx << " NextTarget: " << parent.state.layer_idx <<
|
||||
" Distance to top: " << parent.state.distance_to_top << " Elephant foot increases " << parent.state.elephant_foot_increases << " use_min_xy_dist " << parent.state.use_min_xy_dist <<
|
||||
@@ -1972,7 +1976,7 @@ static void increase_areas_one_layer(
|
||||
elem.use_min_xy_dist = false;
|
||||
if (!settings.no_error)
|
||||
#ifdef TREE_SUPPORT_SHOW_ERRORS
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
#else // TREE_SUPPORT_SHOW_ERRORS
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
#endif // TREE_SUPPORT_SHOW_ERRORS
|
||||
@@ -1999,7 +2003,7 @@ static void increase_areas_one_layer(
|
||||
merging_area.areas.to_model_areas = std::move(to_model_data);
|
||||
}
|
||||
} else {
|
||||
// If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it.
|
||||
// If the bottom most point of a branch is set, later functions will assume that the position is valid, and ignore it.
|
||||
// But as branches connecting with the model that are to small have to be culled, the bottom most point has to be not set.
|
||||
// A point can be set on the top most tip layer (maybe more if it should not move for a few layers).
|
||||
parent.state.result_on_layer_reset();
|
||||
@@ -2039,7 +2043,7 @@ static void increase_areas_one_layer(
|
||||
out.elephant_foot_increases = 0;
|
||||
if (config.bp_radius_increase_per_layer > 0) {
|
||||
coord_t foot_increase_radius = std::abs(std::max(support_element_collision_radius(config, second), support_element_collision_radius(config, first)) - support_element_collision_radius(config, out));
|
||||
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
|
||||
// elephant_foot_increases has to be recalculated, as when a smaller tree with a larger elephant_foot_increases merge with a larger branch
|
||||
// the elephant_foot_increases may have to be lower as otherwise the radius suddenly increases. This results often in a non integer value.
|
||||
out.elephant_foot_increases = foot_increase_radius / (config.bp_radius_increase_per_layer - config.branch_radius_increase_per_layer);
|
||||
}
|
||||
@@ -2062,8 +2066,8 @@ static bool merge_influence_areas_two_elements(
|
||||
{
|
||||
// Don't merge gracious with a non gracious area as bad placement could negatively impact reliability of the whole subtree.
|
||||
const bool merging_gracious_and_non_gracious = dst.state.to_model_gracious != src.state.to_model_gracious;
|
||||
// Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased
|
||||
// by the delta to the larger it is engulfed by it already. But because a different collision
|
||||
// Could cause some issues with the increase of one area, as it is assumed that if the smaller is increased
|
||||
// by the delta to the larger it is engulfed by it already. But because a different collision
|
||||
// may be removed from the in draw_area() generated circles, this assumption could be wrong.
|
||||
const bool merging_min_and_regular_xy = dst.state.use_min_xy_dist != src.state.use_min_xy_dist;
|
||||
|
||||
@@ -2107,10 +2111,10 @@ static bool merge_influence_areas_two_elements(
|
||||
if (increased_to_model_radius > config.max_to_model_radius_increase)
|
||||
return false;
|
||||
}
|
||||
// if a merge could place a stable branch on unstable ground, would be increasing the radius further
|
||||
// than allowed to when merging to model and to_bp trees or would merge to model before it is known
|
||||
// if a merge could place a stable branch on unstable ground, would be increasing the radius further
|
||||
// than allowed to when merging to model and to_bp trees or would merge to model before it is known
|
||||
// they will even been drawn the merge is skipped
|
||||
if (! dst.state.supports_roof && ! src.state.supports_roof &&
|
||||
if (! dst.state.supports_roof && ! src.state.supports_roof &&
|
||||
std::max(src.state.distance_to_top, dst.state.distance_to_top) < config.min_dtt_to_model)
|
||||
return false;
|
||||
}
|
||||
@@ -2121,7 +2125,7 @@ static bool merge_influence_areas_two_elements(
|
||||
return false;
|
||||
|
||||
// the bigger radius is used to verify that the area is still valid after the increase with the delta.
|
||||
// If there were a point where the big influence area could be valid with can_use_safe_radius
|
||||
// If there were a point where the big influence area could be valid with can_use_safe_radius
|
||||
// the element would already be can_use_safe_radius.
|
||||
// the smaller radius, which gets increased by delta may reach into the area where use_min_xy_dist is no longer required.
|
||||
const bool use_min_radius = bigger_rad.state.use_min_xy_dist && smaller_rad.state.use_min_xy_dist;
|
||||
@@ -2145,7 +2149,7 @@ static bool merge_influence_areas_two_elements(
|
||||
merging_to_bp ? bigger_rad.areas.to_bp_areas : bigger_rad.areas.to_model_areas);
|
||||
|
||||
// dont use empty as a line is not empty, but for this use-case it very well may be (and would be one layer down as union does not keep lines)
|
||||
// check if the overlap is large enough (Small ares tend to attract rounding errors in clipper).
|
||||
// check if the overlap is large enough (Small ares tend to attract rounding errors in clipper).
|
||||
if (area(intersect) <= tiny_area_threshold)
|
||||
return false;
|
||||
|
||||
@@ -2160,7 +2164,7 @@ static bool merge_influence_areas_two_elements(
|
||||
Point new_pos = move_inside_if_outside(intersect, dst.state.next_position);
|
||||
|
||||
SupportElementState new_state = merge_support_element_states(dst.state, src.state, new_pos, layer_idx - 1, config);
|
||||
new_state.increased_to_model_radius = increased_to_model_radius == 0 ?
|
||||
new_state.increased_to_model_radius = increased_to_model_radius == 0 ?
|
||||
// increased_to_model_radius was not set yet. Propagate maximum.
|
||||
std::max(dst.state.increased_to_model_radius, src.state.increased_to_model_radius) :
|
||||
increased_to_model_radius;
|
||||
@@ -2248,7 +2252,7 @@ static SupportElementMerging* merge_influence_areas_two_sets(
|
||||
SupportElementMerging * const dst_begin, SupportElementMerging * dst_end,
|
||||
SupportElementMerging * src_begin, SupportElementMerging * const src_end)
|
||||
{
|
||||
// Merging src into dst.
|
||||
// Merging src into dst.
|
||||
// Areas of src should not overlap with areas of another elements of src.
|
||||
// Areas of dst should not overlap with areas of another elements of dst.
|
||||
// The memory from dst_begin to src_end is reserved for the merging operation,
|
||||
@@ -2301,8 +2305,8 @@ static SupportElementMerging* merge_influence_areas_two_sets(
|
||||
* \param layer_idx[in] The current layer.
|
||||
*/
|
||||
static void merge_influence_areas(
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
const LayerIndex layer_idx,
|
||||
std::vector<SupportElementMerging> &influence_areas,
|
||||
std::function<void()> throw_on_cancel)
|
||||
@@ -2489,7 +2493,7 @@ void create_layer_pathing(const TreeModelVolumes &volumes, const TreeSupportSett
|
||||
throw_on_cancel();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 <<
|
||||
BOOST_LOG_TRIVIAL(info) << "Time spent with creating influence areas' subtasks: Increasing areas " << dur_inc.count() / 1000000 <<
|
||||
" ms merging areas: " << (dur_total - dur_inc).count() / 1000000 << " ms";
|
||||
}
|
||||
|
||||
@@ -2518,7 +2522,7 @@ static void set_points_on_areas(const SupportElement &elem, SupportElements *lay
|
||||
// if the value was set somewhere else it it kept. This happens when a branch tries not to move after being unable to create a roof.
|
||||
if (! next_elem.state.result_on_layer_is_set()) {
|
||||
// Move inside has edgecases (see tests) so DONT use Polygons.inside to confirm correct move, Error with distance 0 is <= 1
|
||||
// it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance.
|
||||
// it is not required to check if how far this move moved a point as is can be larger than maximum_movement_distance.
|
||||
// While this seems like a problem it may for example occur after merges.
|
||||
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
|
||||
@@ -2543,9 +2547,9 @@ static void set_to_model_contact_simple(SupportElement &elem)
|
||||
* \param layer_idx[in] The current layer.
|
||||
*/
|
||||
static void set_to_model_contact_to_model_gracious(
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
SupportElement &first_elem,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
@@ -2614,7 +2618,7 @@ static void remove_deleted_elements(std::vector<SupportElements> &move_bounds)
|
||||
assert(i == layer.size() || i + 1 < layer.size());
|
||||
if (i + 1 < int32_t(layer.size())) {
|
||||
element = std::move(layer.back());
|
||||
layer.pop_back();
|
||||
layer.pop_back();
|
||||
// Mark the current element as deleted.
|
||||
map_current[i] = -1;
|
||||
// Mark the moved element as moved to index i.
|
||||
@@ -2643,7 +2647,7 @@ void create_nodes_from_area(
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
// 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.
|
||||
{
|
||||
SupportElements *layer_above = move_bounds.size() > 1 ? &move_bounds[1] : nullptr;
|
||||
@@ -2773,9 +2777,9 @@ struct DrawArea
|
||||
* \param inverse_tree_order[in] A mapping that returns the child of every influence area.
|
||||
*/
|
||||
static void generate_branch_areas(
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
const std::vector<SupportElements> &move_bounds,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
const std::vector<SupportElements> &move_bounds,
|
||||
std::vector<DrawArea> &linear_data,
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
@@ -2933,7 +2937,7 @@ static void smooth_branch_areas(
|
||||
assert(parent.state.layer_idx == layer_idx + 1);
|
||||
if (support_element_radius(config, parent) != support_element_collision_radius(config, parent)) {
|
||||
do_something = true;
|
||||
max_outer_wall_distance = std::max(max_outer_wall_distance,
|
||||
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() - (support_element_radius(config, *draw_area.element) - support_element_radius(config, parent)));
|
||||
}
|
||||
}
|
||||
@@ -3081,7 +3085,7 @@ static void finalize_interface_and_support_areas(
|
||||
SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage,
|
||||
|
||||
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
assert(std::all_of(bottom_contacts.begin(), bottom_contacts.end(), [](auto *p) { return p == nullptr; }));
|
||||
@@ -3174,7 +3178,7 @@ static void finalize_interface_and_support_areas(
|
||||
while (layers_below <= config.support_bottom_layers) {
|
||||
// one sample at 0 layers below, another at config.support_bottom_layers. In-between samples at config.performance_interface_skip_layers distance from each other.
|
||||
const size_t sample_layer = static_cast<size_t>(std::max(0, (static_cast<int>(layer_idx) - static_cast<int>(layers_below)) - static_cast<int>(config.z_distance_bottom_layers)));
|
||||
//FIXME subtract the wipe tower
|
||||
//FIXME subtract the wipe tower
|
||||
append(floor_layer, intersection(layer_outset, overhangs[sample_layer]));
|
||||
if (layers_below < config.support_bottom_layers)
|
||||
layers_below = std::min(layers_below + 1, config.support_bottom_layers);
|
||||
@@ -3226,7 +3230,7 @@ static void finalize_interface_and_support_areas(
|
||||
*/
|
||||
static void draw_areas(
|
||||
PrintObject &print_object,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
const std::vector<Polygons> &overhangs,
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
@@ -3368,7 +3372,7 @@ static void draw_areas(
|
||||
auto dur_drop = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_drop - t_smooth).count();
|
||||
auto dur_finalize = 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_drop).count();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) <<
|
||||
BOOST_LOG_TRIVIAL(info) <<
|
||||
"Time used for drawing subfuctions: generate_branch_areas: " << dur_gen_tips << " ms "
|
||||
"smooth_branch_areas: " << dur_smooth << " ms "
|
||||
"drop_non_gracious_areas: " << dur_drop << " ms "
|
||||
@@ -3380,7 +3384,7 @@ static void draw_areas(
|
||||
// Circles are considered intersecting, if the lowest point on one circle is below the other circle's plane.
|
||||
// Assumption: The two planes are oriented the same way.
|
||||
static bool circles_intersect(
|
||||
const Vec3d &p1, const Vec3d &n1, const double r1,
|
||||
const Vec3d &p1, const Vec3d &n1, const double r1,
|
||||
const Vec3d &p2, const Vec3d &n2, const double r2)
|
||||
{
|
||||
assert(n1.dot(n2) >= 0);
|
||||
@@ -3522,10 +3526,10 @@ static std::pair<int, int> discretize_polygon(const Vec3f& center, const Polygon
|
||||
|
||||
// Returns Z span of the generated mesh.
|
||||
static std::pair<float, float> extrude_branch(
|
||||
const std::vector<const SupportElement*>&path,
|
||||
const std::vector<const SupportElement*>&path,
|
||||
const TreeSupportSettings &config,
|
||||
const SlicingParameters &slicing_params,
|
||||
const std::vector<SupportElements> &move_bounds,
|
||||
const std::vector<SupportElements> &move_bounds,
|
||||
indexed_triangle_set &result)
|
||||
{
|
||||
Vec3d p1, p2, p3;
|
||||
@@ -3678,7 +3682,7 @@ void organic_smooth_branches_avoid_collisions(
|
||||
Vec3f position;
|
||||
// Previous position, for Laplacian smoothing.
|
||||
Vec3f prev_position;
|
||||
//
|
||||
//
|
||||
Vec3f last_collision;
|
||||
double last_collision_depth;
|
||||
// Minimum Z for which the sphere collision will be evaluated.
|
||||
@@ -3780,7 +3784,7 @@ void organic_smooth_branches_avoid_collisions(
|
||||
// Collision detected to be removed.
|
||||
// Nudge the circle center away from the collision.
|
||||
if (collision_sphere.last_collision_depth > EPSILON)
|
||||
// a little bit of hysteresis to detect end of
|
||||
// a little bit of hysteresis to detect end of
|
||||
++ num_moved;
|
||||
// Shift by maximum 2mm.
|
||||
double nudge_dist = std::min(std::max(0., collision_sphere.last_collision_depth + collision_extra_gap), max_nudge_collision_avoidance);
|
||||
@@ -3938,7 +3942,7 @@ static void organic_smooth_branches_avoid_collisions(
|
||||
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
|
||||
indexed_triangle_set draw_branches(
|
||||
PrintObject &print_object,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
std::function<void()> throw_on_cancel)
|
||||
@@ -4058,7 +4062,7 @@ indexed_triangle_set draw_branches(
|
||||
// Organic specific: Slice the cummulative mesh produced by draw_branches().
|
||||
void slice_branches(
|
||||
PrintObject &print_object,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeModelVolumes &volumes,
|
||||
const TreeSupportSettings &config,
|
||||
const std::vector<Polygons> &overhangs,
|
||||
std::vector<SupportElements> &move_bounds,
|
||||
@@ -4068,7 +4072,7 @@ void slice_branches(
|
||||
SupportGeneratorLayersPtr &top_contacts,
|
||||
SupportGeneratorLayersPtr &intermediate_layers,
|
||||
SupportGeneratorLayerStorage &layer_storage,
|
||||
|
||||
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
const SlicingParameters &slicing_params = print_object.slicing_parameters();
|
||||
@@ -4097,7 +4101,7 @@ void slice_branches(
|
||||
if (! slices[layer_idx].empty()) {
|
||||
SupportGeneratorLayer *&l = intermediate_layers[layer_idx];
|
||||
if (l == nullptr)
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::sltBase, slicing_params, layer_idx);
|
||||
l = &layer_allocate(layer_storage, SupporLayerType::sltBase, slicing_params, config, layer_idx);
|
||||
append(l->polygons, to_polygons(std::move(slices[layer_idx])));
|
||||
}
|
||||
|
||||
@@ -4153,7 +4157,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
// Generator for model collision, avoidance and internal guide volumes.
|
||||
TreeModelVolumes volumes{ print_object, build_volume, config.maximum_move_distance, config.maximum_move_distance_slow, processing.second.front(),
|
||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||
m_progress_multiplier, m_progress_offset,
|
||||
m_progress_multiplier, m_progress_offset,
|
||||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||
/* additional_excluded_areas */{} };
|
||||
|
||||
@@ -4170,12 +4174,27 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
for (size_t i = 0; i < print_object.layer_count(); i++) {
|
||||
for (ExPolygon& expoly : print_object.get_layer(i)->loverhangs) {
|
||||
Polygons polys = to_polygons(expoly);
|
||||
if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) {
|
||||
polys = offset(to_polygons(expoly), scale_(0.2));
|
||||
if (tree_support->overhang_types[&expoly] == TreeSupport::SharpTail) { polys = offset(polys, scale_(0.2));
|
||||
}
|
||||
append(overhangs[i + num_raft_layers], polys);
|
||||
}
|
||||
}
|
||||
// add vertical enforcer points
|
||||
std::vector<float> zs = zs_from_layers(print_object.layers());
|
||||
Polygon base_circle = make_circle(scale_(0.5), SUPPORT_TREE_CIRCLE_RESOLUTION);
|
||||
for (auto &pt_and_normal :tree_support->m_vertical_enforcer_points) {
|
||||
auto pt = pt_and_normal.first;
|
||||
auto normal = pt_and_normal.second; // normal seems useless
|
||||
auto iter = std::lower_bound(zs.begin(), zs.end(), pt.z());
|
||||
if (iter != zs.end()) {
|
||||
size_t layer_nr = iter - zs.begin();
|
||||
if (layer_nr > 0 && layer_nr < print_object.layer_count()) {
|
||||
Polygon circle = base_circle;
|
||||
circle.translate(to_2d(pt).cast<coord_t>());
|
||||
overhangs[layer_nr + num_raft_layers].emplace_back(std::move(circle));
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<Polygons> overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
|
||||
#endif
|
||||
@@ -4197,6 +4216,7 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
support_params.support_density = 0;
|
||||
}
|
||||
|
||||
|
||||
SupportGeneratorLayerStorage layer_storage;
|
||||
SupportGeneratorLayersPtr top_contacts;
|
||||
SupportGeneratorLayersPtr bottom_contacts;
|
||||
@@ -4275,7 +4295,9 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
*print.get_object(processing.second.front()), volumes, config, move_bounds,
|
||||
bottom_contacts, top_contacts, interface_placer, intermediate_layers, layer_storage,
|
||||
throw_on_cancel);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//tree_support->move_bounds_to_contact_nodes(move_bounds, print_object, config);
|
||||
|
||||
remove_undefined_layers();
|
||||
|
||||
@@ -4310,11 +4332,18 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
|
||||
SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
||||
SupportGeneratorLayersPtr layers_sorted = generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||
|
||||
// QDS: This is a hack to avoid the support being generated outside the bed area. See #4769.
|
||||
tbb::parallel_for_each(layers_sorted.begin(), layers_sorted.end(), [&](SupportGeneratorLayer *layer) {
|
||||
if (layer) layer->polygons = intersection(layer->polygons, volumes.m_bed_area);
|
||||
});
|
||||
|
||||
// Don't fill in the tree supports, make them hollow with just a single sheath line.
|
||||
print.set_status(69, _L("Generating support"));
|
||||
generate_support_toolpaths(print_object, print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
|
||||
generate_support_toolpaths(print_object.support_layers(), print_object.config(), support_params, print_object.slicing_parameters(),
|
||||
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||
|
||||
|
||||
auto t_end = std::chrono::high_resolution_clock::now();
|
||||
BOOST_LOG_TRIVIAL(info) << "Total time of organic tree support: " << 0.001 * std::chrono::duration_cast<std::chrono::microseconds>(t_end - t_start).count() << " ms";
|
||||
#if 0
|
||||
//#ifdef SLIC3R_DEBUG
|
||||
{
|
||||
@@ -4563,9 +4592,10 @@ void organic_draw_branches(
|
||||
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
||||
bottom_contacts.clear();
|
||||
//FIXME parallelize?
|
||||
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++i)
|
||||
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); //FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
|
||||
|
||||
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++i) {
|
||||
slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist);
|
||||
slices[i] = intersection(slices[i], volumes.m_bed_area);
|
||||
}
|
||||
size_t num_empty = 0;
|
||||
if (slices.front().empty()) {
|
||||
// Some of the initial layers are empty.
|
||||
@@ -4785,7 +4815,9 @@ void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_suppo
|
||||
}
|
||||
|
||||
Points bedpts = tree_support->m_machine_border.contour.points;
|
||||
BuildVolume build_volume{ Pointfs{ unscaled(bedpts[0]), unscaled(bedpts[1]),unscaled(bedpts[2]),unscaled(bedpts[3])}, tree_support->m_print_config->printable_height };
|
||||
Pointfs bedptsf;
|
||||
std::transform(bedpts.begin(), bedpts.end(), std::back_inserter(bedptsf), [](const Point &p) { return unscale(p); });
|
||||
BuildVolume build_volume{ bedptsf, tree_support->m_print_config->printable_height };
|
||||
|
||||
TreeSupport3D::generate_support_areas(*print_object.print(), tree_support, build_volume, { idx }, throw_on_cancel);
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
|
||||
// The number of vertices in each circle.
|
||||
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
|
||||
|
||||
struct AreaIncreaseSettings
|
||||
{
|
||||
@@ -321,7 +319,7 @@ void organic_draw_branches(
|
||||
SupportGeneratorLayersPtr& intermediate_layers,
|
||||
SupportGeneratorLayerStorage& layer_storage,
|
||||
|
||||
std::function<void()> throw_on_cancel);
|
||||
std::function<void()> throw_on_cancel);
|
||||
|
||||
} // namespace TreeSupport3D
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../Utils.hpp"
|
||||
#include "../Slicing.hpp" // SlicingParams
|
||||
#include "TreeModelVolumes.hpp"
|
||||
#include "SupportLayer.hpp"
|
||||
#include "SupportParameters.hpp"
|
||||
namespace Slic3r
|
||||
{
|
||||
// The number of vertices in each circle.
|
||||
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
|
||||
namespace TreeSupport3D
|
||||
{
|
||||
using LayerIndex = int;
|
||||
@@ -76,12 +77,12 @@ struct TreeSupportMeshGroupSettings {
|
||||
this->support_wall_count = std::max(1, config.tree_support_wall_count.value); // at least 1 wall for organic tree support
|
||||
this->support_roof_line_distance = scaled<coord_t>(config.support_interface_spacing.value) + this->support_roof_line_width;
|
||||
double support_tree_angle_slow = 25;// TODO add a setting?
|
||||
double support_tree_branch_diameter_angle = 5; // TODO add a setting?
|
||||
double tree_support_tip_diameter = 0.8;
|
||||
this->support_tree_branch_distance = scaled<coord_t>(config.tree_support_branch_distance.value);
|
||||
this->support_tree_angle = std::clamp<double>(config.tree_support_branch_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||
this->support_tree_angle_slow = std::clamp<double>(support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
|
||||
this->support_tree_branch_diameter = scaled<coord_t>(config.tree_support_branch_diameter.value);
|
||||
this->support_tree_branch_diameter_angle = std::clamp<double>(support_tree_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||
this->support_tree_branch_diameter_angle = std::clamp<double>(config.tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
||||
this->support_tree_top_rate = 30; // percent
|
||||
// this->support_tree_tip_diameter = this->support_line_width;
|
||||
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(tree_support_tip_diameter), 0, this->support_tree_branch_diameter);
|
||||
@@ -728,5 +729,15 @@ private:
|
||||
std::mutex m_mutex_layer_storage;
|
||||
};
|
||||
|
||||
enum class LineStatus
|
||||
{
|
||||
INVALID,
|
||||
TO_MODEL,
|
||||
TO_MODEL_GRACIOUS,
|
||||
TO_MODEL_GRACIOUS_SAFE,
|
||||
TO_BP,
|
||||
TO_BP_SAFE
|
||||
};
|
||||
|
||||
} // namespace TreeSupport3D
|
||||
} // namespace slic3r
|
||||
Reference in New Issue
Block a user