Merge branch 'main' into belt-printer

This commit is contained in:
SoftFever
2026-10-08 23:03:56 +08:00
committed by GitHub
246 changed files with 1288 additions and 3374 deletions
+125 -34
View File
@@ -70,6 +70,7 @@
#include <utility>
#include <boost/log/trivial.hpp>
#include <Eigen/Core>
#include <tbb/parallel_for.h>
#include <tbb/spin_mutex.h>
@@ -723,7 +724,8 @@ void PrintObject::prepare_infill()
bool needs_separated_components = false;
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
const PrintRegionConfig &rc = this->printing_region(i).config();
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model ||
(rc.sparse_infill_density > 0 && is_octree_infill_pattern(rc.sparse_infill_pattern))) {
needs_separated_components = true;
break;
}
@@ -740,8 +742,9 @@ void PrintObject::prepare_infill()
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
needs_separated_components = false;
}
m_separated_body_bboxes.clear();
for (Layer *layer : m_layers)
layer->lslices_separated_component_bboxes.clear();
layer->lslices_separated_component_ids.clear();
if (needs_separated_components) {
const size_t nl = m_layers.size();
std::vector<size_t> offset(nl + 1, 0); // Orca: flat index of the first island of each layer
@@ -792,17 +795,20 @@ void PrintObject::prepare_infill()
});
}
}
// Orca: Full bounding box of each body, indexed by its union-find root.
std::vector<BoundingBox> body_bbox(nreg);
for (size_t i = 0; i < nl; ++ i)
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
// Orca: Store the body bbox for every island.
// Orca: Number the bodies by their first island and merge the bounding boxes of their islands.
std::vector<size_t> body_of_root(nreg, size_t(-1));
for (size_t i = 0; i < nl; ++ i) {
Layer *layer = m_layers[i];
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
for (size_t a = 0; a < layer->lslices.size(); ++ a)
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
layer->lslices_separated_component_ids.resize(layer->lslices.size());
for (size_t a = 0; a < layer->lslices.size(); ++ a) {
size_t &body = body_of_root[find(offset[i] + a)];
if (body == size_t(-1)) {
body = m_separated_body_bboxes.size();
m_separated_body_bboxes.emplace_back();
}
m_separated_body_bboxes[body].merge(layer->lslices_bboxes[a]);
layer->lslices_separated_component_ids[a] = body;
}
}
}
@@ -840,16 +846,13 @@ void PrintObject::infill()
if (this->set_started(posInfill)) {
m_print->set_status(35, L("Generating infill toolpath"));
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) {
[this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), this->m_lightning_generator.get());
m_layers[layer_idx]->make_fills(&m_adaptive_fill_octrees.first, &m_adaptive_fill_octrees.second, this->m_lightning_generator.get());
}
}
);
@@ -1134,14 +1137,69 @@ void PrintObject::simplify_extrusion_path()
}
}
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare_adaptive_infill_data(
const std::vector<std::pair<const Surface *, float>> &surfaces_w_bottom_z) const
// Orca: Separated body of the island containing a point of a layer, else of the island outline nearest within 1 mm, or -1.
static int separated_body_at(const Layer &layer, const Point &point)
{
int body = -1;
double best = scaled<double>(1.);
for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_ids.size() && best > 0.; ++ i) {
BoundingBox bbox = layer.lslices_bboxes[i];
bbox.offset(coord_t(best));
if (! bbox.contains(point))
continue;
const double dist = layer.lslices[i].contains(point) ? 0. : (layer.lslices[i].point_projection(point) - point).cast<double>().norm();
if (dist < best) {
best = dist;
body = int(layer.lslices_separated_component_ids[i]);
}
}
return body;
}
// Orca: The object mesh in the octree frame split by separated body. Each connected component goes to the body
// most of its sampled triangles lie on, sampled a layer height inside the solid at the layer nearest to them.
static std::vector<indexed_triangle_set> split_mesh_by_body(const PrintObject &object, const indexed_triangle_set &mesh, size_t num_bodies)
{
const Eigen::Matrix3d to_object = FillAdaptive::transform_to_world().toRotationMatrix();
const double inset = object.config().layer_height.value;
std::vector<indexed_triangle_set> bodies(num_bodies);
for (const indexed_triangle_set &component : its_split(mesh)) {
std::vector<size_t> votes(num_bodies, 0);
const size_t step = std::max<size_t>(1, component.indices.size() / 8);
for (size_t i = 0; i < component.indices.size(); i += step) {
const stl_triangle_vertex_indices &tri = component.indices[i];
const Vec3d a = component.vertices[tri[0]].cast<double>(), b = component.vertices[tri[1]].cast<double>(),
d = component.vertices[tri[2]].cast<double>();
const Vec3d normal = (b - a).cross(d - a);
const double area2 = normal.norm();
const Vec3d c = to_object * ((a + b + d) / 3. - (area2 > 0. ? Vec3d(normal * (inset / area2)) : Vec3d::Zero()));
size_t lo = 0, hi = object.layer_count();
while (lo < hi) {
const size_t mid = (lo + hi) / 2;
if (object.get_layer(int(mid))->slice_z < c.z())
lo = mid + 1;
else
hi = mid;
}
if (lo == object.layer_count() || (lo > 0 && c.z() - object.get_layer(int(lo) - 1)->slice_z < object.get_layer(int(lo))->slice_z - c.z()))
-- lo;
if (const int body = separated_body_at(*object.get_layer(int(lo)), Point(scaled<coord_t>(c.x()), scaled<coord_t>(c.y()))); body >= 0)
++ votes[body];
}
if (const auto best = std::max_element(votes.begin(), votes.end()); *best > 0)
its_merge(bodies[best - votes.begin()], component);
}
return bodies;
}
std::pair<FillAdaptive::Octrees, FillAdaptive::Octrees> PrintObject::prepare_adaptive_infill_data(
const std::vector<std::pair<const Surface *, const Layer *>> &surfaces_w_layer) const
{
using namespace FillAdaptive;
auto [adaptive_line_spacing, support_line_spacing] = adaptive_fill_line_spacing(*this);
if ((adaptive_line_spacing == 0. && support_line_spacing == 0.) || this->layers().empty())
return std::make_pair(OctreePtr(), OctreePtr());
return {};
indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set();
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes.
@@ -1152,27 +1210,60 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
its_transform(mesh, to_octree * object_trafo, true);
// Triangulate internal bridging surfaces.
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_bottom_z.size(), size_t(1)));
std::vector<std::vector<Vec3d>> overhangs(std::max(surfaces_w_layer.size(), size_t(1)));
// ^ make sure vector is not empty, even with no briding surfaces we still want to build the adaptive trees later, some continue normally
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_bottom_z.size()),
[this, &to_octree, &overhangs, &surfaces_w_bottom_z](const tbb::blocked_range<int> &range) {
tbb::parallel_for(tbb::blocked_range<int>(0, surfaces_w_layer.size()),
[this, &to_octree, &overhangs, &surfaces_w_layer](const tbb::blocked_range<int> &range) {
PRINT_OBJECT_TIME_LIMIT_MILLIS(PRINT_OBJECT_TIME_LIMIT_DEFAULT);
for (int surface_idx = range.begin(); surface_idx < range.end(); ++surface_idx) {
std::vector<Vec3d> &out = overhangs[surface_idx];
m_print->throw_if_canceled();
append(out, triangulate_expolygon_3d(surfaces_w_bottom_z[surface_idx].first->expolygon,
surfaces_w_bottom_z[surface_idx].second));
append(out, triangulate_expolygon_3d(surfaces_w_layer[surface_idx].first->expolygon,
float(surfaces_w_layer[surface_idx].second->bottom_z())));
for (Vec3d &p : out)
p = (to_octree * p).eval();
}
});
// Orca: Each body gets the octree it has when sliced on its own, from its own triangles.
std::pair<Octrees, Octrees> octrees;
const size_t num_bodies = m_separated_body_bboxes.size();
bool need_object = num_bodies <= 1;
if (num_bodies > 1) {
const std::vector<indexed_triangle_set> body_meshes = split_mesh_by_body(*this, mesh, num_bodies);
need_object = std::any_of(body_meshes.begin(), body_meshes.end(), [](const indexed_triangle_set &its) { return its.indices.empty(); });
std::vector<std::vector<Vec3d>> body_overhangs(num_bodies);
for (size_t i = 0; i < surfaces_w_layer.size(); ++ i)
if (const int body = separated_body_at(*surfaces_w_layer[i].second, surfaces_w_layer[i].first->expolygon.contour.points.front()); body >= 0)
append(body_overhangs[body], overhangs[i]);
if (adaptive_line_spacing)
octrees.first.bodies.resize(num_bodies);
if (support_line_spacing)
octrees.second.bodies.resize(num_bodies);
tbb::parallel_for(tbb::blocked_range<size_t>(0, num_bodies), [&, adaptive_spacing = adaptive_line_spacing, support_spacing = support_line_spacing](
const tbb::blocked_range<size_t> &range) {
for (size_t body = range.begin(); body < range.end(); ++ body) {
m_print->throw_if_canceled();
if (body_meshes[body].indices.empty())
continue;
if (adaptive_spacing)
octrees.first.bodies[body] = build_octree(body_meshes[body], body_overhangs[body], adaptive_spacing, false);
if (support_spacing)
octrees.second.bodies[body] = build_octree(body_meshes[body], body_overhangs[body], support_spacing, true);
}
});
}
// and gather them.
for (size_t i = 1; i < overhangs.size(); ++ i)
append(overhangs.front(), std::move(overhangs[i]));
return std::make_pair(
adaptive_line_spacing ? build_octree(mesh, overhangs.front(), adaptive_line_spacing, false) : OctreePtr(),
support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr());
// Orca: The object's octree only serves bodies that have none of their own.
if (need_object && adaptive_line_spacing)
octrees.first.object = build_octree(mesh, overhangs.front(), adaptive_line_spacing, false);
if (need_object && support_line_spacing)
octrees.second.object = build_octree(mesh, overhangs.front(), support_line_spacing, true);
return octrees;
}
FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data()
@@ -3089,14 +3180,14 @@ void PrintObject::bridge_over_infill()
std::map<size_t, Polylines> infill_lines;
// SECTION to generate infill polylines
{
std::vector<std::pair<const Surface *, float>> surfaces_w_bottom_z;
std::vector<std::pair<const Surface *, const Layer *>> surfaces_w_layer;
for (const auto &pair : surfaces_by_layer) {
for (const CandidateSurface &c : pair.second) {
surfaces_w_bottom_z.emplace_back(c.original_surface, c.region->m_layer->bottom_z());
surfaces_w_layer.emplace_back(c.original_surface, c.region->m_layer);
}
}
this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_bottom_z);
this->m_adaptive_fill_octrees = this->prepare_adaptive_infill_data(surfaces_w_layer);
std::vector<size_t> layers_to_generate_infill;
for (const auto &pair : surfaces_by_layer) {
@@ -3112,8 +3203,8 @@ void PrintObject::bridge_over_infill()
for (size_t job_idx = r.begin(); job_idx < r.end(); job_idx++) {
size_t lidx = layers_to_generate_infill[job_idx];
infill_lines.at(
lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(po->m_adaptive_fill_octrees.first.get(),
po->m_adaptive_fill_octrees.second.get(),
lidx) = po->get_layer(lidx)->generate_sparse_infill_polylines_for_anchoring(&po->m_adaptive_fill_octrees.first,
&po->m_adaptive_fill_octrees.second,
po->m_lightning_generator.get());
}
});
@@ -4640,8 +4731,8 @@ void PrintObject::combine_infill()
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
//FIXME limit the layer height to max_layer_height
double nozzle_diameter = std::min(
this->print()->config().nozzle_diameter.get_at(region.config().sparse_infill_filament_id.value - 1),
this->print()->config().nozzle_diameter.get_at(region.config().internal_solid_filament_id.value - 1));
nozzle_diameter_for_filament(this->print()->config(), region.config().sparse_infill_filament_id.value, this->print()->is_BBL_printer()),
nozzle_diameter_for_filament(this->print()->config(), region.config().internal_solid_filament_id.value, this->print()->is_BBL_printer()));
//Orca: Limit combination of infill to up to infill_combination_max_layer_height
const double infill_combination_max_layer_height = region.config().infill_combination_max_layer_height.get_abs_value(nozzle_diameter);