mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-12 01:07:57 -06:00
Clip the organic supports outside bed (#8195)
Clip the organic supports outside bed. (SoftFever/OrcaSlicer#7922) (cherry picked from commit bambulab/BambuStudio@2c6a6ae5f4) Co-authored-by: Arthur <arthur.tang@bambulab.com> Co-authored-by: SoftFever <softfeverever@gmail.com>
This commit is contained in:
parent
af893267e6
commit
42577feeba
6 changed files with 62 additions and 40 deletions
|
@ -66,6 +66,7 @@ TreeModelVolumes::TreeModelVolumes(
|
||||||
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
#endif // SLIC3R_TREESUPPORTS_PROGRESS
|
||||||
m_machine_border{ calculateMachineBorderCollision(build_volume.polygon()) }
|
m_machine_border{ calculateMachineBorderCollision(build_volume.polygon()) }
|
||||||
{
|
{
|
||||||
|
m_bed_area = build_volume.polygon();
|
||||||
#if 0
|
#if 0
|
||||||
std::unordered_map<size_t, size_t> mesh_to_layeroutline_idx;
|
std::unordered_map<size_t, size_t> mesh_to_layeroutline_idx;
|
||||||
for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); ++ mesh_idx) {
|
for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); ++ mesh_idx) {
|
||||||
|
@ -180,6 +181,7 @@ void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord
|
||||||
m_ignorable_radii.emplace_back(radius_eval);
|
m_ignorable_radii.emplace_back(radius_eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
|
|
||||||
// it may seem that the required avoidance can be of a smaller radius when going to model (no initial layer diameter for to model branches)
|
// it may seem that the required avoidance can be of a smaller radius when going to model (no initial layer diameter for to model branches)
|
||||||
|
@ -203,6 +205,7 @@ void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord
|
||||||
update_radius_until_layer(ceilRadius(config.recommendedMinRadius(current_layer) + m_current_min_xy_dist_delta));
|
update_radius_until_layer(ceilRadius(config.recommendedMinRadius(current_layer) + m_current_min_xy_dist_delta));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
|
|
||||||
// Copy to deque to use in parallel for later.
|
// Copy to deque to use in parallel for later.
|
||||||
|
@ -365,7 +368,6 @@ const Polygons& TreeModelVolumes::getPlaceableAreas(const coord_t orig_radius, L
|
||||||
if (orig_radius == 0)
|
if (orig_radius == 0)
|
||||||
// Placable areas for radius 0 are calculated in the general collision code.
|
// Placable areas for radius 0 are calculated in the general collision code.
|
||||||
return this->getCollision(0, layer_idx, true);
|
return this->getCollision(0, layer_idx, true);
|
||||||
else
|
|
||||||
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel);
|
const_cast<TreeModelVolumes*>(this)->calculatePlaceables(radius, layer_idx, throw_on_cancel);
|
||||||
return getPlaceableAreas(orig_radius, layer_idx, throw_on_cancel);
|
return getPlaceableAreas(orig_radius, layer_idx, throw_on_cancel);
|
||||||
}
|
}
|
||||||
|
@ -461,6 +463,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||||
collision_areas_offsetted[layer_idx] = offset_value == 0 ?
|
collision_areas_offsetted[layer_idx] = offset_value == 0 ?
|
||||||
union_(collision_areas) :
|
union_(collision_areas) :
|
||||||
offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
|
offset(union_ex(collision_areas), offset_value, ClipperLib::jtMiter, 1.2);
|
||||||
|
if(throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -524,6 +527,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||||
dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple);
|
dst = polygons_simplify(collisions, min_resolution, polygons_strictly_simple);
|
||||||
} else
|
} else
|
||||||
append(dst, std::move(collisions));
|
append(dst, std::move(collisions));
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -551,6 +555,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||||
dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple);
|
dst = polygons_simplify(placable, min_resolution, polygons_strictly_simple);
|
||||||
} else
|
} else
|
||||||
append(dst, placable);
|
append(dst, placable);
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -567,6 +572,7 @@ void TreeModelVolumes::calculateCollision(const coord_t radius, const LayerIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
m_collision_cache.insert(std::move(data), radius);
|
m_collision_cache.insert(std::move(data), radius);
|
||||||
if (calculate_placable)
|
if (calculate_placable)
|
||||||
|
@ -597,6 +603,7 @@ void TreeModelVolumes::calculateCollisionHolefree(const std::vector<RadiusLayerP
|
||||||
offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
|
offset(union_ex(this->getCollision(m_increase_until_radius, layer_idx, false)),
|
||||||
5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution),
|
5 - increase_radius_ceil, ClipperLib::jtRound, m_min_resolution),
|
||||||
m_min_resolution, polygons_strictly_simple));
|
m_min_resolution, polygons_strictly_simple));
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -640,6 +647,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
|
||||||
avoidance_tasks.emplace_back(task);
|
avoidance_tasks.emplace_back(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
|
|
||||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, avoidance_tasks.size(), 1),
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, avoidance_tasks.size(), 1),
|
||||||
|
@ -685,6 +693,7 @@ void TreeModelVolumes::calculateAvoidance(const std::vector<RadiusLayerPair> &ke
|
||||||
latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel));
|
latest_avoidance = diff(latest_avoidance, getPlaceableAreas(task.radius, layer_idx, throw_on_cancel));
|
||||||
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple);
|
latest_avoidance = polygons_simplify(latest_avoidance, m_min_resolution, polygons_strictly_simple);
|
||||||
data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
|
data.emplace_back(RadiusLayerPair{task.radius, layer_idx}, latest_avoidance);
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
|
||||||
|
@ -736,6 +745,7 @@ void TreeModelVolumes::calculatePlaceables(const coord_t radius, const LayerInde
|
||||||
// xy_distance that cant support it. Making the area smaller by xy_distance fixes this.
|
// xy_distance that cant support it. Making the area smaller by xy_distance fixes this.
|
||||||
- (radius + m_current_min_xy_dist + m_current_min_xy_dist_delta),
|
- (radius + m_current_min_xy_dist + m_current_min_xy_dist_delta),
|
||||||
jtMiter, 1.2);
|
jtMiter, 1.2);
|
||||||
|
if(throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -810,6 +820,7 @@ void TreeModelVolumes::calculateWallRestrictions(const std::vector<RadiusLayerPa
|
||||||
polygons_simplify(
|
polygons_simplify(
|
||||||
intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
|
intersection(getCollision(0, layer_idx, true), getCollision(radius, layer_idx - 1, true)),
|
||||||
m_min_resolution, polygons_strictly_simple);
|
m_min_resolution, polygons_strictly_simple);
|
||||||
|
if (throw_on_cancel)
|
||||||
throw_on_cancel();
|
throw_on_cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -167,6 +167,8 @@ public:
|
||||||
this->ceilRadius(radius + m_current_min_xy_dist_delta) - m_current_min_xy_dist_delta;
|
this->ceilRadius(radius + m_current_min_xy_dist_delta) - m_current_min_xy_dist_delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygon m_bed_area;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Caching polygons for a range of layers.
|
// Caching polygons for a range of layers.
|
||||||
class LayerPolygonCache {
|
class LayerPolygonCache {
|
||||||
|
|
|
@ -669,6 +669,7 @@ static Point bounding_box_middle(const BoundingBox &bbox)
|
||||||
TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_params)
|
TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_params)
|
||||||
: m_object(&object), m_slicing_params(slicing_params), m_object_config(&object.config())
|
: m_object(&object), m_slicing_params(slicing_params), m_object_config(&object.config())
|
||||||
{
|
{
|
||||||
|
m_print_config = &m_object->print()->config();
|
||||||
m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers;
|
m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers;
|
||||||
support_type = m_object_config->support_type;
|
support_type = m_object_config->support_type;
|
||||||
support_style = m_object_config->support_style;
|
support_style = m_object_config->support_style;
|
||||||
|
@ -705,8 +706,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
|
||||||
tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0;
|
tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0;
|
||||||
// by default tree support needs no infill, unless it's tree hybrid which contains normal nodes.
|
// by default tree support needs no infill, unless it's tree hybrid which contains normal nodes.
|
||||||
with_infill = support_pattern != smpNone && support_pattern != smpDefault;
|
with_infill = support_pattern != smpNone && support_pattern != smpDefault;
|
||||||
const PrintConfig& print_config = m_object->print()->config();
|
m_machine_border.contour = get_bed_shape_with_excluded_area(*m_print_config);
|
||||||
m_machine_border.contour = get_bed_shape_with_excluded_area(print_config);
|
|
||||||
Vec3d plate_offset = m_object->print()->get_plate_origin();
|
Vec3d plate_offset = m_object->print()->get_plate_origin();
|
||||||
// align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset)
|
// align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset)
|
||||||
m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift);
|
m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift);
|
||||||
|
@ -1404,10 +1404,9 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& pr
|
||||||
|
|
||||||
void TreeSupport::generate_toolpaths()
|
void TreeSupport::generate_toolpaths()
|
||||||
{
|
{
|
||||||
const PrintConfig &print_config = m_object->print()->config();
|
|
||||||
const PrintObjectConfig &object_config = m_object->config();
|
const PrintObjectConfig &object_config = m_object->config();
|
||||||
coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
|
coordf_t support_extrusion_width = m_support_params.support_extrusion_width;
|
||||||
coordf_t nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_filament - 1);
|
coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(object_config.support_filament - 1);
|
||||||
coordf_t layer_height = object_config.layer_height.value;
|
coordf_t layer_height = object_config.layer_height.value;
|
||||||
const size_t wall_count = object_config.tree_support_wall_count.value;
|
const size_t wall_count = object_config.tree_support_wall_count.value;
|
||||||
|
|
||||||
|
@ -1882,7 +1881,7 @@ Polygons TreeSupport::contact_nodes_to_polygon(const std::vector<Node*>& contact
|
||||||
void TreeSupport::generate()
|
void TreeSupport::generate()
|
||||||
{
|
{
|
||||||
if (support_style == smsOrganic) {
|
if (support_style == smsOrganic) {
|
||||||
generate_tree_support_3D(*m_object, this->throw_on_cancel);
|
generate_tree_support_3D(*m_object, this, this->throw_on_cancel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -403,6 +403,13 @@ public:
|
||||||
std::unordered_map<double, size_t> printZ_to_lightninglayer;
|
std::unordered_map<double, size_t> printZ_to_lightninglayer;
|
||||||
|
|
||||||
std::function<void()> throw_on_cancel;
|
std::function<void()> throw_on_cancel;
|
||||||
|
const PrintConfig* m_print_config;
|
||||||
|
/*!
|
||||||
|
* \brief Polygons representing the limits of the printable area of the
|
||||||
|
* machine
|
||||||
|
*/
|
||||||
|
ExPolygon m_machine_border;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*!
|
/*!
|
||||||
* \brief Generator for model collision, avoidance and internal guide volumes
|
* \brief Generator for model collision, avoidance and internal guide volumes
|
||||||
|
@ -429,11 +436,6 @@ private:
|
||||||
bool with_infill = false;
|
bool with_infill = false;
|
||||||
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* \brief Polygons representing the limits of the printable area of the
|
|
||||||
* machine
|
|
||||||
*/
|
|
||||||
ExPolygon m_machine_border;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Draws circles around each node of the tree into the final support.
|
* \brief Draws circles around each node of the tree into the final support.
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
// CuraEngine is released under the terms of the AGPLv3 or higher.
|
||||||
|
|
||||||
#include "TreeSupport3D.hpp"
|
#include "TreeSupport3D.hpp"
|
||||||
#include "TreeSupportCommon.hpp"
|
|
||||||
#include "SupportCommon.hpp"
|
|
||||||
|
|
||||||
#include "../AABBTreeIndirect.hpp"
|
#include "../AABBTreeIndirect.hpp"
|
||||||
#include "../BuildVolume.hpp"
|
#include "../BuildVolume.hpp"
|
||||||
|
@ -21,6 +19,9 @@
|
||||||
#include "../Polygon.hpp"
|
#include "../Polygon.hpp"
|
||||||
#include "../Polyline.hpp"
|
#include "../Polyline.hpp"
|
||||||
#include "../MutablePolygon.hpp"
|
#include "../MutablePolygon.hpp"
|
||||||
|
#include "TreeSupportCommon.hpp"
|
||||||
|
#include "SupportCommon.hpp"
|
||||||
|
#include "TreeSupport.hpp"
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -34,6 +35,7 @@
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include <tbb/parallel_for.h>
|
#include <tbb/parallel_for.h>
|
||||||
|
#include <tbb/parallel_for_each.h>
|
||||||
|
|
||||||
#if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32)
|
#if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32)
|
||||||
#define TREE_SUPPORT_SHOW_ERRORS_WIN32
|
#define TREE_SUPPORT_SHOW_ERRORS_WIN32
|
||||||
|
@ -4106,13 +4108,14 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Produce the support G-code.
|
// Produce the support G-code.
|
||||||
// Used by both classic and tree supports.
|
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 raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(),
|
SupportGeneratorLayersPtr layers_sorted = generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||||
top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
|
|
||||||
#if 1 //#ifdef SLIC3R_DEBUG
|
// BBS: This is a hack to avoid the support being generated outside the bed area. See #4769.
|
||||||
SupportGeneratorLayersPtr layers_sorted =
|
tbb::parallel_for_each(layers_sorted.begin(), layers_sorted.end(), [&](SupportGeneratorLayer *layer) {
|
||||||
#endif // SLIC3R_DEBUG
|
if (layer) layer->polygons = intersection(layer->polygons, volumes.m_bed_area);
|
||||||
generate_support_layers(print_object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
});
|
||||||
|
|
||||||
// Don't fill in the tree supports, make them hollow with just a single sheath line.
|
// Don't fill in the tree supports, make them hollow with just a single sheath line.
|
||||||
generate_support_toolpaths(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);
|
raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers, base_interface_layers);
|
||||||
|
@ -4363,9 +4366,10 @@ void organic_draw_branches(
|
||||||
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
std::vector<Polygons> slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel);
|
||||||
bottom_contacts.clear();
|
bottom_contacts.clear();
|
||||||
//FIXME parallelize?
|
//FIXME parallelize?
|
||||||
for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++ i)
|
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] = 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;
|
size_t num_empty = 0;
|
||||||
if (slices.front().empty()) {
|
if (slices.front().empty()) {
|
||||||
// Some of the initial layers are empty.
|
// Some of the initial layers are empty.
|
||||||
|
@ -4568,7 +4572,7 @@ void organic_draw_branches(
|
||||||
|
|
||||||
} // namespace TreeSupport3D
|
} // namespace TreeSupport3D
|
||||||
|
|
||||||
void generate_tree_support_3D(PrintObject &print_object, std::function<void()> throw_on_cancel)
|
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel)
|
||||||
{
|
{
|
||||||
size_t idx = 0;
|
size_t idx = 0;
|
||||||
for (const PrintObject *po : print_object.print()->objects()) {
|
for (const PrintObject *po : print_object.print()->objects()) {
|
||||||
|
@ -4576,9 +4580,13 @@ void generate_tree_support_3D(PrintObject &print_object, std::function<void()> t
|
||||||
break;
|
break;
|
||||||
++idx;
|
++idx;
|
||||||
}
|
}
|
||||||
TreeSupport3D::generate_support_areas(*print_object.print(),
|
|
||||||
BuildVolume(Pointfs{ Vec2d{ -300., -300. }, Vec2d{ -300., +300. }, Vec2d{ +300., +300. }, Vec2d{ +300., -300. } }, 0.), { idx },
|
Points bedpts = tree_support->m_machine_border.contour.points;
|
||||||
throw_on_cancel);
|
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(), build_volume, { idx }, throw_on_cancel);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -310,7 +310,7 @@ void organic_draw_branches(
|
||||||
|
|
||||||
} // namespace TreeSupport3D
|
} // namespace TreeSupport3D
|
||||||
|
|
||||||
void generate_tree_support_3D(PrintObject &print_object, std::function<void()> throw_on_cancel = []{});
|
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{});
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue