mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 19:01:02 +00:00
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:
co-authored by
Rodrigo Faselli
Ian Bassi
parent
378843a4da
commit
bc6ffcfb31
@@ -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 = ®ion_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);
|
||||
|
||||
@@ -165,7 +165,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
||||
// ORCA: special flag for flow rate calibration
|
||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill && this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||
this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool();
|
||||
if (is_flow_calib) {
|
||||
if (is_flow_calib || params.is_anisotropic) { // Orca: disable sorting while anisotropic surfaces
|
||||
eec->no_sort = true;
|
||||
}
|
||||
size_t idx = eec->entities.size();
|
||||
@@ -186,7 +186,8 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
||||
}
|
||||
|
||||
// Orca: run gap fill
|
||||
this->_create_gap_fill(surface, params, eec);
|
||||
if (!(params.is_anisotropic)) // Orca: Disable gap filling while anisotropic
|
||||
this->_create_gap_fill(surface, params, eec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,6 +106,8 @@ struct FillParams
|
||||
bool locked_zag{false};
|
||||
float infill_lock_depth{0.0};
|
||||
float skin_infill_depth{0.0};
|
||||
bool is_anisotropic{false};
|
||||
CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface};
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||
|
||||
|
||||
@@ -77,20 +77,24 @@ void FillPlanePath::_fill_surface_single(
|
||||
|
||||
//FIXME Vojtech: We are not sure whether the user expects the fill patterns on visible surfaces to be aligned across all the islands of a single layer.
|
||||
// One may align for this->centered() to align the patterns for Archimedean Chords and Octagram Spiral patterns.
|
||||
const bool align = params.density < 0.995;
|
||||
|
||||
// Orca: the old implementation became obsolete when it became possible to change the density of the top and bottom surfaces
|
||||
bool align = params.extrusion_role == ExtrusionRole::erInternalInfill;
|
||||
BoundingBox bounding_box;
|
||||
BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON);
|
||||
|
||||
// Expand the bounding box to avoid artifacts at the edges
|
||||
snug_bounding_box.offset(scale_(this->spacing)*params.multiline);
|
||||
snug_bounding_box.offset(scale_(this->spacing)*params.multiline);
|
||||
|
||||
// Rotated bounding box of the area to fill in with the pattern.
|
||||
BoundingBox bounding_box = align ?
|
||||
// Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
|
||||
this->bounding_box.rotated(-direction.first) :
|
||||
// Solid infill does not need to be aligned across layers, generate the infill pattern
|
||||
// around the clipping expolygon only.
|
||||
snug_bounding_box;
|
||||
// Sparse infill (or Internal where align == true) needs to be aligned across layers. Align infill across layers using the object's bounding box.
|
||||
// Solid infill does not need to be aligned across layers, generate the infill pattern around the clipping expolygon only.
|
||||
if (align)
|
||||
bounding_box = this->bounding_box.rotated(-direction.first);
|
||||
else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Surface)
|
||||
bounding_box = snug_bounding_box;
|
||||
else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model)
|
||||
bounding_box = this->bounding_box.rotated(-direction.first);
|
||||
else
|
||||
bounding_box = extended_object_bounding_box();
|
||||
|
||||
Point shift = this->centered() ?
|
||||
bounding_box.center() :
|
||||
@@ -129,35 +133,49 @@ void FillPlanePath::_fill_surface_single(
|
||||
polylines = intersection_pl(std::move(polylines), expolygon);
|
||||
if (!polylines.empty()) {
|
||||
Polylines chained;
|
||||
if (params.dont_connect() || params.density > 0.5) {
|
||||
// ORCA: special flag for flow rate calibration
|
||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill &&
|
||||
this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||
this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() &&
|
||||
dynamic_cast<FillArchimedeanChords*>(this);
|
||||
if (is_flow_calib) {
|
||||
// We want the spiral part to be printed inside-out
|
||||
// Find the center spiral line first, by looking for the longest one
|
||||
auto it = std::max_element(polylines.begin(), polylines.end(),
|
||||
[](const Polyline& a, const Polyline& b) { return a.length() < b.length(); });
|
||||
Polyline center_spiral = std::move(*it);
|
||||
if (!params.is_anisotropic) { // Orca: not anisotropic surface
|
||||
if ((params.dont_connect() || params.density > 0.5)) {
|
||||
// ORCA: special flag for flow rate calibration
|
||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill &&
|
||||
this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||
this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() &&
|
||||
dynamic_cast<FillArchimedeanChords*>(this);
|
||||
if (is_flow_calib) {
|
||||
// We want the spiral part to be printed inside-out
|
||||
// Find the center spiral line first, by looking for the longest one
|
||||
auto it = std::max_element(polylines.begin(), polylines.end(),
|
||||
[](const Polyline& a, const Polyline& b) { return a.length() < b.length(); });
|
||||
Polyline center_spiral = std::move(*it);
|
||||
|
||||
// Ensure the spiral is printed from inside to out
|
||||
if (center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm()) {
|
||||
center_spiral.reverse();
|
||||
// Ensure the spiral is printed from inside to out
|
||||
if ((center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm())) {
|
||||
center_spiral.reverse();
|
||||
}
|
||||
|
||||
// Chain the other polylines
|
||||
polylines.erase(it);
|
||||
chained = chain_polylines(std::move(polylines), nullptr);
|
||||
|
||||
// Then add the center spiral back
|
||||
chained.push_back(std::move(center_spiral));
|
||||
} else {
|
||||
chained = chain_polylines(std::move(polylines), nullptr);
|
||||
}
|
||||
|
||||
// Chain the other polylines
|
||||
polylines.erase(it);
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
|
||||
// Then add the center spiral back
|
||||
chained.push_back(std::move(center_spiral));
|
||||
} else {
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
} else
|
||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
||||
} else { // Orca: anisotropic surface
|
||||
const Point _center(0., 0.);
|
||||
for (Polyline& segment : polylines) { // sort paths by its direction
|
||||
if (segment.size() > 1) { // need at least two points to evaluate direction
|
||||
if (segment.first_point().ccw(segment.points[1], _center) < 0)
|
||||
segment.reverse();
|
||||
}
|
||||
chained.emplace_back(std::move(segment));
|
||||
}
|
||||
} else
|
||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
||||
std::sort(chained.begin(), chained.end(), [&_center](const Polyline& a, const Polyline& b) { // just sort polylines from center to outside
|
||||
return a.distance_to(_center) < b.distance_to(_center);
|
||||
});
|
||||
}
|
||||
// paths must be repositioned and rotated back
|
||||
for (Polyline& pl : chained) {
|
||||
pl.translate(shift.x(), shift.y());
|
||||
|
||||
Reference in New Issue
Block a user