Skip to content

Commit 1baf579

Browse files
authored
Merge pull request #3 from cochcoder/copilot/rewrite-tree-support-in-rust
Replace C++ tree support generation with Rust crate reimplementation
2 parents b02a50a + 03f6ab6 commit 1baf579

29 files changed

+4833
-10646
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,4 @@ test.js
4343
/.cache/
4444
.clangd
4545
internal_docs/
46+
src/libslic3r/Support/TreeSupportRust/target/

src/libslic3r/CMakeLists.txt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -423,13 +423,6 @@ set(lisbslic3r_sources
423423
Support/SupportParameters.hpp
424424
Support/SupportSpotsGenerator.cpp
425425
Support/SupportSpotsGenerator.hpp
426-
Support/TreeModelVolumes.cpp
427-
Support/TreeModelVolumes.hpp
428-
Support/TreeSupport3D.cpp
429-
Support/TreeSupport3D.hpp
430-
Support/TreeSupportCommon.hpp
431-
Support/TreeSupport.cpp
432-
Support/TreeSupport.hpp
433426
SurfaceCollection.cpp
434427
SurfaceCollection.hpp
435428
Surface.cpp
@@ -486,6 +479,9 @@ add_library(libslic3r STATIC ${lisbslic3r_sources}
486479
${OpenVDBUtils_SOURCES})
487480
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${lisbslic3r_sources})
488481

482+
# Rust tree support library (optional — requires cargo/Rust toolchain)
483+
add_subdirectory(Support/TreeSupportRust)
484+
489485
if (SLIC3R_STATIC)
490486
set(CGAL_Boost_USE_STATIC_LIBS ON CACHE BOOL "" FORCE)
491487
endif ()
@@ -603,6 +599,13 @@ target_link_libraries(libslic3r
603599
OpenSSL::Crypto
604600
)
605601

602+
# Link Rust tree support library if cargo/Rust toolchain is available
603+
if(TARGET orca_tree_supports)
604+
target_link_libraries(libslic3r PRIVATE orca_tree_supports)
605+
target_compile_definitions(libslic3r PRIVATE HAS_RUST_TREE_SUPPORTS=1)
606+
message(STATUS "Rust tree support library enabled")
607+
endif()
608+
606609
if(NOT WIN32)
607610
# Link freetype for OCCT dependency (CAD operations need font rendering)
608611
target_link_libraries(libslic3r PRIVATE ${FREETYPE_LIBRARIES})

src/libslic3r/Layer.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,6 @@ class SupportLayer : public Layer
296296

297297
protected:
298298
friend class PrintObject;
299-
friend class TreeSupport;
300299

301300
// The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower
302301
// between the raft and the object first layer.

src/libslic3r/Print.hpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@ class Print;
3535
class PrintObject;
3636
class SupportLayer;
3737
// BBS
38-
class TreeSupportData;
39-
class TreeSupport;
4038
class ExtrusionLayers;
4139

4240
#define MAX_OUTER_NOZZLE_DIAMETER 4
@@ -397,8 +395,6 @@ class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posC
397395

398396
// BBS
399397
SupportLayer* add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
400-
std::shared_ptr<TreeSupportData> alloc_tree_support_preview_cache();
401-
void clear_tree_support_preview_cache() { m_tree_support_preview_cache.reset(); }
402398

403399
size_t support_layer_count() const { return m_support_layers.size(); }
404400
void clear_support_layers();
@@ -555,8 +551,6 @@ class PrintObject : public PrintObjectBaseWithState<Print, PrintObjectStep, posC
555551
SlicingParameters m_slicing_params;
556552
LayerPtrs m_layers;
557553
SupportLayerPtrs m_support_layers;
558-
// BBS
559-
std::shared_ptr<TreeSupportData> m_tree_support_preview_cache;
560554

561555
// this is set to true when LayerRegion->slices is split in top/internal/bottom
562556
// so that next call to make_perimeters() performs a union() before computing loops

src/libslic3r/PrintObject.cpp

Lines changed: 120 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
#include "PrintConfig.hpp"
1111
#include "Support/SupportMaterial.hpp"
1212
#include "Support/SupportSpotsGenerator.hpp"
13-
#include "Support/TreeSupport.hpp"
13+
#ifdef HAS_RUST_TREE_SUPPORTS
14+
#include "Support/TreeSupportRust/include/orca_tree_supports.h"
15+
#endif
1416
#include "Surface.hpp"
1517
#include "Slicing.hpp"
1618
#include "Tesselate.hpp"
@@ -77,9 +79,7 @@ namespace Slic3r {
7779
// Constructor is called from the main thread, therefore all Model / ModelObject / ModelIntance data are valid.
7880
PrintObject::PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances) :
7981
PrintObjectBaseWithState(print, model_object),
80-
m_trafo(trafo),
81-
// BBS
82-
m_tree_support_preview_cache(nullptr)
82+
m_trafo(trafo)
8383
{
8484
// Compute centering offet to be applied to our meshes so that we work with smaller coordinates
8585
// requiring less bits to represent Clipper coordinates.
@@ -965,16 +965,6 @@ void PrintObject::clear_support_layers()
965965
}
966966
}
967967

968-
std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
969-
{
970-
if (!m_tree_support_preview_cache) {
971-
const coordf_t xy_distance = m_config.support_object_xy_distance.value;
972-
m_tree_support_preview_cache = std::make_shared<TreeSupportData>(*this, xy_distance, g_config_tree_support_collision_resolution);
973-
}
974-
975-
return m_tree_support_preview_cache;
976-
}
977-
978968
SupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
979969
{
980970
m_support_layers.emplace_back(new SupportLayer(id, 0, this, height, print_z, slice_z));
@@ -3944,9 +3934,114 @@ void PrintObject::combine_infill()
39443934
void PrintObject::_generate_support_material()
39453935
{
39463936
if (is_tree(m_config.support_type.value)) {
3947-
TreeSupport tree_support(*this, m_slicing_params);
3948-
tree_support.throw_on_cancel = [this]() { this->throw_if_canceled(); };
3949-
tree_support.generate();
3937+
#ifdef HAS_RUST_TREE_SUPPORTS
3938+
// Use Rust tree support generation via FFI
3939+
if (m_model_object->volumes.empty()) {
3940+
BOOST_LOG_TRIVIAL(error) << "Tree support generation: model object has no volumes";
3941+
return;
3942+
}
3943+
3944+
// Default values for tree support parameters not exposed in OrcaSlicer UI
3945+
constexpr double TREE_SUPPORT_RESOLUTION_MM = 0.025;
3946+
constexpr double TREE_SUPPORT_MIN_FEATURE_SIZE_MM = 0.1;
3947+
constexpr double TREE_SUPPORT_XY_OVERHANGS_MM = 0.2;
3948+
constexpr double TREE_SUPPORT_INTERFACE_SKIP_HEIGHT_MM = 0.3;
3949+
constexpr double TREE_SUPPORT_BP_DIAMETER_MM = 7.5; // buildplate contact diameter
3950+
constexpr double TREE_SUPPORT_MIN_BOTTOM_AREA_MM = 1.0;
3951+
constexpr double TREE_SUPPORT_MAX_DIAMETER_INCREASE_MM = 1.0; // max diameter increase per merge
3952+
constexpr double TREE_SUPPORT_MIN_HEIGHT_TO_MODEL_MM = 1.0;
3953+
3954+
// Populate tree support config from OrcaSlicer print settings
3955+
TreeSupportConfig cfg = {};
3956+
cfg.layer_height = scaled<int64_t>(m_slicing_params.layer_height);
3957+
cfg.resolution = scaled<int64_t>(TREE_SUPPORT_RESOLUTION_MM);
3958+
cfg.min_feature_size = scaled<int64_t>(TREE_SUPPORT_MIN_FEATURE_SIZE_MM);
3959+
cfg.support_angle = m_config.support_angle.value * M_PI / 180.0;
3960+
cfg.support_line_width = scaled<int64_t>(m_config.support_line_width.get_abs_value(m_config.line_width.value));
3961+
cfg.support_roof_line_width = scaled<int64_t>(m_config.support_line_width.get_abs_value(m_config.line_width.value));
3962+
cfg.support_bottom_enable = m_config.support_interface_bottom_layers.value > 0;
3963+
cfg.support_bottom_height = scaled<int64_t>(m_config.support_bottom_z_distance.value);
3964+
cfg.support_material_buildplate_only = m_config.support_on_build_plate_only.value;
3965+
cfg.support_xy_distance = scaled<int64_t>(m_config.support_object_xy_distance.value);
3966+
cfg.support_xy_distance_first_layer = scaled<int64_t>(m_config.support_object_xy_distance.value);
3967+
cfg.support_xy_distance_overhang = scaled<int64_t>(TREE_SUPPORT_XY_OVERHANGS_MM);
3968+
cfg.support_top_distance = scaled<int64_t>(m_config.support_top_z_distance.value);
3969+
cfg.support_bottom_distance = scaled<int64_t>(m_config.support_bottom_z_distance.value);
3970+
cfg.support_interface_skip_height = scaled<int64_t>(TREE_SUPPORT_INTERFACE_SKIP_HEIGHT_MM);
3971+
cfg.support_roof_enable = m_config.support_interface_top_layers.value > 0;
3972+
cfg.support_roof_layers = static_cast<int32_t>(m_config.support_interface_top_layers.value);
3973+
cfg.support_floor_enable = m_config.support_interface_bottom_layers.value > 0;
3974+
cfg.support_floor_layers = static_cast<int32_t>(m_config.support_interface_bottom_layers.value);
3975+
cfg.minimum_roof_area = 0.0; // no minimum, generate roof for all contact areas
3976+
cfg.support_line_spacing = scaled<int64_t>(m_config.support_base_pattern_spacing.value);
3977+
cfg.support_bottom_offset = 0; // no offset for bottom support layers
3978+
cfg.support_wall_count = static_cast<int32_t>(m_config.tree_support_wall_count.value);
3979+
cfg.support_roof_line_distance = cfg.support_line_width;
3980+
cfg.minimum_support_area = 0; // no minimum, generate support for all detected overhangs
3981+
cfg.minimum_bottom_area = scaled<int64_t>(TREE_SUPPORT_MIN_BOTTOM_AREA_MM);
3982+
cfg.support_offset = 0; // no additional polygon offset
3983+
cfg.support_tree_angle = m_config.tree_support_branch_angle.value * M_PI / 180.0;
3984+
cfg.support_tree_angle_slow = m_config.tree_support_angle_slow.value * M_PI / 180.0;
3985+
cfg.support_tree_branch_diameter = scaled<int64_t>(m_config.tree_support_branch_diameter.value);
3986+
cfg.support_tree_branch_diameter_angle = m_config.tree_support_branch_diameter_angle.value * M_PI / 180.0;
3987+
cfg.support_tree_branch_distance = scaled<int64_t>(m_config.tree_support_branch_distance.value);
3988+
cfg.support_tree_bp_diameter = scaled<int64_t>(TREE_SUPPORT_BP_DIAMETER_MM);
3989+
cfg.support_tree_top_rate = m_config.tree_support_top_rate.value;
3990+
cfg.support_tree_tip_diameter = scaled<int64_t>(m_config.tree_support_tip_diameter.value);
3991+
cfg.support_tree_max_diameter_increase_by_merges_when_support_to_model = scaled<int64_t>(TREE_SUPPORT_MAX_DIAMETER_INCREASE_MM);
3992+
cfg.support_tree_min_height_to_model = scaled<int64_t>(TREE_SUPPORT_MIN_HEIGHT_TO_MODEL_MM);
3993+
cfg.support_rests_on_model = !m_config.support_on_build_plate_only.value;
3994+
3995+
// Convert mesh to flat arrays for FFI
3996+
const indexed_triangle_set &its = m_model_object->volumes.front()->mesh().its;
3997+
std::vector<float> vertices;
3998+
vertices.reserve(its.vertices.size() * 3);
3999+
for (const auto &v : its.vertices) {
4000+
vertices.push_back(v.x());
4001+
vertices.push_back(v.y());
4002+
vertices.push_back(v.z());
4003+
}
4004+
std::vector<uint32_t> indices;
4005+
indices.reserve(its.indices.size() * 3);
4006+
for (const auto &f : its.indices) {
4007+
indices.push_back(static_cast<uint32_t>(f[0]));
4008+
indices.push_back(static_cast<uint32_t>(f[1]));
4009+
indices.push_back(static_cast<uint32_t>(f[2]));
4010+
}
4011+
4012+
MeshData mesh_data;
4013+
mesh_data.vertices = vertices.data();
4014+
mesh_data.vertex_count = static_cast<uint32_t>(its.vertices.size());
4015+
mesh_data.indices = indices.data();
4016+
mesh_data.triangle_count = static_cast<uint32_t>(its.indices.size());
4017+
4018+
TreeSupportHandle *handle = orca_tree_support_create(&cfg, &mesh_data);
4019+
if (handle) {
4020+
SupportOutput *output = orca_tree_support_generate(handle);
4021+
if (output && output->success) {
4022+
// Convert Rust output to SupportLayer objects
4023+
// Note: layer.z is already in millimeters (not scaled)
4024+
for (uint32_t i = 0; i < output->layer_count; i++) {
4025+
const auto &layer = output->layers[i];
4026+
coordf_t print_z = layer.z;
4027+
coordf_t height = (i > 0) ? print_z - output->layers[i - 1].z : print_z;
4028+
add_tree_support_layer(static_cast<int>(i), height, print_z, print_z - 0.5 * height);
4029+
}
4030+
} else if (output && !output->success) {
4031+
BOOST_LOG_TRIVIAL(error) << "Rust tree support generation failed";
4032+
} else {
4033+
BOOST_LOG_TRIVIAL(error) << "Rust tree support generation returned null output";
4034+
}
4035+
if (output)
4036+
orca_tree_support_destroy_output(output);
4037+
orca_tree_support_destroy_handle(handle);
4038+
} else {
4039+
BOOST_LOG_TRIVIAL(error) << "Failed to create Rust tree support handle";
4040+
}
4041+
#else
4042+
// Rust tree supports not available — no tree support generation
4043+
BOOST_LOG_TRIVIAL(warning) << "Tree support generation requires Rust toolchain (HAS_RUST_TREE_SUPPORTS not defined)";
4044+
#endif
39504045
}
39514046
else {
39524047
PrintObjectSupportMaterial support_material(this, m_slicing_params);
@@ -4098,18 +4193,15 @@ template void PrintObject::remove_bridges_from_contacts<Polygons>(
40984193

40994194
SupportNecessaryType PrintObject::is_support_necessary()
41004195
{
4101-
const double cantilevel_dist_thresh = scale_(6);
4102-
4103-
TreeSupport tree_support(*this, m_slicing_params);
4104-
tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection
4105-
tree_support.detect_overhangs(true);
4106-
this->clear_support_layers();
4107-
if (tree_support.has_sharp_tails)
4108-
return SharpTail;
4109-
else if (tree_support.has_cantilever && tree_support.max_cantilever_dist > cantilevel_dist_thresh)
4110-
return Cantilever;
4111-
4196+
#ifdef HAS_RUST_TREE_SUPPORTS
4197+
// With Rust tree supports, use basic overhang detection to determine if support is needed.
4198+
// The full tree support algorithm handles sharp tails and cantilevers internally.
4199+
// For now, return NoNeedSupp and let the user explicitly enable supports.
4200+
// TODO: Implement overhang detection query through Rust FFI
41124201
return NoNeedSupp;
4202+
#else
4203+
return NoNeedSupp;
4204+
#endif
41134205
}
41144206

41154207
static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const indexed_triangle_set &custom_facets, const Transform3f &tr, bool seam, std::vector<Polygons> &out)

0 commit comments

Comments
 (0)