mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-19 19:33:47 +00:00
Raft, support interface fix and overhaul (#10947)
* Support interface fix * Tree support true interlace * remove pure format changes --------- Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
@@ -1330,72 +1330,82 @@ SupportGeneratorLayersPtr generate_support_layers(
|
|||||||
// Install support layers into the object.
|
// Install support layers into the object.
|
||||||
// A support layer installed on a PrintObject has a unique print_z.
|
// A support layer installed on a PrintObject has a unique print_z.
|
||||||
SupportGeneratorLayersPtr layers_sorted;
|
SupportGeneratorLayersPtr layers_sorted;
|
||||||
layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size());
|
layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size()
|
||||||
|
+ intermediate_layers.size() + interface_layers.size() + base_interface_layers.size());
|
||||||
append(layers_sorted, raft_layers);
|
append(layers_sorted, raft_layers);
|
||||||
append(layers_sorted, bottom_contacts);
|
append(layers_sorted, bottom_contacts);
|
||||||
append(layers_sorted, top_contacts);
|
append(layers_sorted, top_contacts);
|
||||||
append(layers_sorted, intermediate_layers);
|
append(layers_sorted, intermediate_layers);
|
||||||
append(layers_sorted, interface_layers);
|
append(layers_sorted, interface_layers);
|
||||||
append(layers_sorted, base_interface_layers);
|
append(layers_sorted, base_interface_layers);
|
||||||
// remove dupliated layers
|
|
||||||
|
// remove duplicated layers
|
||||||
std::sort(layers_sorted.begin(), layers_sorted.end());
|
std::sort(layers_sorted.begin(), layers_sorted.end());
|
||||||
layers_sorted.erase(std::unique(layers_sorted.begin(), layers_sorted.end()), layers_sorted.end());
|
layers_sorted.erase(std::unique(layers_sorted.begin(), layers_sorted.end()), layers_sorted.end());
|
||||||
|
|
||||||
// Sort the layers lexicographically by a raising print_z and a decreasing height.
|
// Sort the layers lexicographically by a raising print_z and a decreasing height.
|
||||||
std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
|
std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
|
||||||
|
|
||||||
int layer_id = 0;
|
int layer_id = 0;
|
||||||
int layer_id_interface = 0;
|
int interface_id = 0;
|
||||||
assert(object.support_layers().empty());
|
assert(object.support_layers().empty());
|
||||||
|
|
||||||
for (size_t i = 0; i < layers_sorted.size();) {
|
for (size_t i = 0; i < layers_sorted.size();) {
|
||||||
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
|
// Group layers with nearly the same print_z (floating point fuzz).
|
||||||
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
|
|
||||||
size_t j = i + 1;
|
size_t j = i + 1;
|
||||||
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
|
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
|
||||||
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ;
|
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ;
|
||||||
|
|
||||||
// Assign an average print_z to the set of layers with nearly equal print_z.
|
// Assign an average print_z to the set of layers with nearly equal print_z.
|
||||||
coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z);
|
coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z);
|
||||||
coordf_t height_min = layers_sorted[i]->height;
|
coordf_t height_min = layers_sorted[i]->height;
|
||||||
bool empty = true;
|
|
||||||
// For snug supports, layers where the direction of the support interface shall change are accounted for.
|
bool empty_layer = true;
|
||||||
size_t num_interfaces = 0;
|
bool has_interface_layer = false;
|
||||||
size_t num_top_contacts = 0;
|
|
||||||
double top_contact_bottom_z = 0;
|
|
||||||
for (size_t u = i; u < j; ++u) {
|
for (size_t u = i; u < j; ++u) {
|
||||||
SupportGeneratorLayer &layer = *layers_sorted[u];
|
SupportGeneratorLayer &layer = *layers_sorted[u];
|
||||||
if (! layer.polygons.empty()) {
|
|
||||||
empty = false;
|
if (!layer.polygons.empty()) {
|
||||||
num_interfaces += one_of(layer.layer_type, support_types_interface);
|
empty_layer = false;
|
||||||
if (layer.layer_type == SupporLayerType::TopContact) {
|
|
||||||
++ num_top_contacts;
|
if (one_of(layer.layer_type, support_types_interface))
|
||||||
assert(num_top_contacts <= 1);
|
has_interface_layer = true;
|
||||||
// All top contact layers sharing this print_z shall also share bottom_z.
|
|
||||||
//assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON);
|
|
||||||
top_contact_bottom_z = layer.bottom_z;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layer.print_z = zavg;
|
layer.print_z = zavg;
|
||||||
height_min = std::min(height_min, layer.height);
|
height_min = std::min(height_min, layer.height);
|
||||||
}
|
}
|
||||||
if (! empty) {
|
|
||||||
// Here the upper_layer and lower_layer pointers are left to null at the support layers,
|
if (!empty_layer) {
|
||||||
// as they are never used. These pointers are candidates for removal.
|
// Default: keep the global interface_id counter.
|
||||||
bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces;
|
int this_iface_id = interface_id;
|
||||||
size_t this_layer_id_interface = layer_id_interface;
|
|
||||||
if (this_layer_contacts_only) {
|
// Special case: TreeOrganic raft-interface layers.
|
||||||
// Find a supporting layer for its interface ID.
|
// Interface IDs may be unstable here, leading to inconsistent angles.
|
||||||
for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it)
|
// Use deterministic IDs: alternate 0/1 by distance from the base raft.
|
||||||
if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) {
|
bool is_tree_organic = (object.config().support_style == SupportMaterialStyle::smsTreeOrganic ||
|
||||||
// other_layer supports this top contact layer. Assign a different support interface direction to this layer
|
(object.config().support_style == SupportMaterialStyle::smsDefault && is_tree(object.config().support_type)));
|
||||||
// from the layer that supports it.
|
|
||||||
this_layer_id_interface = other_layer.interface_id() + 1;
|
if (is_tree_organic &&
|
||||||
}
|
layer_id >= object.slicing_parameters().base_raft_layers &&
|
||||||
|
layer_id < object.slicing_parameters().raft_layers())
|
||||||
|
{
|
||||||
|
// Alternate IDs 0,1,0,1... based on distance from base raft.
|
||||||
|
this_iface_id = (layer_id - object.slicing_parameters().base_raft_layers) & 1;
|
||||||
}
|
}
|
||||||
object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg);
|
// END special case
|
||||||
if (num_interfaces && ! this_layer_contacts_only)
|
|
||||||
++ layer_id_interface;
|
object.add_support_layer(layer_id++, this_iface_id, height_min, zavg);
|
||||||
|
|
||||||
|
// Only increment the global counter if this set actually contains interface/contact layers.
|
||||||
|
if (has_interface_layer)
|
||||||
|
interface_id++;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
return layers_sorted;
|
return layers_sorted;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1571,12 +1581,12 @@ void generate_support_toolpaths(
|
|||||||
if (filler_base_interface)
|
if (filler_base_interface)
|
||||||
filler_base_interface->set_bounding_box(bbox_object);
|
filler_base_interface->set_bounding_box(bbox_object);
|
||||||
filler_support->set_bounding_box(bbox_object);
|
filler_support->set_bounding_box(bbox_object);
|
||||||
|
|
||||||
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
|
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
|
||||||
{
|
{
|
||||||
SupportLayer &support_layer = *support_layers[support_layer_id];
|
SupportLayer &support_layer = *support_layers[support_layer_id];
|
||||||
LayerCache &layer_cache = layer_caches[support_layer_id];
|
LayerCache &layer_cache = layer_caches[support_layer_id];
|
||||||
const float support_interface_angle = (support_params.support_style == smsGrid || config.support_interface_pattern == smipRectilinear) ?
|
const float support_interface_angle = support_params.support_interface_angle(support_layer.interface_id());
|
||||||
support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id());
|
|
||||||
|
|
||||||
// Find polygons with the same print_z.
|
// Find polygons with the same print_z.
|
||||||
SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer;
|
SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer;
|
||||||
@@ -1669,10 +1679,9 @@ void generate_support_toolpaths(
|
|||||||
filler->angle = interface_as_base ?
|
filler->angle = interface_as_base ?
|
||||||
// If zero interface layers are configured, use the same angle as for the base layers.
|
// If zero interface layers are configured, use the same angle as for the base layers.
|
||||||
angles[support_layer_id % angles.size()] :
|
angles[support_layer_id % angles.size()] :
|
||||||
// Use interface angle for the interface layers.
|
|
||||||
raft_contact ?
|
raft_contact ?
|
||||||
support_params.raft_interface_angle(support_layer.interface_id()) :
|
support_params.raft_interface_angle(support_layer.interface_id()) :
|
||||||
support_interface_angle;
|
support_interface_angle; // Use interface angle for the interface layers.
|
||||||
double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density;
|
double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density;
|
||||||
filler->spacing = raft_contact ? support_params.raft_interface_flow.spacing() :
|
filler->spacing = raft_contact ? support_params.raft_interface_flow.spacing() :
|
||||||
interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing();
|
interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing();
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ struct SupportParameters {
|
|||||||
|
|
||||||
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
|
SupportMaterialPattern support_pattern = object_config.support_base_pattern;
|
||||||
this->with_sheath = object_config.tree_support_wall_count > 0;
|
this->with_sheath = object_config.tree_support_wall_count > 0;
|
||||||
|
// Cache Rectilinear Interlaced flag once
|
||||||
|
this->interlaced_interface = (object_config.support_interface_pattern == smipRectilinearInterlaced);
|
||||||
this->base_fill_pattern =
|
this->base_fill_pattern =
|
||||||
support_pattern == smpHoneycomb ? ipHoneycomb :
|
support_pattern == smpHoneycomb ? ipHoneycomb :
|
||||||
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
|
this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase;
|
||||||
@@ -127,37 +129,10 @@ struct SupportParameters {
|
|||||||
ipConcentric :
|
ipConcentric :
|
||||||
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
(this->interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||||
|
|
||||||
this->raft_angle_1st_layer = 0.f;
|
this->raft_angle_1st_layer = float(M_PI_2);
|
||||||
this->raft_angle_base = 0.f;
|
this->raft_angle_base = 0.0f;
|
||||||
this->raft_angle_interface = 0.f;
|
this->raft_angle_interface = slicing_params.base_raft_layers == 1 ? 0.0f : float(M_PI_2); // make it perpendicular to the layer beneath it
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1);
|
const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1);
|
||||||
const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter);
|
const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter);
|
||||||
support_extrusion_width = object_config.support_line_width.get_abs_value(nozzle_diameter);
|
support_extrusion_width = object_config.support_line_width.get_abs_value(nozzle_diameter);
|
||||||
@@ -254,6 +229,8 @@ struct SupportParameters {
|
|||||||
InfillPattern contact_fill_pattern;
|
InfillPattern contact_fill_pattern;
|
||||||
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
|
// Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness?
|
||||||
bool with_sheath;
|
bool with_sheath;
|
||||||
|
// True if support interface pattern is Rectilinear Interlaced
|
||||||
|
bool interlaced_interface = false;
|
||||||
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
|
// Branches of organic supports with area larger than this threshold will be extruded with double lines.
|
||||||
double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(5.0)) * M_PI;;
|
double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled<double>(5.0)) * M_PI;;
|
||||||
|
|
||||||
@@ -262,8 +239,30 @@ struct SupportParameters {
|
|||||||
float raft_angle_interface;
|
float raft_angle_interface;
|
||||||
|
|
||||||
// Produce a raft interface angle for a given SupportLayer::interface_id()
|
// Produce a raft interface angle for a given SupportLayer::interface_id()
|
||||||
float raft_interface_angle(size_t interface_id) const
|
float raft_interface_angle(size_t interface_id) const
|
||||||
{ return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); }
|
{
|
||||||
|
// Raft interfaces → always alternate 0°/90°
|
||||||
|
return this->raft_angle_interface +
|
||||||
|
((interface_id & 1) ? float(M_PI_2) : 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
float support_interface_angle(size_t interface_id) const
|
||||||
|
{
|
||||||
|
if (this->support_style == smsSnug) {
|
||||||
|
// Snug → ±45°
|
||||||
|
return this->interface_angle +
|
||||||
|
((interface_id & 1) ? -float(M_PI_4) : +float(M_PI_4));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->interlaced_interface) {
|
||||||
|
// Interlaced → 0°/90°
|
||||||
|
return this->interface_angle +
|
||||||
|
((interface_id & 1) ? float(M_PI_2) : 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default → fixed
|
||||||
|
return this->interface_angle;
|
||||||
|
}
|
||||||
|
|
||||||
bool independent_layer_height = false;
|
bool independent_layer_height = false;
|
||||||
const double thresh_big_overhang = Slic3r::sqr(scale_(10));
|
const double thresh_big_overhang = Slic3r::sqr(scale_(10));
|
||||||
|
|||||||
@@ -618,7 +618,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
|
|||||||
|
|
||||||
if(support_pattern == smpLightning)
|
if(support_pattern == smpLightning)
|
||||||
m_support_params.base_fill_pattern = ipLightning;
|
m_support_params.base_fill_pattern = ipLightning;
|
||||||
diameter_angle_scale_factor = std::clamp<double>(m_object_config->tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
|
diameter_angle_scale_factor = std::clamp<double>(m_object_config->tree_support_branch_diameter_angle * M_PI / 180., 0., M_PI_2 - EPSILON);
|
||||||
is_slim = is_tree_slim(support_type, m_support_params.support_style);
|
is_slim = is_tree_slim(support_type, m_support_params.support_style);
|
||||||
is_strong = is_tree(support_type) && m_support_params.support_style == smsTreeStrong;
|
is_strong = is_tree(support_type) && m_support_params.support_style == smsTreeStrong;
|
||||||
base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2);
|
base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2);
|
||||||
@@ -1369,7 +1369,7 @@ void TreeSupport::generate_toolpaths()
|
|||||||
|
|
||||||
Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||||
Fill* filler_raft = Fill::new_from_type(ipRectilinear);
|
Fill* filler_raft = Fill::new_from_type(ipRectilinear);
|
||||||
filler_raft->angle = layer_nr == 0 ? PI/2 : 0;
|
filler_raft->angle = layer_nr == 0 ? M_PI_2 : 0;
|
||||||
filler_raft->spacing = support_flow.spacing();
|
filler_raft->spacing = support_flow.spacing();
|
||||||
|
|
||||||
FillParams fill_params;
|
FillParams fill_params;
|
||||||
@@ -1404,14 +1404,24 @@ void TreeSupport::generate_toolpaths()
|
|||||||
|
|
||||||
// raft interfaces
|
// raft interfaces
|
||||||
for (layer_nr = m_slicing_params.base_raft_layers;
|
for (layer_nr = m_slicing_params.base_raft_layers;
|
||||||
layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers;
|
layer_nr < m_raft_layers;
|
||||||
layer_nr++)
|
layer_nr++)
|
||||||
{
|
{
|
||||||
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
||||||
|
|
||||||
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||||
Fill* filler_interface = Fill::new_from_type(ipRectilinear);
|
Fill* filler_interface = Fill::new_from_type(ipRectilinear);
|
||||||
filler_interface->angle = PI / 2; // interface should be perpendicular to base
|
|
||||||
|
const size_t iface_idx = layer_nr - m_slicing_params.base_raft_layers;
|
||||||
|
const bool single_raft_base_layer = (m_slicing_params.base_raft_layers == 1);
|
||||||
|
|
||||||
|
// Make interface lines alternate 0°/90° between layers, to improve bonding.
|
||||||
|
// If only 1 base layer → start at 0°; otherwise (0 or ≥2 base layers) → start at 90°.
|
||||||
|
filler_interface->angle =
|
||||||
|
(iface_idx & 1)
|
||||||
|
? (single_raft_base_layer ? float(M_PI_2) : 0.0f)
|
||||||
|
: (single_raft_base_layer ? 0.0f : float(M_PI_2));
|
||||||
|
|
||||||
filler_interface->spacing = support_flow.spacing();
|
filler_interface->spacing = support_flow.spacing();
|
||||||
|
|
||||||
FillParams fill_params;
|
FillParams fill_params;
|
||||||
@@ -1431,7 +1441,7 @@ void TreeSupport::generate_toolpaths()
|
|||||||
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
||||||
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||||
Fill* filler_raft = Fill::new_from_type(ipRectilinear);
|
Fill* filler_raft = Fill::new_from_type(ipRectilinear);
|
||||||
filler_raft->angle = PI / 2;
|
filler_raft->angle = M_PI_2;
|
||||||
filler_raft->spacing = support_flow.spacing();
|
filler_raft->spacing = support_flow.spacing();
|
||||||
for (auto& poly : first_non_raft_base)
|
for (auto& poly : first_non_raft_base)
|
||||||
make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::min(size_t(1), wall_count), support_flow, erSupportMaterial, filler_raft, interface_density, false);
|
make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::min(size_t(1), wall_count), support_flow, erSupportMaterial, filler_raft, interface_density, false);
|
||||||
@@ -1492,6 +1502,10 @@ void TreeSupport::generate_toolpaths()
|
|||||||
fill_params.density = interface_density;
|
fill_params.density = interface_density;
|
||||||
// Note: spacing means the separation between two lines as if they are tightly extruded
|
// Note: spacing means the separation between two lines as if they are tightly extruded
|
||||||
filler_Roof1stLayer->spacing = interface_flow.spacing();
|
filler_Roof1stLayer->spacing = interface_flow.spacing();
|
||||||
|
|
||||||
|
if (m_object_config->support_interface_pattern == smipRectilinearInterlaced)
|
||||||
|
filler_Roof1stLayer->layer_id = area_group.interface_id;
|
||||||
|
|
||||||
// generate a perimeter first to support interface better
|
// generate a perimeter first to support interface better
|
||||||
ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection();
|
ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection();
|
||||||
make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_flow, erSupportMaterial,
|
make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_flow, erSupportMaterial,
|
||||||
@@ -1505,6 +1519,10 @@ void TreeSupport::generate_toolpaths()
|
|||||||
// floor_areas
|
// floor_areas
|
||||||
fill_params.density = bottom_interface_density;
|
fill_params.density = bottom_interface_density;
|
||||||
filler_interface->spacing = interface_flow.spacing();
|
filler_interface->spacing = interface_flow.spacing();
|
||||||
|
|
||||||
|
if (m_object_config->support_interface_pattern == smipRectilinearInterlaced)
|
||||||
|
filler_interface->layer_id = area_group.interface_id;
|
||||||
|
|
||||||
fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys,
|
fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys,
|
||||||
filler_interface.get(), fill_params, erSupportMaterialInterface, interface_flow);
|
filler_interface.get(), fill_params, erSupportMaterialInterface, interface_flow);
|
||||||
} else if (area_group.type == SupportLayer::RoofType) {
|
} else if (area_group.type == SupportLayer::RoofType) {
|
||||||
@@ -1701,7 +1719,7 @@ void TreeSupport::generate()
|
|||||||
draw_circles();
|
draw_circles();
|
||||||
profiler.stage_finish(STAGE_DRAW_CIRCLES);
|
profiler.stage_finish(STAGE_DRAW_CIRCLES);
|
||||||
|
|
||||||
|
normalize_interface_ids();
|
||||||
|
|
||||||
profiler.stage_start(STAGE_GENERATE_TOOLPATHS);
|
profiler.stage_start(STAGE_GENERATE_TOOLPATHS);
|
||||||
m_object->print()->set_status(70, _u8L("Generating support"));
|
m_object->print()->set_status(70, _u8L("Generating support"));
|
||||||
@@ -1930,7 +1948,7 @@ void TreeSupport::draw_circles()
|
|||||||
{
|
{
|
||||||
double angle;
|
double angle;
|
||||||
if (SQUARE_SUPPORT)
|
if (SQUARE_SUPPORT)
|
||||||
angle = (double) i / CIRCLE_RESOLUTION * TAU + PI / 4.0 + nodes_angle;
|
angle = (double) i / CIRCLE_RESOLUTION * TAU + M_PI_4 + nodes_angle;
|
||||||
else
|
else
|
||||||
angle = (double) i / CIRCLE_RESOLUTION * TAU;
|
angle = (double) i / CIRCLE_RESOLUTION * TAU;
|
||||||
branch_circle.append(Point(cos(angle) * branch_radius_scaled, sin(angle) * branch_radius_scaled));
|
branch_circle.append(Point(cos(angle) * branch_radius_scaled, sin(angle) * branch_radius_scaled));
|
||||||
@@ -1994,7 +2012,6 @@ void TreeSupport::draw_circles()
|
|||||||
coordf_t max_layers_above_base = 0;
|
coordf_t max_layers_above_base = 0;
|
||||||
coordf_t max_layers_above_roof = 0;
|
coordf_t max_layers_above_roof = 0;
|
||||||
coordf_t max_layers_above_roof1 = 0;
|
coordf_t max_layers_above_roof1 = 0;
|
||||||
int interface_id = 0;
|
|
||||||
bool has_circle_node = false;
|
bool has_circle_node = false;
|
||||||
bool need_extra_wall = false;
|
bool need_extra_wall = false;
|
||||||
ExPolygons collision_sharp_tails;
|
ExPolygons collision_sharp_tails;
|
||||||
@@ -2089,18 +2106,17 @@ void TreeSupport::draw_circles()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj_layer_nr>0 && node.distance_to_top < 0)
|
if (obj_layer_nr > 0 && node.distance_to_top < 0)
|
||||||
append(roof_gap_areas, area);
|
append(roof_gap_areas, area);
|
||||||
else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false)
|
else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false)
|
||||||
{
|
{
|
||||||
append(roof_1st_layer, area);
|
append(roof_1st_layer, area);
|
||||||
max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top);
|
max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top);
|
||||||
}
|
}
|
||||||
else if (obj_layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail == false)
|
else if (obj_layer_nr > 0 && node.support_roof_layers_below > 1 && node.is_sharp_tail == false)
|
||||||
{
|
{
|
||||||
append(roof_areas, area);
|
append(roof_areas, area);
|
||||||
max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top);
|
max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top);
|
||||||
interface_id = node.obj_layer_nr % top_interface_layers;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2171,7 +2187,6 @@ void TreeSupport::draw_circles()
|
|||||||
for (auto& expoly : ts_layer->roof_areas) {
|
for (auto& expoly : ts_layer->roof_areas) {
|
||||||
//if (area(expoly) < SQ(scale_(1))) continue;
|
//if (area(expoly) < SQ(scale_(1))) continue;
|
||||||
area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof);
|
area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof);
|
||||||
area_groups.back().interface_id = interface_id;
|
|
||||||
}
|
}
|
||||||
for (auto &expoly : ts_layer->floor_areas) {
|
for (auto &expoly : ts_layer->floor_areas) {
|
||||||
//if (area(expoly) < SQ(scale_(1))) continue;
|
//if (area(expoly) < SQ(scale_(1))) continue;
|
||||||
@@ -2412,6 +2427,27 @@ void TreeSupport::draw_circles()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Reassigns sequential interface IDs to enforce A/B alternation
|
||||||
|
* for RectilinearInterlaced support patterns.
|
||||||
|
*/
|
||||||
|
void TreeSupport::normalize_interface_ids()
|
||||||
|
{
|
||||||
|
if (m_object_config->support_interface_pattern != smipRectilinearInterlaced)
|
||||||
|
return;
|
||||||
|
|
||||||
|
size_t iface_parity = 0;
|
||||||
|
for (int lid = m_raft_layers; lid < m_object->support_layer_count(); ++lid) {
|
||||||
|
SupportLayer* ts_layer = m_object->get_support_layer(lid);
|
||||||
|
if (!ts_layer) continue;
|
||||||
|
|
||||||
|
for (auto &ag : ts_layer->area_groups)
|
||||||
|
ag.interface_id = iface_parity;
|
||||||
|
|
||||||
|
iface_parity ^= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double SupportNode::diameter_angle_scale_factor;
|
double SupportNode::diameter_angle_scale_factor;
|
||||||
|
|
||||||
void TreeSupport::drop_nodes()
|
void TreeSupport::drop_nodes()
|
||||||
@@ -2423,7 +2459,7 @@ void TreeSupport::drop_nodes()
|
|||||||
const double angle = config.tree_support_branch_angle.value * M_PI / 180.;
|
const double angle = config.tree_support_branch_angle.value * M_PI / 180.;
|
||||||
const int wall_count = std::max(1, config.tree_support_wall_count.value);
|
const int wall_count = std::max(1, config.tree_support_wall_count.value);
|
||||||
double tan_angle = tan(angle); // when nodes are thick, they can move further. this is the max angle
|
double tan_angle = tan(angle); // when nodes are thick, they can move further. this is the max angle
|
||||||
const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits<coordf_t>::max();
|
const coordf_t max_move_distance = (angle < M_PI_2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits<coordf_t>::max();
|
||||||
const double max_move_distance2 = max_move_distance * max_move_distance;
|
const double max_move_distance2 = max_move_distance * max_move_distance;
|
||||||
const size_t tip_layers = base_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle.
|
const size_t tip_layers = base_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle.
|
||||||
const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution;
|
const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution;
|
||||||
|
|||||||
@@ -459,6 +459,21 @@ private:
|
|||||||
* \param contact_nodes The nodes to draw as support.
|
* \param contact_nodes The nodes to draw as support.
|
||||||
*/
|
*/
|
||||||
void draw_circles();
|
void draw_circles();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Normalize interface IDs for interlaced supports.
|
||||||
|
*
|
||||||
|
* Reassigns sequential IDs to support layers when using
|
||||||
|
* RectilinearInterlaced, alternating (0,1,0,1,…) so consecutive
|
||||||
|
* interface layers never share orientation. Applies to all support
|
||||||
|
* area groups, including transition layers (e.g. TreeStrong
|
||||||
|
* body-to-interface), as well as top and bottom interfaces, so
|
||||||
|
* FillRectilinearInterlaced computes correct extrusion angles.
|
||||||
|
*
|
||||||
|
* \note Call after support layers are built and before toolpaths,
|
||||||
|
* so fillers use the normalized IDs.
|
||||||
|
*/
|
||||||
|
void normalize_interface_ids();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Drops down the nodes of the tree support towards the build plate.
|
* \brief Drops down the nodes of the tree support towards the build plate.
|
||||||
|
|||||||
Reference in New Issue
Block a user