Anisotropic surfaces + Separated Infills (remake) (#11682)

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
This commit is contained in:
π²
2026-07-08 21:40:19 +03:00
committed by GitHub
parent 378843a4da
commit bc6ffcfb31
14 changed files with 288 additions and 50 deletions

View File

@@ -272,6 +272,10 @@ struct SurfaceFillParams
// For Gyroid: when true, use the parameterized "optimized" wave.
bool gyroid_optimized = false;
bool anisotropic_surfaces{false};
CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface};
bool separated_infills{false};
bool operator<(const SurfaceFillParams &rhs) const {
#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false;
#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false;
@@ -301,8 +305,12 @@ struct SurfaceFillParams
RETURN_COMPARE_NON_EQUAL(lateral_lattice_angle_2);
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
RETURN_COMPARE_NON_EQUAL(skin_infill_depth);
RETURN_COMPARE_NON_EQUAL(infill_overhang_angle);
RETURN_COMPARE_NON_EQUAL(gyroid_optimized);
RETURN_COMPARE_NON_EQUAL(anisotropic_surfaces);
RETURN_COMPARE_NON_EQUAL(center_of_surface_pattern);
RETURN_COMPARE_NON_EQUAL(separated_infills);
return false;
}
@@ -329,6 +337,9 @@ struct SurfaceFillParams
this->infill_lock_depth == rhs.infill_lock_depth &&
this->skin_infill_depth == rhs.skin_infill_depth &&
this->infill_overhang_angle == rhs.infill_overhang_angle &&
this->anisotropic_surfaces == rhs.anisotropic_surfaces &&
this->center_of_surface_pattern == rhs.center_of_surface_pattern &&
this->separated_infills == rhs.separated_infills &&
this->gyroid_optimized == rhs.gyroid_optimized;
}
};
@@ -868,6 +879,9 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
params.lateral_lattice_angle_1 = region_config.lateral_lattice_angle_1;
params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2;
params.infill_overhang_angle = region_config.infill_overhang_angle;
params.anisotropic_surfaces = region_config.anisotropic_surfaces;
params.center_of_surface_pattern = region_config.center_of_surface_pattern;
params.separated_infills = region_config.separated_infills;
if (params.pattern == ipLockedZag) {
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
params.skin_infill_depth = scale_(region_config.skin_infill_depth);
@@ -1309,6 +1323,22 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.config = &region_config;
params.pattern = surface_fill.params.pattern;
// Orca: Checking the filling of a centered surface by drawing for each model parts
bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral;
if (is_top_or_bottom) {
params.is_anisotropic = surface_fill.params.anisotropic_surfaces; // Orca: anisotropic surfaces
params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern
}
// Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly
// fall through to the default (whole-object) bounding box below.
bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill;
bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills &&
(
is_centered_infill ||
params.config->solid_infill_rotate_template != "" ||
params.config->sparse_infill_rotate_template != "" );
if( surface_fill.params.pattern == ipLockedZag ) {
params.locked_zag = true;
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
@@ -1332,7 +1362,65 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.can_reverse = false;
for (ExPolygon& expoly : surface_fill.expolygons) {
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
// Orca: separate infill / per-model pattern centering.
//
// First assign this fill region to the model part whose slice at this layer overlaps it
// the most. A strict "contains" test is ambiguous for assemblies whose parts overlap (a
// region may sit inside several parts, or straddle a boundary and be inside none), so we
// pick by intersection area instead.
//
// The center must belong to an *overlap group*, not a single part: parts that
// touch/overlap form one connected physical body that shares a single center, while a
// part detached from the rest of the assembly gets its own. This holds for both
// separated infills and Each_Model surface centering (Each_Model == per connected body).
// firstLayerObjGroups() already holds these connected components, so we widen the chosen
// part's bbox to the whole group it belongs to.
if (is_per_model_center || is_separate_infill) {
double best_overlap = 0.;
ObjectID best_vol_id;
const PrintInstance* best_instance = nullptr;
for (const auto& instance : this->object()->instances()) {
for (const auto& volume : instance.print_object->firstLayerObjSlice()) {
if (f->layer_id >= volume.slices.size())
continue;
const double overlap = area(intersection_ex(volume.slices[f->layer_id], ExPolygons{expoly}));
if (overlap > best_overlap) {
best_overlap = overlap;
best_vol_id = volume.volume_id;
best_instance = &instance;
}
}
}
if (best_instance) {
const Transform3d matrix = best_instance->model_instance->get_matrix();
Point shift = best_instance->shift; // get_volume_bbox takes a non-const ref
auto& volumes = best_instance->model_instance->get_object()->volumes;
// Volume ids to center on: the whole overlap group the winning part belongs to,
// falling back to just that part if it isn't part of any group.
std::vector<ObjectID> center_ids;
for (const auto& group : best_instance->print_object->firstLayerObjGroups()) {
bool in_group = false;
for (const ObjectID& vid : group.volume_ids)
if (vid == best_vol_id) { in_group = true; break; }
if (in_group) { center_ids = group.volume_ids; break; }
}
if (center_ids.empty())
center_ids.push_back(best_vol_id);
BoundingBox bbox;
for (const ObjectID& vid : center_ids)
for (auto model_volume : volumes)
if (vid.id == model_volume->id().id) {
bbox.merge(model_volume->get_volume_bbox(matrix, shift, true));
break;
}
if (bbox.defined)
f->set_bounding_box(bbox);
}
} // - End: separate infill / per-model pattern centering
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
if (params.symmetric_infill_y_axis) {
params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
expoly.symmetric_y(params.symmetric_y_axis);