mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 00:37:51 -06:00
Introduction of Monotonous infill type. Fill no-sort only for monotonous
and ironing infills.
This commit is contained in:
parent
e390ebc95c
commit
033548a568
10 changed files with 286 additions and 191 deletions
|
@ -373,7 +373,11 @@ void Layer::make_fills()
|
||||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||||
f->spacing = surface_fill.params.spacing;
|
f->spacing = surface_fill.params.spacing;
|
||||||
surface_fill.surface.expolygon = std::move(expoly);
|
surface_fill.surface.expolygon = std::move(expoly);
|
||||||
Polylines polylines = f->fill_surface(&surface_fill.surface, params);
|
Polylines polylines;
|
||||||
|
try {
|
||||||
|
polylines = f->fill_surface(&surface_fill.surface, params);
|
||||||
|
} catch (InfillFailedException &) {
|
||||||
|
}
|
||||||
if (! polylines.empty()) {
|
if (! polylines.empty()) {
|
||||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||||
// pattern generator)
|
// pattern generator)
|
||||||
|
@ -496,10 +500,11 @@ void Layer::make_ironing()
|
||||||
if (! layerm->slices.empty()) {
|
if (! layerm->slices.empty()) {
|
||||||
IroningParams ironing_params;
|
IroningParams ironing_params;
|
||||||
const PrintRegionConfig &config = layerm->region()->config();
|
const PrintRegionConfig &config = layerm->region()->config();
|
||||||
if (config.ironing_type == IroningType::AllSolid ||
|
if (config.ironing &&
|
||||||
(config.top_solid_layers > 0 &&
|
(config.ironing_type == IroningType::AllSolid ||
|
||||||
(config.ironing_type == IroningType::TopSurfaces ||
|
(config.top_solid_layers > 0 &&
|
||||||
(config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr)))) {
|
(config.ironing_type == IroningType::TopSurfaces ||
|
||||||
|
(config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr))))) {
|
||||||
if (config.perimeter_extruder == config.solid_infill_extruder || config.perimeters == 0) {
|
if (config.perimeter_extruder == config.solid_infill_extruder || config.perimeters == 0) {
|
||||||
// Iron the whole face.
|
// Iron the whole face.
|
||||||
ironing_params.extruder = config.solid_infill_extruder;
|
ironing_params.extruder = config.solid_infill_extruder;
|
||||||
|
@ -528,6 +533,7 @@ void Layer::make_ironing()
|
||||||
fill.overlap = 0;
|
fill.overlap = 0;
|
||||||
fill_params.density = 1.;
|
fill_params.density = 1.;
|
||||||
fill_params.dont_connect = true;
|
fill_params.dont_connect = true;
|
||||||
|
fill_params.monotonous = true;
|
||||||
|
|
||||||
for (size_t i = 0; i < by_extruder.size(); ++ i) {
|
for (size_t i = 0; i < by_extruder.size(); ++ i) {
|
||||||
// Find span of regions equivalent to the ironing operation.
|
// Find span of regions equivalent to the ironing operation.
|
||||||
|
@ -562,13 +568,17 @@ void Layer::make_ironing()
|
||||||
Surface surface_fill(stTop, ExPolygon());
|
Surface surface_fill(stTop, ExPolygon());
|
||||||
for (ExPolygon &expoly : ironing_areas) {
|
for (ExPolygon &expoly : ironing_areas) {
|
||||||
surface_fill.expolygon = std::move(expoly);
|
surface_fill.expolygon = std::move(expoly);
|
||||||
Polylines polylines = fill.fill_surface(&surface_fill, fill_params);
|
Polylines polylines;
|
||||||
|
try {
|
||||||
|
polylines = fill.fill_surface(&surface_fill, fill_params);
|
||||||
|
} catch (InfillFailedException &) {
|
||||||
|
}
|
||||||
if (! polylines.empty()) {
|
if (! polylines.empty()) {
|
||||||
// Save into layer.
|
// Save into layer.
|
||||||
ExtrusionEntityCollection *eec = nullptr;
|
ExtrusionEntityCollection *eec = nullptr;
|
||||||
ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection());
|
ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection());
|
||||||
//FIXME we may not want to sort a monotonous infill.
|
// Don't sort the ironing infill lines as they are monotonously ordered.
|
||||||
eec->no_sort = false;
|
eec->no_sort = true;
|
||||||
extrusion_entities_append_paths(
|
extrusion_entities_append_paths(
|
||||||
eec->entities, std::move(polylines),
|
eec->entities, std::move(polylines),
|
||||||
erIroning,
|
erIroning,
|
||||||
|
|
|
@ -27,7 +27,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
||||||
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
||||||
case ipGyroid: return new FillGyroid();
|
case ipGyroid: return new FillGyroid();
|
||||||
case ipRectilinear: return new FillRectilinear2();
|
case ipRectilinear: return new FillRectilinear2();
|
||||||
// case ipRectilinear: return new FillRectilinear();
|
case ipMonotonous: return new FillMonotonous();
|
||||||
case ipLine: return new FillLine();
|
case ipLine: return new FillLine();
|
||||||
case ipGrid: return new FillGrid2();
|
case ipGrid: return new FillGrid2();
|
||||||
case ipTriangles: return new FillTriangles();
|
case ipTriangles: return new FillTriangles();
|
||||||
|
|
|
@ -37,6 +37,9 @@ struct FillParams
|
||||||
// Don't adjust spacing to fill the space evenly.
|
// Don't adjust spacing to fill the space evenly.
|
||||||
bool dont_adjust { true };
|
bool dont_adjust { true };
|
||||||
|
|
||||||
|
// Monotonous infill - strictly left to right for better surface quality of top infills.
|
||||||
|
bool monotonous { false };
|
||||||
|
|
||||||
// For Honeycomb.
|
// For Honeycomb.
|
||||||
// we were requested to complete each loop;
|
// we were requested to complete each loop;
|
||||||
// in this case we don't try to make more continuous paths
|
// in this case we don't try to make more continuous paths
|
||||||
|
|
|
@ -951,110 +951,111 @@ static void connect_segment_intersections_by_contours(const ExPolygonWithOffset
|
||||||
const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr;
|
const SegmentedIntersectionLine *il_next = i_vline + 1 < segs.size() ? &segs[i_vline + 1] : nullptr;
|
||||||
|
|
||||||
for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
|
for (int i_intersection = 0; i_intersection < il.intersections.size(); ++ i_intersection) {
|
||||||
SegmentIntersection &itsct = il.intersections[i_intersection];
|
SegmentIntersection &itsct = il.intersections[i_intersection];
|
||||||
const Polygon &poly = poly_with_offset.contour(itsct.iContour);
|
const Polygon &poly = poly_with_offset.contour(itsct.iContour);
|
||||||
|
const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
|
||||||
|
|
||||||
// 1) Find possible connection points on the previous / next vertical line.
|
// 1) Find possible connection points on the previous / next vertical line.
|
||||||
// Find an intersection point on il_prev, intersecting i_intersection
|
// Find an intersection point on il_prev, intersecting i_intersection
|
||||||
// at the same orientation as i_intersection, and being closest to i_intersection
|
// at the same orientation as i_intersection, and being closest to i_intersection
|
||||||
// in the number of contour segments, when following the direction of the contour.
|
// in the number of contour segments, when following the direction of the contour.
|
||||||
//FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible.
|
//FIXME this has O(n) time complexity. Likely an O(log(n)) scheme is possible.
|
||||||
int iprev = -1;
|
int iprev = -1;
|
||||||
|
int d_prev = std::numeric_limits<int>::max();
|
||||||
if (il_prev) {
|
if (il_prev) {
|
||||||
int dmin = std::numeric_limits<int>::max();
|
|
||||||
for (int i = 0; i < il_prev->intersections.size(); ++ i) {
|
for (int i = 0; i < il_prev->intersections.size(); ++ i) {
|
||||||
const SegmentIntersection &itsct2 = il_prev->intersections[i];
|
const SegmentIntersection &itsct2 = il_prev->intersections[i];
|
||||||
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
|
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
|
||||||
// The intersection points lie on the same contour and have the same orientation.
|
// The intersection points lie on the same contour and have the same orientation.
|
||||||
// Find the intersection point with a shortest path in the direction of the contour.
|
// Find the intersection point with a shortest path in the direction of the contour.
|
||||||
int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, false);
|
int d = distance_of_segmens(poly, itsct2.iSegment, itsct.iSegment, forward);
|
||||||
if (d < dmin) {
|
if (d < d_prev) {
|
||||||
iprev = i;
|
iprev = i;
|
||||||
dmin = d;
|
d_prev = d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The same for il_next.
|
// The same for il_next.
|
||||||
int inext = -1;
|
int inext = -1;
|
||||||
if (il_next) {
|
int d_next = std::numeric_limits<int>::max();
|
||||||
int dmin = std::numeric_limits<int>::max();
|
if (il_next) {
|
||||||
for (int i = 0; i < il_next->intersections.size(); ++ i) {
|
for (int i = 0; i < il_next->intersections.size(); ++ i) {
|
||||||
const SegmentIntersection &itsct2 = il_next->intersections[i];
|
const SegmentIntersection &itsct2 = il_next->intersections[i];
|
||||||
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
|
if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) {
|
||||||
// The intersection points lie on the same contour and have the same orientation.
|
// The intersection points lie on the same contour and have the same orientation.
|
||||||
// Find the intersection point with a shortest path in the direction of the contour.
|
// Find the intersection point with a shortest path in the direction of the contour.
|
||||||
int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, true);
|
int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward);
|
||||||
if (d < dmin) {
|
if (d < d_next) {
|
||||||
inext = i;
|
inext = i;
|
||||||
dmin = d;
|
d_next = d;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Find possible connection points on the same vertical line.
|
// 2) Find possible connection points on the same vertical line.
|
||||||
int iabove = -1;
|
bool same_prev = false;
|
||||||
|
bool same_next = false;
|
||||||
// Does the perimeter intersect the current vertical line above intrsctn?
|
// Does the perimeter intersect the current vertical line above intrsctn?
|
||||||
for (int i = i_intersection + 1; i < il.intersections.size(); ++ i)
|
for (int i = 0; i < il.intersections.size(); ++ i)
|
||||||
if (il.intersections[i].iContour == itsct.iContour) {
|
if (const SegmentIntersection &it2 = il.intersections[i];
|
||||||
iabove = i;
|
i != i_intersection && it2.iContour == itsct.iContour && it2.type != itsct.type) {
|
||||||
break;
|
int d = distance_of_segmens(poly, it2.iSegment, itsct.iSegment, forward);
|
||||||
}
|
if (d < d_prev) {
|
||||||
// Does the perimeter intersect the current vertical line below intrsctn?
|
iprev = i;
|
||||||
int ibelow = -1;
|
d_prev = d;
|
||||||
for (int i = i_intersection - 1; i >= 0; -- i)
|
same_prev = true;
|
||||||
if (il.intersections[i].iContour == itsct.iContour) {
|
}
|
||||||
ibelow = i;
|
d = distance_of_segmens(poly, itsct.iSegment, it2.iSegment, forward);
|
||||||
break;
|
if (d < d_next) {
|
||||||
|
inext = i;
|
||||||
|
d_next = d;
|
||||||
|
same_next = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
assert(iprev >= 0);
|
||||||
|
assert(inext >= 0);
|
||||||
|
|
||||||
// 3) Sort the intersection points, clear iprev / inext / iSegBelow / iSegAbove,
|
itsct.prev_on_contour = iprev;
|
||||||
// if it is preceded by any other intersection point along the contour.
|
itsct.prev_on_contour_type = same_prev ?
|
||||||
// The perimeter contour orientation.
|
(iprev < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) :
|
||||||
const bool forward = itsct.is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour);
|
SegmentIntersection::LinkType::Horizontal;
|
||||||
{
|
itsct.next_on_contour = inext;
|
||||||
int d_horiz = (iprev == -1) ? std::numeric_limits<int>::max() :
|
itsct.next_on_contour_type = same_next ?
|
||||||
distance_of_segmens(poly, il_prev->intersections[iprev].iSegment, itsct.iSegment, forward);
|
(inext < i_intersection ? SegmentIntersection::LinkType::Down : SegmentIntersection::LinkType::Up) :
|
||||||
int d_down = (ibelow == -1) ? std::numeric_limits<int>::max() :
|
SegmentIntersection::LinkType::Horizontal;
|
||||||
distance_of_segmens(poly, il.intersections[ibelow].iSegment, itsct.iSegment, forward);
|
|
||||||
int d_up = (iabove == -1) ? std::numeric_limits<int>::max() :
|
|
||||||
distance_of_segmens(poly, il.intersections[iabove].iSegment, itsct.iSegment, forward);
|
|
||||||
if (d_horiz < std::min(d_down, d_up)) {
|
|
||||||
itsct.prev_on_contour = iprev;
|
|
||||||
itsct.prev_on_contour_type = SegmentIntersection::LinkType::Horizontal;
|
|
||||||
} else if (d_down < d_up) {
|
|
||||||
itsct.prev_on_contour = ibelow;
|
|
||||||
itsct.prev_on_contour_type = SegmentIntersection::LinkType::Down;
|
|
||||||
} else {
|
|
||||||
itsct.prev_on_contour = iabove;
|
|
||||||
itsct.prev_on_contour_type = SegmentIntersection::LinkType::Up;
|
|
||||||
}
|
|
||||||
// There should always be a link to the next intersection point on the same contour.
|
|
||||||
assert(itsct.prev_on_contour != -1);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
int d_horiz = (inext == -1) ? std::numeric_limits<int>::max() :
|
|
||||||
distance_of_segmens(poly, itsct.iSegment, il_next->intersections[inext].iSegment, forward);
|
|
||||||
int d_down = (ibelow == -1) ? std::numeric_limits<int>::max() :
|
|
||||||
distance_of_segmens(poly, itsct.iSegment, il.intersections[ibelow].iSegment, forward);
|
|
||||||
int d_up = (iabove == -1) ? std::numeric_limits<int>::max() :
|
|
||||||
distance_of_segmens(poly, itsct.iSegment, il.intersections[iabove].iSegment, forward);
|
|
||||||
if (d_horiz < std::min(d_down, d_up)) {
|
|
||||||
itsct.next_on_contour = inext;
|
|
||||||
itsct.next_on_contour_type = SegmentIntersection::LinkType::Horizontal;
|
|
||||||
} else if (d_down < d_up) {
|
|
||||||
itsct.next_on_contour = ibelow;
|
|
||||||
itsct.next_on_contour_type = SegmentIntersection::LinkType::Down;
|
|
||||||
} else {
|
|
||||||
itsct.next_on_contour = iabove;
|
|
||||||
itsct.next_on_contour_type = SegmentIntersection::LinkType::Up;
|
|
||||||
}
|
|
||||||
// There should always be a link to the next intersection point on the same contour.
|
|
||||||
assert(itsct.next_on_contour != -1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// Validate the connectivity.
|
||||||
|
for (size_t i_vline = 0; i_vline + 1 < segs.size(); ++ i_vline) {
|
||||||
|
const SegmentedIntersectionLine &il_left = segs[i_vline];
|
||||||
|
const SegmentedIntersectionLine &il_right = segs[i_vline + 1];
|
||||||
|
for (const SegmentIntersection &it : il_left.intersections) {
|
||||||
|
if (it.has_right_horizontal()) {
|
||||||
|
const SegmentIntersection &it_right = il_right.intersections[it.right_horizontal()];
|
||||||
|
// For a right link there is a symmetric left link.
|
||||||
|
assert(it.iContour == it_right.iContour);
|
||||||
|
assert(it.type == it_right.type);
|
||||||
|
assert(it_right.has_left_horizontal());
|
||||||
|
assert(it_right.left_horizontal() == int(&it - il_left.intersections.data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const SegmentIntersection &it : il_right.intersections) {
|
||||||
|
if (it.has_left_horizontal()) {
|
||||||
|
const SegmentIntersection &it_left = il_left.intersections[it.left_horizontal()];
|
||||||
|
// For a right link there is a symmetric left link.
|
||||||
|
assert(it.iContour == it_left.iContour);
|
||||||
|
assert(it.type == it_left.type);
|
||||||
|
assert(it_left.has_right_horizontal());
|
||||||
|
assert(it_left.right_horizontal() == int(&it - il_right.intersections.data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* NDEBUG */
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection.
|
// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection.
|
||||||
|
@ -1548,7 +1549,7 @@ struct MonotonousRegion
|
||||||
// If false, then ending at the same side as starting.
|
// If false, then ending at the same side as starting.
|
||||||
bool flips { false };
|
bool flips { false };
|
||||||
|
|
||||||
bool length(bool region_flipped) const { return region_flipped ? len2 : len1; }
|
float length(bool region_flipped) const { return region_flipped ? len2 : len1; }
|
||||||
int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
|
int left_intersection_point(bool region_flipped) const { return region_flipped ? left.high : left.low; }
|
||||||
int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
|
int right_intersection_point(bool region_flipped) const { return (region_flipped == flips) ? right.low : right.high; }
|
||||||
|
|
||||||
|
@ -1580,12 +1581,16 @@ struct MonotonousRegionLink
|
||||||
class AntPathMatrix
|
class AntPathMatrix
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AntPathMatrix(const std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs) :
|
AntPathMatrix(
|
||||||
|
const std::vector<MonotonousRegion> ®ions,
|
||||||
|
const ExPolygonWithOffset &poly_with_offset,
|
||||||
|
const std::vector<SegmentedIntersectionLine> &segs,
|
||||||
|
const float initial_pheromone) :
|
||||||
m_regions(regions),
|
m_regions(regions),
|
||||||
m_poly_with_offset(poly_with_offset),
|
m_poly_with_offset(poly_with_offset),
|
||||||
m_segs(segs),
|
m_segs(segs),
|
||||||
// From end of one region to the start of another region, both flipped or not flipped.
|
// From end of one region to the start of another region, both flipped or not flipped.
|
||||||
m_matrix(regions.size() * regions.size() * 4) {}
|
m_matrix(regions.size() * regions.size() * 4, AntPath{ -1., -1., initial_pheromone}) {}
|
||||||
|
|
||||||
AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to)
|
AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to)
|
||||||
{
|
{
|
||||||
|
@ -1602,12 +1607,12 @@ public:
|
||||||
int i_right = vline_from.intersections[i_from].right_horizontal();
|
int i_right = vline_from.intersections[i_from].right_horizontal();
|
||||||
if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
|
if (i_right == i_to && vline_from.intersections[i_from].next_on_contour_quality == SegmentIntersection::LinkQuality::Valid) {
|
||||||
// Measure length along the contour.
|
// Measure length along the contour.
|
||||||
path.length = measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to);
|
path.length = unscale<float>(measure_perimeter_next_segment_length(m_poly_with_offset, m_segs, region_from.right.vline, i_from, i_to));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (path.length == -1.) {
|
if (path.length == -1.) {
|
||||||
// Just apply the Eucledian distance of the end points.
|
// Just apply the Eucledian distance of the end points.
|
||||||
path.length = Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm();
|
path.length = unscale<float>(Vec2f(vline_to.pos - vline_from.pos, vline_to.intersections[i_to].pos() - vline_from.intersections[i_from].pos()).norm());
|
||||||
}
|
}
|
||||||
path.visibility = 1. / (path.length + EPSILON);
|
path.visibility = 1. / (path.length + EPSILON);
|
||||||
}
|
}
|
||||||
|
@ -1682,86 +1687,98 @@ static SegmentIntersection& vertical_run_top(SegmentedIntersectionLine& vline, S
|
||||||
return const_cast<SegmentIntersection&>(vertical_run_top(std::as_const(vline), std::as_const(start)));
|
return const_cast<SegmentIntersection&>(vertical_run_top(std::as_const(vline), std::as_const(start)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static SegmentIntersection* left_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
|
static SegmentIntersection* overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_other, SegmentIntersection::Side side)
|
||||||
{
|
{
|
||||||
SegmentIntersection *left = nullptr;
|
SegmentIntersection *other = nullptr;
|
||||||
for (SegmentIntersection *it = &start; it <= &end; ++ it) {
|
assert(start.is_inner());
|
||||||
int i = it->left_horizontal();
|
assert(end.is_inner());
|
||||||
if (i != -1) {
|
const SegmentIntersection *it = &start;
|
||||||
left = &vline_left.intersections[i];
|
for (;;) {
|
||||||
break;
|
if (it->is_inner()) {
|
||||||
}
|
int i = it->horizontal(side);
|
||||||
}
|
if (i != -1) {
|
||||||
return left == nullptr ? nullptr : &vertical_run_bottom(vline_left, *left);
|
other = &vline_other.intersections[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (it == &end)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (it->type != SegmentIntersection::INNER_HIGH)
|
||||||
|
++ it;
|
||||||
|
else if ((it + 1)->type == SegmentIntersection::INNER_LOW)
|
||||||
|
++ it;
|
||||||
|
else {
|
||||||
|
int up = it->vertical_up();
|
||||||
|
if (up == -1 || it->vertical_up_quality() != SegmentIntersection::LinkQuality::Valid)
|
||||||
|
break;
|
||||||
|
it = &vline_this.intersections[up];
|
||||||
|
assert(it->type == SegmentIntersection::INNER_LOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return other == nullptr ? nullptr : &vertical_run_bottom(vline_other, *other);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SegmentIntersection* left_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
|
static SegmentIntersection* overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_other, SegmentIntersection::Side side)
|
||||||
{
|
{
|
||||||
SegmentIntersection *left = nullptr;
|
SegmentIntersection *other = nullptr;
|
||||||
for (SegmentIntersection *it = &end; it >= &start; -- it) {
|
assert(start.is_inner());
|
||||||
int i = it->left_horizontal();
|
assert(end.is_inner());
|
||||||
if (i != -1) {
|
const SegmentIntersection *it = &end;
|
||||||
left = &vline_left.intersections[i];
|
for (;;) {
|
||||||
break;
|
if (it->is_inner()) {
|
||||||
}
|
int i = it->horizontal(side);
|
||||||
}
|
if (i != -1) {
|
||||||
return left == nullptr ? nullptr : &vertical_run_top(vline_left, *left);
|
other = &vline_other.intersections[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (it == &start)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (it->type != SegmentIntersection::INNER_LOW)
|
||||||
|
-- it;
|
||||||
|
else if ((it - 1)->type == SegmentIntersection::INNER_HIGH)
|
||||||
|
-- it;
|
||||||
|
else {
|
||||||
|
int down = it->vertical_down();
|
||||||
|
if (down == -1 || it->vertical_down_quality() != SegmentIntersection::LinkQuality::Valid)
|
||||||
|
break;
|
||||||
|
it = &vline_this.intersections[down];
|
||||||
|
assert(it->type == SegmentIntersection::INNER_HIGH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return other == nullptr ? nullptr : &vertical_run_top(vline_other, *other);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_left)
|
static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_left)
|
||||||
{
|
{
|
||||||
std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
|
std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
|
||||||
out.first = left_overlap_bottom(start, end, vline_left);
|
out.first = overlap_bottom(start, end, vline_this, vline_left, SegmentIntersection::Side::Left);
|
||||||
if (out.first != nullptr)
|
if (out.first != nullptr)
|
||||||
out.second = left_overlap_top(start, end, vline_left);
|
out.second = overlap_top(start, end, vline_this, vline_left, SegmentIntersection::Side::Left);
|
||||||
|
assert((out.first == nullptr && out.second == nullptr) || out.first < out.second);
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_left)
|
static std::pair<SegmentIntersection*, SegmentIntersection*> left_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_left)
|
||||||
{
|
{
|
||||||
assert((start_end.first == nullptr) == (start_end.second == nullptr));
|
assert((start_end.first == nullptr) == (start_end.second == nullptr));
|
||||||
return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_left);
|
return start_end.first == nullptr ? start_end : left_overlap(*start_end.first, *start_end.second, vline_this, vline_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
static SegmentIntersection* right_overlap_bottom(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
|
static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_right)
|
||||||
{
|
|
||||||
SegmentIntersection *right = nullptr;
|
|
||||||
for (SegmentIntersection *it = &start; it <= &end; ++ it) {
|
|
||||||
int i = it->right_horizontal();
|
|
||||||
if (i != -1) {
|
|
||||||
right = &vline_right.intersections[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return right == nullptr ? nullptr : &vertical_run_bottom(vline_right, *right);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SegmentIntersection* right_overlap_top(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
|
|
||||||
{
|
|
||||||
SegmentIntersection *right = nullptr;
|
|
||||||
for (SegmentIntersection *it = &end; it >= &start; -- it) {
|
|
||||||
int i = it->right_horizontal();
|
|
||||||
if (i != -1) {
|
|
||||||
right = &vline_right.intersections[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return right == nullptr ? nullptr : &vertical_run_top(vline_right, *right);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(SegmentIntersection &start, SegmentIntersection &end, SegmentedIntersectionLine &vline_right)
|
|
||||||
{
|
{
|
||||||
std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
|
std::pair<SegmentIntersection*, SegmentIntersection*> out(nullptr, nullptr);
|
||||||
out.first = right_overlap_bottom(start, end, vline_right);
|
out.first = overlap_bottom(start, end, vline_this, vline_right, SegmentIntersection::Side::Right);
|
||||||
if (out.first != nullptr)
|
if (out.first != nullptr)
|
||||||
out.second = right_overlap_top(start, end, vline_right);
|
out.second = overlap_top(start, end, vline_this, vline_right, SegmentIntersection::Side::Right);
|
||||||
return out;
|
assert((out.first == nullptr && out.second == nullptr) || out.first < out.second);
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_right)
|
static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(std::pair<SegmentIntersection*, SegmentIntersection*> &start_end, SegmentedIntersectionLine &vline_this, SegmentedIntersectionLine &vline_right)
|
||||||
{
|
{
|
||||||
assert((start_end.first == nullptr) == (start_end.second == nullptr));
|
assert((start_end.first == nullptr) == (start_end.second == nullptr));
|
||||||
return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_right);
|
return start_end.first == nullptr ? start_end : right_overlap(*start_end.first, *start_end.second, vline_this, vline_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<SegmentedIntersectionLine> &segs)
|
static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<SegmentedIntersectionLine> &segs)
|
||||||
|
@ -1771,9 +1788,11 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
|
||||||
for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
|
for (int i_vline_seed = 0; i_vline_seed < segs.size(); ++ i_vline_seed) {
|
||||||
SegmentedIntersectionLine &vline_seed = segs[i_vline_seed];
|
SegmentedIntersectionLine &vline_seed = segs[i_vline_seed];
|
||||||
for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
|
for (int i_intersection_seed = 1; i_intersection_seed + 1 < vline_seed.intersections.size(); ) {
|
||||||
while (i_intersection_seed + 1 < vline_seed.intersections.size() &&
|
while (i_intersection_seed < vline_seed.intersections.size() &&
|
||||||
vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW)
|
vline_seed.intersections[i_intersection_seed].type != SegmentIntersection::INNER_LOW)
|
||||||
++ i_intersection_seed;
|
++ i_intersection_seed;
|
||||||
|
if (i_intersection_seed == vline_seed.intersections.size())
|
||||||
|
break;
|
||||||
SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed];
|
SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed];
|
||||||
SegmentIntersection *end = &end_of_vertical_run(vline_seed, *start);
|
SegmentIntersection *end = &end_of_vertical_run(vline_seed, *start);
|
||||||
if (! start->consumed_vertical_up) {
|
if (! start->consumed_vertical_up) {
|
||||||
|
@ -1791,7 +1810,7 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
|
||||||
while (++ i_vline < segs.size()) {
|
while (++ i_vline < segs.size()) {
|
||||||
SegmentedIntersectionLine &vline_left = segs[i_vline - 1];
|
SegmentedIntersectionLine &vline_left = segs[i_vline - 1];
|
||||||
SegmentedIntersectionLine &vline_right = segs[i_vline];
|
SegmentedIntersectionLine &vline_right = segs[i_vline];
|
||||||
std::pair<SegmentIntersection*, SegmentIntersection*> right = right_overlap(left, vline_right);
|
std::pair<SegmentIntersection*, SegmentIntersection*> right = right_overlap(left, vline_left, vline_right);
|
||||||
if (right.first == nullptr)
|
if (right.first == nullptr)
|
||||||
// No neighbor at the right side of the current segment.
|
// No neighbor at the right side of the current segment.
|
||||||
break;
|
break;
|
||||||
|
@ -1799,7 +1818,7 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
|
||||||
if (right_top_first != right.second)
|
if (right_top_first != right.second)
|
||||||
// This segment overlaps with multiple segments at its right side.
|
// This segment overlaps with multiple segments at its right side.
|
||||||
break;
|
break;
|
||||||
std::pair<SegmentIntersection*, SegmentIntersection*> right_left = left_overlap(right, vline_left);
|
std::pair<SegmentIntersection*, SegmentIntersection*> right_left = left_overlap(right, vline_right, vline_left);
|
||||||
if (left != right_left)
|
if (left != right_left)
|
||||||
// Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left.
|
// Left & right draws don't overlap exclusively, right neighbor segment overlaps with multiple segments at its left.
|
||||||
break;
|
break;
|
||||||
|
@ -1843,7 +1862,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, s
|
||||||
if (region.left.vline > 0) {
|
if (region.left.vline > 0) {
|
||||||
auto &vline = segs[region.left.vline];
|
auto &vline = segs[region.left.vline];
|
||||||
auto &vline_left = segs[region.left.vline - 1];
|
auto &vline_left = segs[region.left.vline - 1];
|
||||||
auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline_left);
|
auto[lbegin, lend] = left_overlap(vline.intersections[region.left.low], vline.intersections[region.left.high], vline, vline_left);
|
||||||
if (lbegin != nullptr) {
|
if (lbegin != nullptr) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
MapType key(lbegin, nullptr);
|
MapType key(lbegin, nullptr);
|
||||||
|
@ -1862,7 +1881,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, s
|
||||||
if (region.right.vline + 1 < segs.size()) {
|
if (region.right.vline + 1 < segs.size()) {
|
||||||
auto &vline = segs[region.right.vline];
|
auto &vline = segs[region.right.vline];
|
||||||
auto &vline_right = segs[region.right.vline + 1];
|
auto &vline_right = segs[region.right.vline + 1];
|
||||||
auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline_right);
|
auto [rbegin, rend] = right_overlap(vline.intersections[region.right.low], vline.intersections[region.right.high], vline, vline_right);
|
||||||
if (rbegin != nullptr) {
|
if (rbegin != nullptr) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
MapType key(rbegin, nullptr);
|
MapType key(rbegin, nullptr);
|
||||||
|
@ -1904,24 +1923,19 @@ void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, const std::vector
|
||||||
// exchange by copying the pieces.
|
// exchange by copying the pieces.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #define SLIC3R_DEBUG_ANTS
|
||||||
|
|
||||||
|
template<typename... TArgs>
|
||||||
|
inline void print_ant(const std::string& fmt, TArgs&&... args) {
|
||||||
|
#ifdef SLIC3R_DEBUG_ANTS
|
||||||
|
std::cout << Slic3r::format(fmt, std::forward<TArgs>(args)...) << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Find a run through monotonous infill blocks using an 'Ant colony" optimization method.
|
// Find a run through monotonous infill blocks using an 'Ant colony" optimization method.
|
||||||
static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
|
std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
|
||||||
{
|
{
|
||||||
// Start point of a region (left) given the direction of the initial infill line.
|
|
||||||
auto region_start_point = [&segs](const MonotonousRegion ®ion, bool dir) {
|
|
||||||
const SegmentedIntersectionLine &vline = segs[region.left.vline];
|
|
||||||
const SegmentIntersection &ipt = vline.intersections[dir ? region.left.high : region.left.low];
|
|
||||||
return Vec2f(float(vline.pos), float(ipt.pos()));
|
|
||||||
};
|
|
||||||
// End point of a region (right) given the direction of the initial infill line and whether the monotonous run contains
|
|
||||||
// even or odd number of vertical lines.
|
|
||||||
auto region_end_point = [&segs](const MonotonousRegion ®ion, bool dir) {
|
|
||||||
const SegmentedIntersectionLine &vline = segs[region.right.vline];
|
|
||||||
const SegmentIntersection &ipt = vline.intersections[(dir == region.flips) ? region.right.low : region.right.high];
|
|
||||||
return Vec2f(float(vline.pos), float(ipt.pos()));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed).
|
// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed).
|
||||||
std::vector<int32_t> left_neighbors_unprocessed(regions.size(), 0);
|
std::vector<int32_t> left_neighbors_unprocessed(regions.size(), 0);
|
||||||
// Queue of regions, which have their left neighbors already printed.
|
// Queue of regions, which have their left neighbors already printed.
|
||||||
|
@ -1945,15 +1959,13 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
MonotonousRegion *region;
|
MonotonousRegion *region;
|
||||||
AntPath *link;
|
AntPath *link;
|
||||||
AntPath *link_flipped;
|
AntPath *link_flipped;
|
||||||
float cost;
|
float probability;
|
||||||
bool dir;
|
bool dir;
|
||||||
};
|
};
|
||||||
std::vector<NextCandidate> next_candidates;
|
std::vector<NextCandidate> next_candidates;
|
||||||
|
|
||||||
AntPathMatrix path_matrix(regions, poly_with_offset, segs);
|
|
||||||
|
|
||||||
// How many times to repeat the ant simulation.
|
// How many times to repeat the ant simulation.
|
||||||
constexpr int num_runs = 10;
|
constexpr int num_rounds = 10;
|
||||||
// With how many ants each of the run will be performed?
|
// With how many ants each of the run will be performed?
|
||||||
constexpr int num_ants = 10;
|
constexpr int num_ants = 10;
|
||||||
// Base (initial) pheromone level.
|
// Base (initial) pheromone level.
|
||||||
|
@ -1965,15 +1977,25 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
// Exponents of the cost function.
|
// Exponents of the cost function.
|
||||||
constexpr float pheromone_alpha = 1.f; // pheromone exponent
|
constexpr float pheromone_alpha = 1.f; // pheromone exponent
|
||||||
constexpr float pheromone_beta = 2.f; // attractiveness weighted towards edge length
|
constexpr float pheromone_beta = 2.f; // attractiveness weighted towards edge length
|
||||||
// Cost of traversing a link between two monotonous regions.
|
|
||||||
auto path_cost = [pheromone_alpha, pheromone_beta](AntPath &path) {
|
AntPathMatrix path_matrix(regions, poly_with_offset, segs, pheromone_initial_deposit);
|
||||||
|
|
||||||
|
// Probability (unnormalized) of traversing a link between two monotonous regions.
|
||||||
|
auto path_probability = [pheromone_alpha, pheromone_beta](AntPath &path) {
|
||||||
return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
|
return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
|
||||||
};
|
};
|
||||||
for (int run = 0; run < num_runs; ++ run)
|
|
||||||
|
#ifdef SLIC3R_DEBUG_ANTS
|
||||||
|
static int irun = 0;
|
||||||
|
++ irun;
|
||||||
|
#endif /* SLIC3R_DEBUG_ANTS */
|
||||||
|
|
||||||
|
for (int round = 0; round < num_rounds; ++ round)
|
||||||
{
|
{
|
||||||
for (int ant = 0; ant < num_ants; ++ ant)
|
for (int ant = 0; ant < num_ants; ++ ant)
|
||||||
{
|
{
|
||||||
// Find a new path following the pheromones deposited by the previous ants.
|
// Find a new path following the pheromones deposited by the previous ants.
|
||||||
|
print_ant("Round %1% ant %2%", round, ant);
|
||||||
path.clear();
|
path.clear();
|
||||||
queue = queue_initial;
|
queue = queue_initial;
|
||||||
left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
|
left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
|
||||||
|
@ -1983,12 +2005,18 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
*(queue.begin() + first_idx) = std::move(queue.back());
|
*(queue.begin() + first_idx) = std::move(queue.back());
|
||||||
queue.pop_back();
|
queue.pop_back();
|
||||||
assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0);
|
assert(left_neighbors_unprocessed[path.back().region - regions.data()] == 0);
|
||||||
|
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)",
|
||||||
|
path.back().region->left.vline,
|
||||||
|
path.back().flipped ? path.back().region->left.high : path.back().region->left.low,
|
||||||
|
path.back().flipped ? path.back().region->left.low : path.back().region->left.high,
|
||||||
|
path.back().region->right.vline,
|
||||||
|
path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low,
|
||||||
|
path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high);
|
||||||
|
|
||||||
while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
|
while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
|
||||||
// Chain.
|
// Chain.
|
||||||
MonotonousRegion ®ion = *path.back().region;
|
MonotonousRegion ®ion = *path.back().region;
|
||||||
bool dir = path.back().flipped;
|
bool dir = path.back().flipped;
|
||||||
Vec2f end_pt = region_end_point(region, dir);
|
|
||||||
// Sort by distance to pt.
|
// Sort by distance to pt.
|
||||||
next_candidates.clear();
|
next_candidates.clear();
|
||||||
next_candidates.reserve(region.right_neighbors.size() * 2);
|
next_candidates.reserve(region.right_neighbors.size() * 2);
|
||||||
|
@ -2001,8 +2029,8 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
|
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
|
||||||
AntPath &path2 = path_matrix(region, dir, *next, true);
|
AntPath &path2 = path_matrix(region, dir, *next, true);
|
||||||
AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
|
AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
|
||||||
next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false });
|
next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_probability(path1), false });
|
||||||
next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true });
|
next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_probability(path2), true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
size_t num_direct_neighbors = next_candidates.size();
|
size_t num_direct_neighbors = next_candidates.size();
|
||||||
|
@ -2014,28 +2042,30 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
|
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
|
||||||
AntPath &path2 = path_matrix(region, dir, *next, true);
|
AntPath &path2 = path_matrix(region, dir, *next, true);
|
||||||
AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
|
AntPath &path2_flipped = path_matrix(region, ! dir, *next, false);
|
||||||
next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_cost(path1), false });
|
next_candidates.emplace_back(NextCandidate{ next, &path1, &path1_flipped, path_probability(path1), false });
|
||||||
next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_cost(path2), true });
|
next_candidates.emplace_back(NextCandidate{ next, &path2, &path2_flipped, path_probability(path2), true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
float dice = float(rng()) / float(rng.max());
|
float dice = float(rng()) / float(rng.max());
|
||||||
std::vector<NextCandidate>::iterator take_path;
|
std::vector<NextCandidate>::iterator take_path;
|
||||||
if (dice < probability_take_best) {
|
if (dice < probability_take_best) {
|
||||||
// Take the lowest cost path.
|
// Take the highest probability path.
|
||||||
take_path = std::min_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.cost < r.cost; });
|
take_path = std::max_element(next_candidates.begin(), next_candidates.end(), [](auto &l, auto &r){ return l.probability < r.probability; });
|
||||||
|
print_ant("\tTaking best path at probability %1% below %2%", dice, probability_take_best);
|
||||||
} else {
|
} else {
|
||||||
// Take the path based on the cost.
|
// Take the path based on the probability.
|
||||||
// Calculate the total cost.
|
// Calculate the total probability.
|
||||||
float total_cost = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.cost; });
|
float total_probability = std::accumulate(next_candidates.begin(), next_candidates.end(), 0.f, [](const float l, const NextCandidate& r) { return l + r.probability; });
|
||||||
// Take a random path based on the cost.
|
// Take a random path based on the probability.
|
||||||
float cost_threshold = floor(float(rng()) * total_cost / float(rng.max()));
|
float probability_threshold = float(rng()) * total_probability / float(rng.max());
|
||||||
take_path = next_candidates.end();
|
take_path = next_candidates.end();
|
||||||
-- take_path;
|
-- take_path;
|
||||||
for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it)
|
for (auto it = next_candidates.begin(); it < next_candidates.end(); ++ it)
|
||||||
if (cost_threshold -= it->cost <= 0.) {
|
if ((probability_threshold -= it->probability) <= 0.) {
|
||||||
take_path = it;
|
take_path = it;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
print_ant("\tTaking path at probability threshold %1% of %2%", probability_threshold, total_probability);
|
||||||
}
|
}
|
||||||
// Move the other right neighbors with satisified constraints to the queue.
|
// Move the other right neighbors with satisified constraints to the queue.
|
||||||
bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors;
|
bool direct_neighbor_taken = take_path - next_candidates.begin() < num_direct_neighbors;
|
||||||
|
@ -2054,6 +2084,23 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
path.back().next = take_path->link;
|
path.back().next = take_path->link;
|
||||||
path.back().next_flipped = take_path->link_flipped;
|
path.back().next_flipped = take_path->link_flipped;
|
||||||
path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
|
path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
|
||||||
|
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%",
|
||||||
|
next_region->left.vline,
|
||||||
|
next_dir ? next_region->left.high : next_region->left.low,
|
||||||
|
next_dir ? next_region->left.low : next_region->left.high,
|
||||||
|
next_region->right.vline,
|
||||||
|
next_dir == next_region->flips ? next_region->right.high : next_region->right.low,
|
||||||
|
next_dir == next_region->flips ? next_region->right.low : next_region->right.high,
|
||||||
|
take_path->link->length);
|
||||||
|
|
||||||
|
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%)",
|
||||||
|
path.back().region->left.vline,
|
||||||
|
path.back().flipped ? path.back().region->left.high : path.back().region->left.low,
|
||||||
|
path.back().flipped ? path.back().region->left.low : path.back().region->left.high,
|
||||||
|
path.back().region->right.vline,
|
||||||
|
path.back().flipped == path.back().region->flips ? path.back().region->right.high : path.back().region->right.low,
|
||||||
|
path.back().flipped == path.back().region->flips ? path.back().region->right.low : path.back().region->right.high);
|
||||||
|
|
||||||
// Update pheromones along this link.
|
// Update pheromones along this link.
|
||||||
take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
|
take_path->link->pheromone = (1.f - pheromone_evaporation) * take_path->link->pheromone + pheromone_evaporation * pheromone_initial_deposit;
|
||||||
}
|
}
|
||||||
|
@ -2070,6 +2117,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
||||||
return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length;
|
return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length;
|
||||||
});
|
});
|
||||||
// Save the shortest path.
|
// Save the shortest path.
|
||||||
|
print_ant("\tThis length: %1%, shortest length: %2%", path_length, best_path_length);
|
||||||
if (path_length < best_path_length) {
|
if (path_length < best_path_length) {
|
||||||
best_path_length = path_length;
|
best_path_length = path_length;
|
||||||
std::swap(best_path, path);
|
std::swap(best_path, path);
|
||||||
|
@ -2322,7 +2370,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
|
||||||
#endif /* SLIC3R_DEBUG */
|
#endif /* SLIC3R_DEBUG */
|
||||||
|
|
||||||
//FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison.
|
//FIXME this is a hack to get the monotonous infill rolling. We likely want a smarter switch, likely based on user decison.
|
||||||
bool monotonous_infill = params.density > 0.99;
|
bool monotonous_infill = params.monotonous; // || params.density > 0.99;
|
||||||
if (monotonous_infill) {
|
if (monotonous_infill) {
|
||||||
std::vector<MonotonousRegion> regions = generate_montonous_regions(segs);
|
std::vector<MonotonousRegion> regions = generate_montonous_regions(segs);
|
||||||
connect_monotonous_regions(regions, segs);
|
connect_monotonous_regions(regions, segs);
|
||||||
|
@ -2379,6 +2427,17 @@ Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParam
|
||||||
return polylines_out;
|
return polylines_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polylines FillMonotonous::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||||
|
{
|
||||||
|
FillParams params2 = params;
|
||||||
|
params2.monotonous = true;
|
||||||
|
Polylines polylines_out;
|
||||||
|
if (! fill_surface_by_lines(surface, params2, 0.f, 0.f, polylines_out)) {
|
||||||
|
printf("FillMonotonous::fill_surface() failed to fill a region.\n");
|
||||||
|
}
|
||||||
|
return polylines_out;
|
||||||
|
}
|
||||||
|
|
||||||
Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams ¶ms)
|
Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||||
{
|
{
|
||||||
// Each linear fill covers half of the target coverage.
|
// Each linear fill covers half of the target coverage.
|
||||||
|
|
|
@ -13,18 +13,27 @@ class FillRectilinear2 : public Fill
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Fill* clone() const { return new FillRectilinear2(*this); };
|
virtual Fill* clone() const { return new FillRectilinear2(*this); };
|
||||||
virtual ~FillRectilinear2() {}
|
virtual ~FillRectilinear2() = default;
|
||||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FillMonotonous : public FillRectilinear2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual Fill* clone() const { return new FillMonotonous(*this); };
|
||||||
|
virtual ~FillMonotonous() = default;
|
||||||
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
|
virtual bool no_sort() const { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
class FillGrid2 : public FillRectilinear2
|
class FillGrid2 : public FillRectilinear2
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Fill* clone() const { return new FillGrid2(*this); };
|
virtual Fill* clone() const { return new FillGrid2(*this); };
|
||||||
virtual ~FillGrid2() {}
|
virtual ~FillGrid2() = default;
|
||||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -36,7 +45,7 @@ class FillTriangles : public FillRectilinear2
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Fill* clone() const { return new FillTriangles(*this); };
|
virtual Fill* clone() const { return new FillTriangles(*this); };
|
||||||
virtual ~FillTriangles() {}
|
virtual ~FillTriangles() = default;
|
||||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -48,7 +57,7 @@ class FillStars : public FillRectilinear2
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Fill* clone() const { return new FillStars(*this); };
|
virtual Fill* clone() const { return new FillStars(*this); };
|
||||||
virtual ~FillStars() {}
|
virtual ~FillStars() = default;
|
||||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -60,7 +69,7 @@ class FillCubic : public FillRectilinear2
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual Fill* clone() const { return new FillCubic(*this); };
|
virtual Fill* clone() const { return new FillCubic(*this); };
|
||||||
virtual ~FillCubic() {}
|
virtual ~FillCubic() = default;
|
||||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -418,18 +418,20 @@ void PrintConfigDef::init_fff_params()
|
||||||
def->cli = "top-fill-pattern|external-fill-pattern|solid-fill-pattern";
|
def->cli = "top-fill-pattern|external-fill-pattern|solid-fill-pattern";
|
||||||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||||
def->enum_values.push_back("rectilinear");
|
def->enum_values.push_back("rectilinear");
|
||||||
|
def->enum_values.push_back("monotonous");
|
||||||
def->enum_values.push_back("concentric");
|
def->enum_values.push_back("concentric");
|
||||||
def->enum_values.push_back("hilbertcurve");
|
def->enum_values.push_back("hilbertcurve");
|
||||||
def->enum_values.push_back("archimedeanchords");
|
def->enum_values.push_back("archimedeanchords");
|
||||||
def->enum_values.push_back("octagramspiral");
|
def->enum_values.push_back("octagramspiral");
|
||||||
def->enum_labels.push_back(L("Rectilinear"));
|
def->enum_labels.push_back(L("Rectilinear"));
|
||||||
|
def->enum_labels.push_back(L("Monotonous"));
|
||||||
def->enum_labels.push_back(L("Concentric"));
|
def->enum_labels.push_back(L("Concentric"));
|
||||||
def->enum_labels.push_back(L("Hilbert Curve"));
|
def->enum_labels.push_back(L("Hilbert Curve"));
|
||||||
def->enum_labels.push_back(L("Archimedean Chords"));
|
def->enum_labels.push_back(L("Archimedean Chords"));
|
||||||
def->enum_labels.push_back(L("Octagram Spiral"));
|
def->enum_labels.push_back(L("Octagram Spiral"));
|
||||||
// solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern.
|
// solid_fill_pattern is an obsolete equivalent to top_fill_pattern/bottom_fill_pattern.
|
||||||
def->aliases = { "solid_fill_pattern", "external_fill_pattern" };
|
def->aliases = { "solid_fill_pattern", "external_fill_pattern" };
|
||||||
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
|
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipMonotonous));
|
||||||
|
|
||||||
def = this->add("bottom_fill_pattern", coEnum);
|
def = this->add("bottom_fill_pattern", coEnum);
|
||||||
def->label = L("Bottom fill pattern");
|
def->label = L("Bottom fill pattern");
|
||||||
|
|
|
@ -34,7 +34,7 @@ enum PrintHostType {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InfillPattern {
|
enum InfillPattern {
|
||||||
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
ipRectilinear, ipMonotonous, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
|
||||||
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
|
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::g
|
||||||
static t_config_enum_values keys_map;
|
static t_config_enum_values keys_map;
|
||||||
if (keys_map.empty()) {
|
if (keys_map.empty()) {
|
||||||
keys_map["rectilinear"] = ipRectilinear;
|
keys_map["rectilinear"] = ipRectilinear;
|
||||||
|
keys_map["monotonous"] = ipMonotonous;
|
||||||
keys_map["grid"] = ipGrid;
|
keys_map["grid"] = ipGrid;
|
||||||
keys_map["triangles"] = ipTriangles;
|
keys_map["triangles"] = ipTriangles;
|
||||||
keys_map["stars"] = ipStars;
|
keys_map["stars"] = ipStars;
|
||||||
|
|
|
@ -2629,6 +2629,7 @@ void PrintObject::combine_infill()
|
||||||
// Because fill areas for rectilinear and honeycomb are grown
|
// Because fill areas for rectilinear and honeycomb are grown
|
||||||
// later to overlap perimeters, we need to counteract that too.
|
// later to overlap perimeters, we need to counteract that too.
|
||||||
((region->config().fill_pattern == ipRectilinear ||
|
((region->config().fill_pattern == ipRectilinear ||
|
||||||
|
region->config().fill_pattern == ipMonotonous ||
|
||||||
region->config().fill_pattern == ipGrid ||
|
region->config().fill_pattern == ipGrid ||
|
||||||
region->config().fill_pattern == ipLine ||
|
region->config().fill_pattern == ipLine ||
|
||||||
region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
|
region->config().fill_pattern == ipHoneycomb) ? 1.5f : 0.5f) *
|
||||||
|
|
|
@ -38,7 +38,7 @@ std::vector<std::pair<size_t, bool>> chain_segments_closest_point(std::vector<En
|
||||||
// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
|
// Ignore the starting point as the starting point is considered to be occupied, no end point coud connect to it.
|
||||||
size_t next_idx = find_closest_point(kdtree, this_point.pos,
|
size_t next_idx = find_closest_point(kdtree, this_point.pos,
|
||||||
[this_idx, &end_points, &could_reverse_func](size_t idx) {
|
[this_idx, &end_points, &could_reverse_func](size_t idx) {
|
||||||
return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx ^ 1) == 0 || could_reverse_func(idx >> 1));
|
return (idx ^ this_idx) > 1 && end_points[idx].chain_id == 0 && ((idx & 1) == 0 || could_reverse_func(idx >> 1));
|
||||||
});
|
});
|
||||||
assert(next_idx < end_points.size());
|
assert(next_idx < end_points.size());
|
||||||
EndPointType &end_point = end_points[next_idx];
|
EndPointType &end_point = end_points[next_idx];
|
||||||
|
|
|
@ -2317,10 +2317,15 @@ static inline void fill_expolygons_generate_paths(
|
||||||
fill_params.dont_adjust = true;
|
fill_params.dont_adjust = true;
|
||||||
for (const ExPolygon &expoly : expolygons) {
|
for (const ExPolygon &expoly : expolygons) {
|
||||||
Surface surface(stInternal, expoly);
|
Surface surface(stInternal, expoly);
|
||||||
|
Polylines polylines;
|
||||||
|
try {
|
||||||
|
polylines = filler->fill_surface(&surface, fill_params);
|
||||||
|
} catch (InfillFailedException &) {
|
||||||
|
}
|
||||||
extrusion_entities_append_paths(
|
extrusion_entities_append_paths(
|
||||||
dst,
|
dst,
|
||||||
filler->fill_surface(&surface, fill_params),
|
std::move(polylines),
|
||||||
role,
|
role,
|
||||||
flow.mm3_per_mm(), flow.width, flow.height);
|
flow.mm3_per_mm(), flow.width, flow.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2339,9 +2344,14 @@ static inline void fill_expolygons_generate_paths(
|
||||||
fill_params.dont_adjust = true;
|
fill_params.dont_adjust = true;
|
||||||
for (ExPolygon &expoly : expolygons) {
|
for (ExPolygon &expoly : expolygons) {
|
||||||
Surface surface(stInternal, std::move(expoly));
|
Surface surface(stInternal, std::move(expoly));
|
||||||
|
Polylines polylines;
|
||||||
|
try {
|
||||||
|
polylines = filler->fill_surface(&surface, fill_params);
|
||||||
|
} catch (InfillFailedException &) {
|
||||||
|
}
|
||||||
extrusion_entities_append_paths(
|
extrusion_entities_append_paths(
|
||||||
dst,
|
dst,
|
||||||
filler->fill_surface(&surface, fill_params),
|
std::move(polylines),
|
||||||
role,
|
role,
|
||||||
flow.mm3_per_mm(), flow.width, flow.height);
|
flow.mm3_per_mm(), flow.width, flow.height);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue