Apply the Belt Slicing Transform to Painted Supports and Seams

Painted support/seam facets, support volumes, seam occlusion, MMU and fuzzy skin painting (top/bottom
and side facets) and the adaptive infill octree used trafo_centered(), or trafo() with a centre-offset
shift, while the layers were sliced with the belt rotation, remap and Z lift; they now share
PrintObject::trafo_sliced().
This commit is contained in:
Hanif Koh
2026-09-13 16:28:09 +08:00
parent 9cca3093ee
commit 5092831007
5 changed files with 26 additions and 11 deletions

View File

@@ -627,7 +627,7 @@ void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po,
SeamPosition seam_position = spAligned) { SeamPosition seam_position = spAligned) {
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: gather occlusion meshes: start"; << "SeamPlacer: gather occlusion meshes: start";
auto obj_transform = po->trafo_centered(); auto obj_transform = po->trafo_sliced();
indexed_triangle_set triangle_set; indexed_triangle_set triangle_set;
indexed_triangle_set negative_volumes_set; indexed_triangle_set negative_volumes_set;
//add all parts //add all parts
@@ -712,7 +712,7 @@ void gather_enforcers_blockers(GlobalModelInfo &result, const PrintObject *po) {
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: build AABB trees for raycasting enforcers/blockers: start"; << "SeamPlacer: build AABB trees for raycasting enforcers/blockers: start";
auto obj_transform = po->trafo_centered(); auto obj_transform = po->trafo_sliced();
for (const ModelVolume *mv : po->model_object()->volumes) { for (const ModelVolume *mv : po->model_object()->volumes) {
if (mv->is_seam_painted()) { if (mv->is_seam_painted()) {

View File

@@ -1215,7 +1215,7 @@ static inline std::vector<std::vector<ExPolygons>> segmentation_top_and_bottom_l
// project downards pointing painted triangles over bottom surfaces. // project downards pointing painted triangles over bottom surfaces.
std::vector<std::vector<Polygons>> top_raw(num_facets_states), bottom_raw(num_facets_states); std::vector<std::vector<Polygons>> top_raw(num_facets_states), bottom_raw(num_facets_states);
std::vector<float> zs = zs_from_layers(layers); std::vector<float> zs = zs_from_layers(layers);
Transform3d object_trafo = print_object.trafo_centered(); Transform3d object_trafo = print_object.trafo_sliced();
#ifdef MM_SEGMENTATION_DEBUG_TOP_BOTTOM #ifdef MM_SEGMENTATION_DEBUG_TOP_BOTTOM
static int iRun = 0; static int iRun = 0;
@@ -2039,17 +2039,19 @@ std::vector<std::vector<ExPolygons>> segmentation_by_painting(const PrintObject
} }
BOOST_LOG_TRIVIAL(debug) << "Print object segmentation - Projection of painted triangles - Begin"; BOOST_LOG_TRIVIAL(debug) << "Print object segmentation - Projection of painted triangles - Begin";
// The layers were sliced in this frame (belt rotation, remap and Z lift included), and it already centers the object.
const Transform3d object_trafo = print_object.trafo_sliced();
for (const ModelVolume *mv : print_object.model_object()->volumes) { for (const ModelVolume *mv : print_object.model_object()->volumes) {
const ModelVolumeFacetsInfo facets_info = extract_facets_info(*mv); const ModelVolumeFacetsInfo facets_info = extract_facets_info(*mv);
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_facets_states), [&mv, &print_object, &facets_info, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) { tbb::parallel_for(tbb::blocked_range<size_t>(1, num_facets_states), [&mv, &object_trafo, &facets_info, &layers, &edge_grids, &painted_lines, &painted_lines_mutex, &input_expolygons, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) { for (size_t extruder_idx = range.begin(); extruder_idx < range.end(); ++extruder_idx) {
throw_on_cancel_callback(); throw_on_cancel_callback();
const indexed_triangle_set custom_facets = facets_info.facets_annotation.get_facets(*mv, EnforcerBlockerType(extruder_idx)); const indexed_triangle_set custom_facets = facets_info.facets_annotation.get_facets(*mv, EnforcerBlockerType(extruder_idx));
if (!mv->is_model_part() || custom_facets.indices.empty()) if (!mv->is_model_part() || custom_facets.indices.empty())
continue; continue;
const Transform3f tr = print_object.trafo().cast<float>() * mv->get_matrix().cast<float>(); const Transform3f tr = (object_trafo * mv->get_matrix()).cast<float>();
tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &print_object, &layers, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) { tbb::parallel_for(tbb::blocked_range<size_t>(0, custom_facets.indices.size()), [&tr, &custom_facets, &layers, &edge_grids, &input_expolygons, &painted_lines, &painted_lines_mutex, &extruder_idx](const tbb::blocked_range<size_t> &range) {
for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) { for (size_t facet_idx = range.begin(); facet_idx < range.end(); ++facet_idx) {
float min_z = std::numeric_limits<float>::max(); float min_z = std::numeric_limits<float>::max();
float max_z = std::numeric_limits<float>::lowest(); float max_z = std::numeric_limits<float>::lowest();
@@ -2102,7 +2104,6 @@ std::vector<std::vector<ExPolygons>> segmentation_by_painting(const PrintObject
Line line_to_test(Point(scale_(line_start_f.x()), scale_(line_start_f.y())), Line line_to_test(Point(scale_(line_start_f.x()), scale_(line_start_f.y())),
Point(scale_(line_end_f.x()), scale_(line_end_f.y()))); Point(scale_(line_end_f.x()), scale_(line_end_f.y())));
line_to_test.translate(-print_object.center_offset());
// BoundingBoxes for EdgeGrids are computed from printable regions. It is possible that the painted line (line_to_test) could // BoundingBoxes for EdgeGrids are computed from printable regions. It is possible that the painted line (line_to_test) could
// be outside EdgeGrid's BoundingBox, for example, when the negative volume is used on the painted area (GH #7618). // be outside EdgeGrid's BoundingBox, for example, when the negative volume is used on the painted area (GH #7618).

View File

@@ -359,6 +359,9 @@ public:
// Trafo with the center_offset() applied after the transformation, to center the object in XY before slicing. // Trafo with the center_offset() applied after the transformation, to center the object in XY before slicing.
Transform3d trafo_centered() const Transform3d trafo_centered() const
{ Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0)); return t; } { Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0)); return t; }
// trafo_centered() with the belt pre-slice transforms applied: the frame the layers were sliced in (Layer::slice_z).
// Equal to trafo_centered() unless a belt rotation or pre-slice remap is active.
Transform3d trafo_sliced() const;
const PrintInstances& instances() const { return m_instances; } const PrintInstances& instances() const { return m_instances; }
PrintInstances &instances() { return m_instances; } PrintInstances &instances() { return m_instances; }

View File

@@ -1101,7 +1101,10 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set(); indexed_triangle_set mesh = this->model_object()->raw_indexed_triangle_set();
// Rotate mesh and build octree on it with axis-aligned (standart base) cubes. // Rotate mesh and build octree on it with axis-aligned (standart base) cubes.
auto to_octree = transform_to_octree().toRotationMatrix(); auto to_octree = transform_to_octree().toRotationMatrix();
its_transform(mesh, to_octree * this->trafo_centered(), true); // Overhangs below are placed at Layer::bottom_z(), which includes the belt global Z offset.
Transform3d object_trafo = this->trafo_sliced();
object_trafo.translation().z() += m_belt_global_z_offset;
its_transform(mesh, to_octree * object_trafo, true);
// Triangulate internal bridging surfaces. // 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_bottom_z.size(), size_t(1)));
@@ -5124,6 +5127,7 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index
void PrintObject::project_and_append_custom_facets( void PrintObject::project_and_append_custom_facets(
bool seam, EnforcerBlockerType type, std::vector<Polygons>& out, std::vector<std::pair<Vec3f, Vec3f>>* vertical_points) const bool seam, EnforcerBlockerType type, std::vector<Polygons>& out, std::vector<std::pair<Vec3f, Vec3f>>* vertical_points) const
{ {
const Transform3d object_trafo = this->trafo_sliced();
for (const ModelVolume* mv : this->model_object()->volumes) for (const ModelVolume* mv : this->model_object()->volumes)
if (mv->is_model_part()) { if (mv->is_model_part()) {
const indexed_triangle_set custom_facets = seam const indexed_triangle_set custom_facets = seam
@@ -5132,12 +5136,12 @@ void PrintObject::project_and_append_custom_facets(
if (! custom_facets.indices.empty()) { if (! custom_facets.indices.empty()) {
if (seam) if (seam)
project_triangles_to_slabs(this->layers(), custom_facets, project_triangles_to_slabs(this->layers(), custom_facets,
(this->trafo_centered() * mv->get_matrix()).cast<float>(), (object_trafo * mv->get_matrix()).cast<float>(),
seam, out); seam, out);
else { else {
std::vector<Polygons> projected; std::vector<Polygons> projected;
// Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane. // Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane.
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, vertical_points, [](){}); slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), object_trafo * mv->get_matrix(), nullptr, &projected, vertical_points, [](){});
// Merge these projections with the output, layer by layer. // Merge these projections with the output, layer by layer.
assert(! projected.empty()); assert(! projected.empty());
assert(out.empty() || out.size() == projected.size()); assert(out.empty() || out.size() == projected.size());

View File

@@ -1806,6 +1806,13 @@ ExPolygons PrintObject::_shrink_contour_holes(double contour_delta, double hole_
return union_ex(new_ex_polys); return union_ex(new_ex_polys);
} }
Transform3d PrintObject::trafo_sliced() const
{
Transform3d trafo = this->trafo_centered();
BeltSliceStrategy::apply_preslice_transforms(trafo, this->print()->config(), this->model_object()->volumes);
return trafo;
}
std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const
{ {
auto it_volume = this->model_object()->volumes.begin(); auto it_volume = this->model_object()->volumes.begin();
@@ -1820,7 +1827,7 @@ std::vector<Polygons> PrintObject::slice_support_volumes(const ModelVolumeType m
const Print *print = this->print(); const Print *print = this->print();
auto throw_on_cancel_callback = std::function<void()>([print](){ print->throw_if_canceled(); }); auto throw_on_cancel_callback = std::function<void()>([print](){ print->throw_if_canceled(); });
MeshSlicingParamsEx params; MeshSlicingParamsEx params;
params.trafo = this->trafo_centered(); params.trafo = this->trafo_sliced();
for (; it_volume != it_volume_end; ++ it_volume) for (; it_volume != it_volume_end; ++ it_volume)
if ((*it_volume)->type() == model_volume_type) { if ((*it_volume)->type() == model_volume_type) {
std::vector<ExPolygons> slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback); std::vector<ExPolygons> slices2 = slice_volume(*(*it_volume), zs, params, throw_on_cancel_callback);