mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 13:32:44 +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.
|
// For Gyroid: when true, use the parameterized "optimized" wave.
|
||||||
bool gyroid_optimized = false;
|
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 {
|
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(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;
|
#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(lateral_lattice_angle_2);
|
||||||
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
|
RETURN_COMPARE_NON_EQUAL(symmetric_infill_y_axis);
|
||||||
RETURN_COMPARE_NON_EQUAL(infill_lock_depth);
|
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(gyroid_optimized);
|
||||||
|
RETURN_COMPARE_NON_EQUAL(anisotropic_surfaces);
|
||||||
|
RETURN_COMPARE_NON_EQUAL(center_of_surface_pattern);
|
||||||
|
RETURN_COMPARE_NON_EQUAL(separated_infills);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -329,6 +337,9 @@ struct SurfaceFillParams
|
|||||||
this->infill_lock_depth == rhs.infill_lock_depth &&
|
this->infill_lock_depth == rhs.infill_lock_depth &&
|
||||||
this->skin_infill_depth == rhs.skin_infill_depth &&
|
this->skin_infill_depth == rhs.skin_infill_depth &&
|
||||||
this->infill_overhang_angle == rhs.infill_overhang_angle &&
|
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;
|
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_1 = region_config.lateral_lattice_angle_1;
|
||||||
params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2;
|
params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2;
|
||||||
params.infill_overhang_angle = region_config.infill_overhang_angle;
|
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) {
|
if (params.pattern == ipLockedZag) {
|
||||||
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
|
params.infill_lock_depth = scale_(region_config.infill_lock_depth);
|
||||||
params.skin_infill_depth = scale_(region_config.skin_infill_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.config = ®ion_config;
|
||||||
params.pattern = surface_fill.params.pattern;
|
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 ) {
|
if( surface_fill.params.pattern == ipLockedZag ) {
|
||||||
params.locked_zag = true;
|
params.locked_zag = true;
|
||||||
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
|
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
|
||||||
@@ -1332,6 +1362,64 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
|||||||
params.can_reverse = false;
|
params.can_reverse = false;
|
||||||
for (ExPolygon& expoly : surface_fill.expolygons) {
|
for (ExPolygon& expoly : surface_fill.expolygons) {
|
||||||
|
|
||||||
|
// 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);
|
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||||
if (params.symmetric_infill_y_axis) {
|
if (params.symmetric_infill_y_axis) {
|
||||||
params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
|
params.symmetric_y_axis = f->extended_object_bounding_box().center().x();
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
|||||||
// ORCA: special flag for flow rate calibration
|
// 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") &&
|
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();
|
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;
|
eec->no_sort = true;
|
||||||
}
|
}
|
||||||
size_t idx = eec->entities.size();
|
size_t idx = eec->entities.size();
|
||||||
@@ -186,6 +186,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Orca: run gap fill
|
// Orca: run gap fill
|
||||||
|
if (!(params.is_anisotropic)) // Orca: Disable gap filling while anisotropic
|
||||||
this->_create_gap_fill(surface, params, eec);
|
this->_create_gap_fill(surface, params, eec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,8 @@ struct FillParams
|
|||||||
bool locked_zag{false};
|
bool locked_zag{false};
|
||||||
float infill_lock_depth{0.0};
|
float infill_lock_depth{0.0};
|
||||||
float skin_infill_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).");
|
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.
|
//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.
|
// 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);
|
BoundingBox snug_bounding_box = get_extents(expolygon).inflated(SCALED_EPSILON);
|
||||||
|
|
||||||
// Expand the bounding box to avoid artifacts at the edges
|
// 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.
|
// Sparse infill (or Internal where align == true) needs to be aligned across layers. Align infill across layers using the object's bounding box.
|
||||||
BoundingBox bounding_box = align ?
|
// Solid infill does not need to be aligned across layers, generate the infill pattern around the clipping expolygon only.
|
||||||
// Sparse infill needs to be aligned across layers. Align infill across layers using the object's bounding box.
|
if (align)
|
||||||
this->bounding_box.rotated(-direction.first) :
|
bounding_box = this->bounding_box.rotated(-direction.first);
|
||||||
// Solid infill does not need to be aligned across layers, generate the infill pattern
|
else if (params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Surface)
|
||||||
// around the clipping expolygon only.
|
bounding_box = snug_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() ?
|
Point shift = this->centered() ?
|
||||||
bounding_box.center() :
|
bounding_box.center() :
|
||||||
@@ -129,7 +133,8 @@ void FillPlanePath::_fill_surface_single(
|
|||||||
polylines = intersection_pl(std::move(polylines), expolygon);
|
polylines = intersection_pl(std::move(polylines), expolygon);
|
||||||
if (!polylines.empty()) {
|
if (!polylines.empty()) {
|
||||||
Polylines chained;
|
Polylines chained;
|
||||||
if (params.dont_connect() || params.density > 0.5) {
|
if (!params.is_anisotropic) { // Orca: not anisotropic surface
|
||||||
|
if ((params.dont_connect() || params.density > 0.5)) {
|
||||||
// ORCA: special flag for flow rate calibration
|
// ORCA: special flag for flow rate calibration
|
||||||
auto is_flow_calib = params.extrusion_role == erTopSolidInfill &&
|
auto is_flow_calib = params.extrusion_role == erTopSolidInfill &&
|
||||||
this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
this->print_object_config->has("calib_flowrate_topinfill_special_order") &&
|
||||||
@@ -143,21 +148,34 @@ void FillPlanePath::_fill_surface_single(
|
|||||||
Polyline center_spiral = std::move(*it);
|
Polyline center_spiral = std::move(*it);
|
||||||
|
|
||||||
// Ensure the spiral is printed from inside to out
|
// Ensure the spiral is printed from inside to out
|
||||||
if (center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm()) {
|
if ((center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm())) {
|
||||||
center_spiral.reverse();
|
center_spiral.reverse();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chain the other polylines
|
// Chain the other polylines
|
||||||
polylines.erase(it);
|
polylines.erase(it);
|
||||||
chained = chain_polylines(std::move(polylines));
|
chained = chain_polylines(std::move(polylines), nullptr);
|
||||||
|
|
||||||
// Then add the center spiral back
|
// Then add the center spiral back
|
||||||
chained.push_back(std::move(center_spiral));
|
chained.push_back(std::move(center_spiral));
|
||||||
} else {
|
} else {
|
||||||
chained = chain_polylines(std::move(polylines));
|
chained = chain_polylines(std::move(polylines), nullptr);
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
connect_infill(std::move(polylines), expolygon, chained, this->spacing, params);
|
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));
|
||||||
|
}
|
||||||
|
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
|
// paths must be repositioned and rotated back
|
||||||
for (Polyline& pl : chained) {
|
for (Polyline& pl : chained) {
|
||||||
pl.translate(shift.x(), shift.y());
|
pl.translate(shift.x(), shift.y());
|
||||||
|
|||||||
@@ -2708,6 +2708,23 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
|
|||||||
return *m_convex_hull.get();
|
return *m_convex_hull.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Orca: get volume bbox for separate infill
|
||||||
|
static std::mutex mtx_model;
|
||||||
|
BoundingBox ModelVolume::get_volume_bbox(const Transform3d &matrix, Point &shift, bool apply_cache = false) {
|
||||||
|
std::unique_lock l(mtx_model); // locks function here
|
||||||
|
// Orca: the cache is keyed by the instance transform/shift; a ModelVolume is shared
|
||||||
|
// across instances, so returning the cache blindly would hand back another instance's bbox.
|
||||||
|
if (m_cached_volume_bbox.defined && apply_cache
|
||||||
|
&& matrix.isApprox(m_cached_volume_bbox_matrix)
|
||||||
|
&& shift == m_cached_volume_bbox_shift)
|
||||||
|
return m_cached_volume_bbox;
|
||||||
|
auto hull = get_convex_hull_2d(matrix);
|
||||||
|
hull.translate(-shift);
|
||||||
|
m_cached_volume_bbox_matrix = matrix;
|
||||||
|
m_cached_volume_bbox_shift = shift;
|
||||||
|
return m_cached_volume_bbox = hull.bounding_box().polygon().bounding_box();
|
||||||
|
}
|
||||||
|
|
||||||
//BBS: refine the model part names
|
//BBS: refine the model part names
|
||||||
ModelVolumeType ModelVolume::type_from_string(const std::string &s)
|
ModelVolumeType ModelVolume::type_from_string(const std::string &s)
|
||||||
{
|
{
|
||||||
|
|||||||
+24
-10
@@ -920,6 +920,14 @@ public:
|
|||||||
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
|
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
|
||||||
int extruder_id() const;
|
int extruder_id() const;
|
||||||
|
|
||||||
|
//Orca: cache clearing procedure to ensure that the shape is positioned accurately when manipulating it
|
||||||
|
void clear_cache() {
|
||||||
|
m_cached_trans_matrix = Transform3d::Identity().inverse(); // get unvelivable matrix
|
||||||
|
m_cached_volume_bbox.reset();
|
||||||
|
m_convex_hull_2d.clear();
|
||||||
|
m_cached_2d_polygon.clear();
|
||||||
|
};
|
||||||
|
|
||||||
bool is_splittable() const;
|
bool is_splittable() const;
|
||||||
|
|
||||||
// BBS
|
// BBS
|
||||||
@@ -961,39 +969,42 @@ public:
|
|||||||
// Get count of errors in the mesh
|
// Get count of errors in the mesh
|
||||||
int get_repaired_errors_count() const;
|
int get_repaired_errors_count() const;
|
||||||
|
|
||||||
|
BoundingBox get_volume_bbox(const Transform3d &matrix, Point &shift, bool apply_cache);
|
||||||
|
void reset_volume_bbox() { m_cached_volume_bbox.reset(); };
|
||||||
|
|
||||||
// Helpers for loading / storing into AMF / 3MF files.
|
// Helpers for loading / storing into AMF / 3MF files.
|
||||||
static ModelVolumeType type_from_string(const std::string &s);
|
static ModelVolumeType type_from_string(const std::string &s);
|
||||||
static std::string type_to_string(const ModelVolumeType t);
|
static std::string type_to_string(const ModelVolumeType t);
|
||||||
|
|
||||||
const Geometry::Transformation& get_transformation() const { return m_transformation; }
|
const Geometry::Transformation& get_transformation() const { return m_transformation; }
|
||||||
void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; }
|
void set_transformation(const Geometry::Transformation& transformation) { clear_cache(); m_transformation = transformation; }
|
||||||
void set_transformation(const Transform3d& trafo) { m_transformation.set_matrix(trafo); }
|
void set_transformation(const Transform3d& trafo) { clear_cache(); m_transformation.set_matrix(trafo); }
|
||||||
|
|
||||||
Vec3d get_offset() const { return m_transformation.get_offset(); }
|
Vec3d get_offset() const { return m_transformation.get_offset(); }
|
||||||
|
|
||||||
double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
|
double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
|
||||||
|
|
||||||
void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); }
|
void set_offset(const Vec3d& offset) { clear_cache(); m_transformation.set_offset(offset); }
|
||||||
void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); }
|
void set_offset(Axis axis, double offset) { clear_cache(); m_transformation.set_offset(axis, offset); }
|
||||||
|
|
||||||
Vec3d get_rotation() const { return m_transformation.get_rotation(); }
|
Vec3d get_rotation() const { return m_transformation.get_rotation(); }
|
||||||
double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
|
double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
|
||||||
|
|
||||||
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
|
void set_rotation(const Vec3d& rotation) { clear_cache(); m_transformation.set_rotation(rotation); }
|
||||||
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
|
void set_rotation(Axis axis, double rotation) { clear_cache(); m_transformation.set_rotation(axis, rotation); }
|
||||||
|
|
||||||
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||||
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
|
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
|
||||||
|
|
||||||
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
|
void set_scaling_factor(const Vec3d& scaling_factor) { clear_cache(); m_transformation.set_scaling_factor(scaling_factor); }
|
||||||
void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); }
|
void set_scaling_factor(Axis axis, double scaling_factor) {clear_cache(); m_transformation.set_scaling_factor(axis, scaling_factor); }
|
||||||
|
|
||||||
Vec3d get_mirror() const { return m_transformation.get_mirror(); }
|
Vec3d get_mirror() const { return m_transformation.get_mirror(); }
|
||||||
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
|
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
|
||||||
bool is_left_handed() const { return m_transformation.is_left_handed(); }
|
bool is_left_handed() const { return m_transformation.is_left_handed(); }
|
||||||
|
|
||||||
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); }
|
void set_mirror(const Vec3d& mirror) { clear_cache(); m_transformation.set_mirror(mirror); }
|
||||||
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); }
|
void set_mirror(Axis axis, double mirror) { clear_cache(); m_transformation.set_mirror(axis, mirror); }
|
||||||
void convert_from_imperial_units();
|
void convert_from_imperial_units();
|
||||||
void convert_from_meters();
|
void convert_from_meters();
|
||||||
|
|
||||||
@@ -1048,6 +1059,9 @@ private:
|
|||||||
mutable Transform3d m_cached_trans_matrix; //BBS, used for convex_hell_2d acceleration
|
mutable Transform3d m_cached_trans_matrix; //BBS, used for convex_hell_2d acceleration
|
||||||
mutable Polygon m_cached_2d_polygon; //BBS, used for convex_hell_2d acceleration
|
mutable Polygon m_cached_2d_polygon; //BBS, used for convex_hell_2d acceleration
|
||||||
Geometry::Transformation m_transformation;
|
Geometry::Transformation m_transformation;
|
||||||
|
mutable BoundingBox m_cached_volume_bbox; //Orca: used for separated infills
|
||||||
|
mutable Transform3d m_cached_volume_bbox_matrix{Transform3d::Identity()}; //Orca: cache key for m_cached_volume_bbox
|
||||||
|
mutable Point m_cached_volume_bbox_shift{Point(0, 0)}; //Orca: cache key for m_cached_volume_bbox
|
||||||
|
|
||||||
//BBS: add convex_hell_2d related logic
|
//BBS: add convex_hell_2d related logic
|
||||||
void calculate_convex_hull_2d(const Geometry::Transformation &transformation) const;
|
void calculate_convex_hull_2d(const Geometry::Transformation &transformation) const;
|
||||||
|
|||||||
@@ -1063,6 +1063,9 @@ static std::vector<std::string> s_Preset_print_options{
|
|||||||
"skin_infill_density",
|
"skin_infill_density",
|
||||||
"align_infill_direction_to_model",
|
"align_infill_direction_to_model",
|
||||||
"extra_solid_infills",
|
"extra_solid_infills",
|
||||||
|
"anisotropic_surfaces",
|
||||||
|
"center_of_surface_pattern",
|
||||||
|
"separated_infills",
|
||||||
"minimum_sparse_infill_area",
|
"minimum_sparse_infill_area",
|
||||||
"reduce_infill_retraction",
|
"reduce_infill_retraction",
|
||||||
"internal_solid_infill_pattern",
|
"internal_solid_infill_pattern",
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
"chamber_minimal_temperature",
|
"chamber_minimal_temperature",
|
||||||
"thumbnails",
|
"thumbnails",
|
||||||
"thumbnails_format",
|
"thumbnails_format",
|
||||||
|
"anisotropic_surfaces", "center_of_surface_pattern", "separated_infills",
|
||||||
"seam_gap",
|
"seam_gap",
|
||||||
"role_based_wipe_speed",
|
"role_based_wipe_speed",
|
||||||
"wipe_speed",
|
"wipe_speed",
|
||||||
|
|||||||
@@ -188,6 +188,12 @@ static t_config_enum_values s_keys_map_PowerLossRecoveryMode {
|
|||||||
};
|
};
|
||||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PowerLossRecoveryMode)
|
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PowerLossRecoveryMode)
|
||||||
|
|
||||||
|
static t_config_enum_values s_keys_map_CenterOfSurfacePattern{
|
||||||
|
{"each_surface", int(CenterOfSurfacePattern::Each_Surface)},
|
||||||
|
{"each_model", int(CenterOfSurfacePattern::Each_Model)},
|
||||||
|
{"each_assembly", int(CenterOfSurfacePattern::Each_Assembly)}};
|
||||||
|
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(CenterOfSurfacePattern)
|
||||||
|
|
||||||
static t_config_enum_values s_keys_map_FuzzySkinType {
|
static t_config_enum_values s_keys_map_FuzzySkinType {
|
||||||
{ "none", int(FuzzySkinType::None) },
|
{ "none", int(FuzzySkinType::None) },
|
||||||
{ "external", int(FuzzySkinType::External) },
|
{ "external", int(FuzzySkinType::External) },
|
||||||
@@ -6869,6 +6875,48 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->set_default_value(new ConfigOptionFloat(0.6));
|
def->set_default_value(new ConfigOptionFloat(0.6));
|
||||||
|
|
||||||
|
def = this->add("anisotropic_surfaces", coBool);
|
||||||
|
def->label = L("Anisotropic surfaces");
|
||||||
|
def->category = L("Strength");
|
||||||
|
def->tooltip = L("Anisotropic patterns on the top and bottom surfaces.\n"
|
||||||
|
"Co-directional printing mode will be applied. For certain patterns, omni-directional filling provides color "
|
||||||
|
"dispersion when using multi-colored or silk plastics.\n"
|
||||||
|
"This option disable the gap fill.\n"
|
||||||
|
"This option can increase a printing time.");
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("separated_infills", coBool);
|
||||||
|
def->label = L("Separated infills");
|
||||||
|
def->category = L("Strength");
|
||||||
|
def->tooltip = L("Aligns the internal infill pattern of each part independently instead of across the whole object or assembly.\n"
|
||||||
|
"By default, aligned infill patterns share a single origin for the entire object, so the pattern of every "
|
||||||
|
"part is referenced to the same point. When enabled, each connected body is aligned on its own: parts that "
|
||||||
|
"touch or overlap are treated as one body and share an origin, while parts detached from the rest each get "
|
||||||
|
"their own.\n Useful when an assembly groups several distinct objects that should each keep a self-centered infill.\n"
|
||||||
|
"Only affects centered infill patterns (Archimedean Chords, Octagram Spiral) and patterns driven by an "
|
||||||
|
"infill rotation template.");
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("center_of_surface_pattern", coEnum);
|
||||||
|
def->label = L("Center surface pattern on");
|
||||||
|
def->category = L("Strength");
|
||||||
|
def->tooltip = L("Chooses where the centering point of centered top/bottom surface patterns (Archimedean Chords, "
|
||||||
|
"Octagram Spiral) is placed.\n"
|
||||||
|
" - Each Surface: centers the pattern on every individual surface region, so each island is symmetric on its own.\n"
|
||||||
|
" - Each Model: centers the pattern on each connected body. Parts that touch or overlap share one center; "
|
||||||
|
"parts detached from the rest each get their own.\n"
|
||||||
|
" - Each Assembly: uses a single shared center for the whole object or assembly.");
|
||||||
|
def->enum_keys_map = &ConfigOptionEnum<CenterOfSurfacePattern>::get_enum_values();
|
||||||
|
def->enum_values.push_back("each_surface");
|
||||||
|
def->enum_values.push_back("each_model");
|
||||||
|
def->enum_values.push_back("each_assembly");
|
||||||
|
def->enum_labels.push_back(L("Each Surface"));
|
||||||
|
def->enum_labels.push_back(L("Each Model"));
|
||||||
|
def->enum_labels.push_back(L("Each Assembly"));
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionEnum<CenterOfSurfacePattern>(CenterOfSurfacePattern::Each_Surface));
|
||||||
|
|
||||||
def = this->add("travel_speed", coFloats);
|
def = this->add("travel_speed", coFloats);
|
||||||
def->label = L("Travel");
|
def->label = L("Travel");
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ enum class TopSurfaceExpansionDirection {
|
|||||||
Outward,
|
Outward,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class CenterOfSurfacePattern {
|
||||||
|
Each_Surface,
|
||||||
|
Each_Model,
|
||||||
|
Each_Assembly,
|
||||||
|
};
|
||||||
|
|
||||||
enum class NoiseType {
|
enum class NoiseType {
|
||||||
Classic,
|
Classic,
|
||||||
Perlin,
|
Perlin,
|
||||||
@@ -1137,6 +1143,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionFloat, lightning_prune_angle))
|
((ConfigOptionFloat, lightning_prune_angle))
|
||||||
((ConfigOptionFloat, lightning_straightening_angle))
|
((ConfigOptionFloat, lightning_straightening_angle))
|
||||||
((ConfigOptionBool, align_infill_direction_to_model))
|
((ConfigOptionBool, align_infill_direction_to_model))
|
||||||
|
((ConfigOptionBool, anisotropic_surfaces))
|
||||||
|
((ConfigOptionEnum<CenterOfSurfacePattern>, center_of_surface_pattern))
|
||||||
|
((ConfigOptionBool, separated_infills))
|
||||||
((ConfigOptionString, extra_solid_infills))
|
((ConfigOptionString, extra_solid_infills))
|
||||||
((ConfigOptionEnum<FuzzySkinType>, fuzzy_skin))
|
((ConfigOptionEnum<FuzzySkinType>, fuzzy_skin))
|
||||||
((ConfigOptionFloat, fuzzy_skin_thickness))
|
((ConfigOptionFloat, fuzzy_skin_thickness))
|
||||||
|
|||||||
@@ -563,6 +563,11 @@ void PrintObject::prepare_infill()
|
|||||||
{
|
{
|
||||||
if (! this->set_started(posPrepareInfill))
|
if (! this->set_started(posPrepareInfill))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Orca: clear all volume bbox caches
|
||||||
|
for (auto volume : this->model_object()->volumes)
|
||||||
|
volume->reset_volume_bbox();
|
||||||
|
|
||||||
m_print->set_status(25, L("Generating infill regions"));
|
m_print->set_status(25, L("Generating infill regions"));
|
||||||
if (m_typed_slices) {
|
if (m_typed_slices) {
|
||||||
// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
|
// To improve robustness of detect_surfaces_type() when reslicing (working with typed slices), see GH issue #7442.
|
||||||
@@ -1334,6 +1339,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||||||
|| opt_key == "top_surface_line_width"
|
|| opt_key == "top_surface_line_width"
|
||||||
|| opt_key == "top_surface_density"
|
|| opt_key == "top_surface_density"
|
||||||
|| opt_key == "bottom_surface_density"
|
|| opt_key == "bottom_surface_density"
|
||||||
|
|| opt_key == "anisotropic_surfaces"
|
||||||
|
|| opt_key == "center_of_surface_pattern"
|
||||||
|
|| opt_key == "separated_infills"
|
||||||
|| opt_key == "initial_layer_line_width"
|
|| opt_key == "initial_layer_line_width"
|
||||||
|| opt_key == "small_area_infill_flow_compensation"
|
|| opt_key == "small_area_infill_flow_compensation"
|
||||||
|| opt_key == "lateral_lattice_angle_1"
|
|| opt_key == "lateral_lattice_angle_1"
|
||||||
|
|||||||
@@ -720,6 +720,29 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
|||||||
toggle_line("top_surface_expansion_direction", has_top_shell);
|
toggle_line("top_surface_expansion_direction", has_top_shell);
|
||||||
toggle_field("top_surface_expansion_direction", has_top_surface_expansion);
|
toggle_field("top_surface_expansion_direction", has_top_surface_expansion);
|
||||||
|
|
||||||
|
// Orca: Archimedean Chords and Octagram Spiral are the centered surface patterns that the
|
||||||
|
// pattern-centering, anisotropic-surface and separated-infill features act on.
|
||||||
|
auto is_centered_pattern = [](InfillPattern p) {
|
||||||
|
return p == InfillPattern::ipArchimedeanChords || p == InfillPattern::ipOctagramSpiral;
|
||||||
|
};
|
||||||
|
bool is_top_centered = is_centered_pattern(config->option<ConfigOptionEnum<InfillPattern>>("top_surface_pattern")->value);
|
||||||
|
bool is_bottom_centered = is_centered_pattern(config->option<ConfigOptionEnum<InfillPattern>>("bottom_surface_pattern")->value);
|
||||||
|
bool has_centered_surface = (has_top_shell && is_top_centered) || (has_bottom_shell && is_bottom_centered);
|
||||||
|
|
||||||
|
// Orca: center of surface pattern / anisotropic surfaces
|
||||||
|
toggle_line("center_of_surface_pattern", has_centered_surface);
|
||||||
|
toggle_line("anisotropic_surfaces", has_centered_surface);
|
||||||
|
|
||||||
|
// Orca: separate infills
|
||||||
|
bool is_internal_infill_centered = is_centered_pattern(config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value) ||
|
||||||
|
config->opt_string("sparse_infill_rotate_template") != "" ||
|
||||||
|
config->opt_string("solid_infill_rotate_template") != "";
|
||||||
|
toggle_line("separated_infills", is_internal_infill_centered);
|
||||||
|
|
||||||
|
// Orca: no need gaps
|
||||||
|
for (auto el : {"gap_fill_target", "filter_out_gap_fill"})
|
||||||
|
toggle_field(el, !config->opt_bool("anisotropic_surfaces"));
|
||||||
|
|
||||||
for (auto el : { "infill_direction", "sparse_infill_line_width", "gap_fill_target","filter_out_gap_fill","infill_wall_overlap",
|
for (auto el : { "infill_direction", "sparse_infill_line_width", "gap_fill_target","filter_out_gap_fill","infill_wall_overlap",
|
||||||
"bridge_angle", "internal_bridge_angle", "relative_bridge_angle",
|
"bridge_angle", "internal_bridge_angle", "relative_bridge_angle",
|
||||||
"solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "internal_solid_filament_id", "top_surface_filament_id", "bottom_surface_filament_id",
|
"solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "internal_solid_filament_id", "top_surface_filament_id", "bottom_surface_filament_id",
|
||||||
|
|||||||
@@ -13113,6 +13113,9 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i
|
|||||||
_obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f));
|
_obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f));
|
||||||
_obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45));
|
_obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45));
|
||||||
_obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135));
|
_obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135));
|
||||||
|
_obj->config.set_key_value("anisotropic_surfaces", new ConfigOptionBool(false));
|
||||||
|
_obj->config.set_key_value("center_of_surface_pattern", new ConfigOptionEnum<CenterOfSurfacePattern>(CenterOfSurfacePattern::Each_Surface));
|
||||||
|
_obj->config.set_key_value("separated_infills", new ConfigOptionBool(false));
|
||||||
_obj->config.set_key_value("align_infill_direction_to_model", new ConfigOptionBool(true));
|
_obj->config.set_key_value("align_infill_direction_to_model", new ConfigOptionBool(true));
|
||||||
_obj->config.set_key_value("ironing_type", new ConfigOptionEnum<IroningType>(IroningType::NoIroning));
|
_obj->config.set_key_value("ironing_type", new ConfigOptionEnum<IroningType>(IroningType::NoIroning));
|
||||||
_obj->config.set_key_value("internal_solid_infill_speed", new ConfigOptionFloatsNullable(internal_solid_speeds));
|
_obj->config.set_key_value("internal_solid_infill_speed", new ConfigOptionFloatsNullable(internal_solid_speeds));
|
||||||
|
|||||||
@@ -2751,6 +2751,8 @@ void TabPrint::build()
|
|||||||
optgroup->append_single_option_line("bottom_surface_density", "strength_settings_top_bottom_shells#surface-density");
|
optgroup->append_single_option_line("bottom_surface_density", "strength_settings_top_bottom_shells#surface-density");
|
||||||
optgroup->append_single_option_line("bottom_surface_pattern", "strength_settings_top_bottom_shells#surface-pattern");
|
optgroup->append_single_option_line("bottom_surface_pattern", "strength_settings_top_bottom_shells#surface-pattern");
|
||||||
optgroup->append_single_option_line("bottom_layer_direction", "strength_settings_infill#direction");
|
optgroup->append_single_option_line("bottom_layer_direction", "strength_settings_infill#direction");
|
||||||
|
optgroup->append_single_option_line("center_of_surface_pattern", "strength_settings_top_bottom_shells#center-surface-pattern-on");
|
||||||
|
optgroup->append_single_option_line("anisotropic_surfaces", "strength_settings_top_bottom_shells#anisotropic-surfaces");
|
||||||
optgroup->append_single_option_line("top_bottom_infill_wall_overlap", "strength_settings_top_bottom_shells#infillwall-overlap");
|
optgroup->append_single_option_line("top_bottom_infill_wall_overlap", "strength_settings_top_bottom_shells#infillwall-overlap");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Infill"), L"param_infill");
|
optgroup = page->new_optgroup(L("Infill"), L"param_infill");
|
||||||
@@ -2781,6 +2783,7 @@ void TabPrint::build()
|
|||||||
optgroup->append_single_option_line("solid_infill_rotate_template", "strength_settings_infill_rotation_template_metalanguage");
|
optgroup->append_single_option_line("solid_infill_rotate_template", "strength_settings_infill_rotation_template_metalanguage");
|
||||||
optgroup->append_single_option_line("gap_fill_target", "strength_settings_infill#apply-gap-fill");
|
optgroup->append_single_option_line("gap_fill_target", "strength_settings_infill#apply-gap-fill");
|
||||||
optgroup->append_single_option_line("filter_out_gap_fill", "strength_settings_infill#filter-out-tiny-gaps");
|
optgroup->append_single_option_line("filter_out_gap_fill", "strength_settings_infill#filter-out-tiny-gaps");
|
||||||
|
optgroup->append_single_option_line("separated_infills", "strength_settings_infill#separated-infills");
|
||||||
optgroup->append_single_option_line("infill_wall_overlap", "strength_settings_infill#infill-wall-overlap");
|
optgroup->append_single_option_line("infill_wall_overlap", "strength_settings_infill#infill-wall-overlap");
|
||||||
|
|
||||||
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
|
optgroup = page->new_optgroup(L("Advanced"), L"param_advanced");
|
||||||
|
|||||||
Reference in New Issue
Block a user