mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-11-02 20:51:23 -07:00
Merge remote-tracking branch 'remotes/origin/master' into lh_adaptive_infill_hooks
This commit is contained in:
commit
414fdaefc5
228 changed files with 30930 additions and 13115 deletions
|
|
@ -535,7 +535,7 @@ void Layer::make_ironing()
|
|||
fill_params.density = 1.;
|
||||
// fill_params.dont_connect = true;
|
||||
fill_params.dont_connect = false;
|
||||
fill_params.monotonous = true;
|
||||
fill_params.monotonic = true;
|
||||
|
||||
for (size_t i = 0; i < by_extruder.size(); ++ i) {
|
||||
// Find span of regions equivalent to the ironing operation.
|
||||
|
|
@ -579,7 +579,7 @@ void Layer::make_ironing()
|
|||
// Save into layer.
|
||||
ExtrusionEntityCollection *eec = nullptr;
|
||||
ironing_params.layerm->fills.entities.push_back(eec = new ExtrusionEntityCollection());
|
||||
// Don't sort the ironing infill lines as they are monotonously ordered.
|
||||
// Don't sort the ironing infill lines as they are monotonicly ordered.
|
||||
eec->no_sort = true;
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(polylines),
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ static const std::array<Vec3d, 8> child_centers {
|
|||
};
|
||||
|
||||
// Traversal order of octree children cells for three infill directions,
|
||||
// so that a single line will be discretized in a strictly monotonous order.
|
||||
// so that a single line will be discretized in a strictly monotonic order.
|
||||
static constexpr std::array<std::array<int, 8>, 3> child_traversal_order {
|
||||
std::array<int, 8>{ 2, 3, 0, 1, 6, 7, 4, 5 },
|
||||
std::array<int, 8>{ 4, 0, 6, 2, 5, 1, 7, 3 },
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#include "FillPlanePath.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
#include "FillRectilinear2.hpp"
|
||||
#include "FillRectilinear3.hpp"
|
||||
#include "FillAdaptive.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -28,7 +27,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
|
|||
case ip3DHoneycomb: return new Fill3DHoneycomb();
|
||||
case ipGyroid: return new FillGyroid();
|
||||
case ipRectilinear: return new FillRectilinear2();
|
||||
case ipMonotonous: return new FillMonotonous();
|
||||
case ipMonotonic: return new FillMonotonic();
|
||||
case ipLine: return new FillLine();
|
||||
case ipGrid: return new FillGrid2();
|
||||
case ipTriangles: return new FillTriangles();
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ struct FillParams
|
|||
// Don't adjust spacing to fill the space evenly.
|
||||
bool dont_adjust { true };
|
||||
|
||||
// Monotonous infill - strictly left to right for better surface quality of top infills.
|
||||
bool monotonous { false };
|
||||
// Monotonic infill - strictly left to right for better surface quality of top infills.
|
||||
bool monotonic { false };
|
||||
|
||||
// For Honeycomb.
|
||||
// we were requested to complete each loop;
|
||||
|
|
|
|||
|
|
@ -154,7 +154,9 @@ struct SegmentIntersection
|
|||
// Vertical link, up.
|
||||
Up,
|
||||
// Vertical link, down.
|
||||
Down
|
||||
Down,
|
||||
// Phony intersection point has no link.
|
||||
Phony,
|
||||
};
|
||||
|
||||
enum class LinkQuality : uint8_t {
|
||||
|
|
@ -353,6 +355,25 @@ struct SegmentedIntersectionLine
|
|||
std::vector<SegmentIntersection> intersections;
|
||||
};
|
||||
|
||||
static SegmentIntersection phony_outer_intersection(SegmentIntersection::SegmentIntersectionType type, coord_t pos)
|
||||
{
|
||||
assert(type == SegmentIntersection::OUTER_LOW || type == SegmentIntersection::OUTER_HIGH);
|
||||
SegmentIntersection out;
|
||||
// Invalid contour & segment.
|
||||
out.iContour = std::numeric_limits<size_t>::max();
|
||||
out.iSegment = std::numeric_limits<size_t>::max();
|
||||
out.pos_p = pos;
|
||||
out.type = type;
|
||||
// Invalid prev / next.
|
||||
out.prev_on_contour = -1;
|
||||
out.next_on_contour = -1;
|
||||
out.prev_on_contour_type = SegmentIntersection::LinkType::Phony;
|
||||
out.next_on_contour_type = SegmentIntersection::LinkType::Phony;
|
||||
out.prev_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
|
||||
out.next_on_contour_quality = SegmentIntersection::LinkQuality::Invalid;
|
||||
return out;
|
||||
}
|
||||
|
||||
// A container maintaining an expolygon with its inner offsetted polygon.
|
||||
// The purpose of the inner offsetted polygon is to provide segments to connect the infill lines.
|
||||
struct ExPolygonWithOffset
|
||||
|
|
@ -889,6 +910,60 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
|||
return segs;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
bool validate_segment_intersection_connectivity(const std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
|
||||
const SegmentedIntersectionLine &il = segs[i_vline];
|
||||
for (const SegmentIntersection &it : il.intersections) {
|
||||
auto i_it = int(&it - il.intersections.data());
|
||||
if (it.has_left_vertical_up()) {
|
||||
assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it);
|
||||
assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality);
|
||||
}
|
||||
if (it.has_left_vertical_down()) {
|
||||
assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it);
|
||||
assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality);
|
||||
}
|
||||
if (it.has_right_vertical_up()) {
|
||||
assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it);
|
||||
assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality);
|
||||
}
|
||||
if (it.has_right_vertical_down()) {
|
||||
assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it);
|
||||
assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
// Connect each contour / vertical line intersection point with another two contour / vertical line intersection points.
|
||||
// (fill in SegmentIntersection::{prev_on_contour, prev_on_contour_vertical, next_on_contour, next_on_contour_vertical}.
|
||||
// These contour points are either on the same vertical line, or on the vertical line left / right to the current one.
|
||||
|
|
@ -1055,55 +1130,104 @@ static void connect_segment_intersections_by_contours(
|
|||
}
|
||||
}
|
||||
|
||||
#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()));
|
||||
assert(validate_segment_intersection_connectivity(segs));
|
||||
}
|
||||
|
||||
static void pinch_contours_insert_phony_outer_intersections(std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// Keep the vector outside the loops, so they will not be reallocated.
|
||||
// Where to insert new outer points.
|
||||
std::vector<size_t> insert_after;
|
||||
// Mapping of indices of current intersection line after inserting new outer points.
|
||||
std::vector<int32_t> map;
|
||||
std::vector<SegmentIntersection> temp_intersections;
|
||||
|
||||
for (size_t i_vline = 1; i_vline < segs.size(); ++ i_vline) {
|
||||
SegmentedIntersectionLine &il = segs[i_vline];
|
||||
assert(il.intersections.empty() || il.intersections.size() >= 2);
|
||||
if (! il.intersections.empty()) {
|
||||
assert(il.intersections.front().type == SegmentIntersection::OUTER_LOW);
|
||||
assert(il.intersections.back().type == SegmentIntersection::OUTER_HIGH);
|
||||
auto end = il.intersections.end() - 1;
|
||||
insert_after.clear();
|
||||
for (auto it = il.intersections.begin() + 1; it != end;) {
|
||||
if (it->type == SegmentIntersection::OUTER_HIGH) {
|
||||
++ it;
|
||||
assert(it->type == SegmentIntersection::OUTER_LOW);
|
||||
++ it;
|
||||
} else {
|
||||
auto lo = it;
|
||||
assert(lo->type == SegmentIntersection::INNER_LOW);
|
||||
auto hi = ++ it;
|
||||
assert(hi->type == SegmentIntersection::INNER_HIGH);
|
||||
auto lo2 = ++ it;
|
||||
if (lo2->type == SegmentIntersection::INNER_LOW) {
|
||||
// INNER_HIGH followed by INNER_LOW. The outer contour may have squeezed the inner contour into two separate loops.
|
||||
// In that case one shall insert a phony OUTER_HIGH / OUTER_LOW pair.
|
||||
int up = hi->vertical_up();
|
||||
int dn = lo2->vertical_down();
|
||||
#ifndef _NDEBUG
|
||||
assert(up == -1 || up > 0);
|
||||
assert(dn == -1 || dn >= 0);
|
||||
assert((up == -1 && dn == -1) || (dn + 1 == up));
|
||||
#endif // _NDEBUG
|
||||
bool pinched = dn + 1 != up;
|
||||
if (pinched) {
|
||||
// hi is not connected with its inner contour to lo2.
|
||||
// Insert a phony OUTER_HIGH / OUTER_LOW pair.
|
||||
#if 0
|
||||
static int pinch_idx = 0;
|
||||
printf("Pinched %d\n", pinch_idx++);
|
||||
#endif
|
||||
insert_after.emplace_back(hi - il.intersections.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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()));
|
||||
|
||||
if (! insert_after.empty()) {
|
||||
// Insert phony OUTER_HIGH / OUTER_LOW pairs, adjust indices pointing to intersection points on this contour.
|
||||
map.clear();
|
||||
{
|
||||
size_t i = 0;
|
||||
temp_intersections.clear();
|
||||
for (size_t idx_inset_after : insert_after) {
|
||||
for (; i <= idx_inset_after; ++ i) {
|
||||
map.emplace_back(temp_intersections.size());
|
||||
temp_intersections.emplace_back(il.intersections[i]);
|
||||
}
|
||||
coord_t pos = (temp_intersections.back().pos() + il.intersections[i].pos()) / 2;
|
||||
temp_intersections.emplace_back(phony_outer_intersection(SegmentIntersection::OUTER_HIGH, pos));
|
||||
temp_intersections.emplace_back(phony_outer_intersection(SegmentIntersection::OUTER_LOW, pos));
|
||||
}
|
||||
for (; i < il.intersections.size(); ++ i) {
|
||||
map.emplace_back(temp_intersections.size());
|
||||
temp_intersections.emplace_back(il.intersections[i]);
|
||||
}
|
||||
temp_intersections.swap(il.intersections);
|
||||
}
|
||||
// Reindex references on current intersection line.
|
||||
for (SegmentIntersection &ip : il.intersections) {
|
||||
if (ip.has_left_vertical())
|
||||
ip.prev_on_contour = map[ip.prev_on_contour];
|
||||
if (ip.has_right_vertical())
|
||||
ip.next_on_contour = map[ip.next_on_contour];
|
||||
}
|
||||
// Reindex references on previous intersection line.
|
||||
for (SegmentIntersection &ip : segs[i_vline - 1].intersections)
|
||||
if (ip.has_right_horizontal())
|
||||
ip.next_on_contour = map[ip.next_on_contour];
|
||||
if (i_vline < segs.size()) {
|
||||
// Reindex references on next intersection line.
|
||||
for (SegmentIntersection &ip : segs[i_vline + 1].intersections)
|
||||
if (ip.has_left_horizontal())
|
||||
ip.prev_on_contour = map[ip.prev_on_contour];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) {
|
||||
const SegmentedIntersectionLine &il = segs[i_vline];
|
||||
for (const SegmentIntersection &it : il.intersections) {
|
||||
auto i_it = int(&it - il.intersections.data());
|
||||
if (it.has_left_vertical_up()) {
|
||||
assert(il.intersections[it.left_vertical_up()].left_vertical_down() == i_it);
|
||||
assert(il.intersections[it.left_vertical_up()].prev_on_contour_quality == it.prev_on_contour_quality);
|
||||
}
|
||||
if (it.has_left_vertical_down()) {
|
||||
assert(il.intersections[it.left_vertical_down()].left_vertical_up() == i_it);
|
||||
assert(il.intersections[it.left_vertical_down()].prev_on_contour_quality == it.prev_on_contour_quality);
|
||||
}
|
||||
if (it.has_right_vertical_up()) {
|
||||
assert(il.intersections[it.right_vertical_up()].right_vertical_down() == i_it);
|
||||
assert(il.intersections[it.right_vertical_up()].next_on_contour_quality == it.next_on_contour_quality);
|
||||
}
|
||||
if (it.has_right_vertical_down()) {
|
||||
assert(il.intersections[it.right_vertical_down()].right_vertical_up() == i_it);
|
||||
assert(il.intersections[it.right_vertical_down()].next_on_contour_quality == it.next_on_contour_quality);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
assert(validate_segment_intersection_connectivity(segs));
|
||||
}
|
||||
|
||||
// Find the last INNER_HIGH intersection starting with INNER_LOW, that is followed by OUTER_HIGH intersection.
|
||||
|
|
@ -1387,7 +1511,7 @@ static void traverse_graph_generate_polylines(
|
|||
}
|
||||
}
|
||||
|
||||
struct MonotonousRegion
|
||||
struct MonotonicRegion
|
||||
{
|
||||
struct Boundary {
|
||||
int vline;
|
||||
|
|
@ -1412,13 +1536,13 @@ struct MonotonousRegion
|
|||
|
||||
#if NDEBUG
|
||||
// Left regions are used to track whether all regions left to this one have already been printed.
|
||||
boost::container::small_vector<MonotonousRegion*, 4> left_neighbors;
|
||||
boost::container::small_vector<MonotonicRegion*, 4> left_neighbors;
|
||||
// Right regions are held to pick a next region to be extruded using the "Ant colony" heuristics.
|
||||
boost::container::small_vector<MonotonousRegion*, 4> right_neighbors;
|
||||
boost::container::small_vector<MonotonicRegion*, 4> right_neighbors;
|
||||
#else
|
||||
// For debugging, use the normal vector as it is better supported by debug visualizers.
|
||||
std::vector<MonotonousRegion*> left_neighbors;
|
||||
std::vector<MonotonousRegion*> right_neighbors;
|
||||
std::vector<MonotonicRegion*> left_neighbors;
|
||||
std::vector<MonotonicRegion*> right_neighbors;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -1429,9 +1553,9 @@ struct AntPath
|
|||
float pheromone { 0 }; // <0, 1>
|
||||
};
|
||||
|
||||
struct MonotonousRegionLink
|
||||
struct MonotonicRegionLink
|
||||
{
|
||||
MonotonousRegion *region;
|
||||
MonotonicRegion *region;
|
||||
bool flipped;
|
||||
// Distance of right side of this region to left side of the next region, if the "flipped" flag of this region and the next region
|
||||
// is applied as defined.
|
||||
|
|
@ -1447,7 +1571,7 @@ class AntPathMatrix
|
|||
{
|
||||
public:
|
||||
AntPathMatrix(
|
||||
const std::vector<MonotonousRegion> ®ions,
|
||||
const std::vector<MonotonicRegion> ®ions,
|
||||
const ExPolygonWithOffset &poly_with_offset,
|
||||
const std::vector<SegmentedIntersectionLine> &segs,
|
||||
const float initial_pheromone) :
|
||||
|
|
@ -1463,7 +1587,7 @@ public:
|
|||
ap.pheromone = initial_pheromone;
|
||||
}
|
||||
|
||||
AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegion ®ion_to, bool flipped_to)
|
||||
AntPath& operator()(const MonotonicRegion ®ion_from, bool flipped_from, const MonotonicRegion ®ion_to, bool flipped_to)
|
||||
{
|
||||
int row = 2 * int(®ion_from - m_regions.data()) + flipped_from;
|
||||
int col = 2 * int(®ion_to - m_regions.data()) + flipped_to;
|
||||
|
|
@ -1490,16 +1614,16 @@ public:
|
|||
return path;
|
||||
}
|
||||
|
||||
AntPath& operator()(const MonotonousRegionLink ®ion_from, const MonotonousRegion ®ion_to, bool flipped_to)
|
||||
AntPath& operator()(const MonotonicRegionLink ®ion_from, const MonotonicRegion ®ion_to, bool flipped_to)
|
||||
{ return (*this)(*region_from.region, region_from.flipped, region_to, flipped_to); }
|
||||
AntPath& operator()(const MonotonousRegion ®ion_from, bool flipped_from, const MonotonousRegionLink ®ion_to)
|
||||
AntPath& operator()(const MonotonicRegion ®ion_from, bool flipped_from, const MonotonicRegionLink ®ion_to)
|
||||
{ return (*this)(region_from, flipped_from, *region_to.region, region_to.flipped); }
|
||||
AntPath& operator()(const MonotonousRegionLink ®ion_from, const MonotonousRegionLink ®ion_to)
|
||||
AntPath& operator()(const MonotonicRegionLink ®ion_from, const MonotonicRegionLink ®ion_to)
|
||||
{ return (*this)(*region_from.region, region_from.flipped, *region_to.region, region_to.flipped); }
|
||||
|
||||
private:
|
||||
// Source regions, used for addressing and updating m_matrix.
|
||||
const std::vector<MonotonousRegion> &m_regions;
|
||||
const std::vector<MonotonicRegion> &m_regions;
|
||||
// To calculate the intersection points and contour lengths.
|
||||
const ExPolygonWithOffset &m_poly_with_offset;
|
||||
const std::vector<SegmentedIntersectionLine> &m_segs;
|
||||
|
|
@ -1652,9 +1776,9 @@ static std::pair<SegmentIntersection*, SegmentIntersection*> right_overlap(std::
|
|||
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<MonotonicRegion> generate_montonous_regions(std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
std::vector<MonotonousRegion> monotonous_regions;
|
||||
std::vector<MonotonicRegion> monotonic_regions;
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define SLIC3R_DEBUG_MONOTONOUS_REGIONS
|
||||
|
|
@ -1685,11 +1809,11 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
|
|||
SegmentIntersection *start = &vline_seed.intersections[i_intersection_seed];
|
||||
SegmentIntersection *end = &end_of_vertical_run(vline_seed, *start);
|
||||
if (! start->consumed_vertical_up) {
|
||||
// Draw a new monotonous region starting with this segment.
|
||||
// Draw a new monotonic region starting with this segment.
|
||||
// while there is only a single right neighbor
|
||||
int i_vline = i_vline_seed;
|
||||
std::pair<SegmentIntersection*, SegmentIntersection*> left(start, end);
|
||||
MonotonousRegion region;
|
||||
MonotonicRegion region;
|
||||
region.left.vline = i_vline;
|
||||
region.left.low = int(left.first - vline_seed.intersections.data());
|
||||
region.left.high = int(left.second - vline_seed.intersections.data());
|
||||
|
|
@ -1722,19 +1846,19 @@ static std::vector<MonotonousRegion> generate_montonous_regions(std::vector<Segm
|
|||
}
|
||||
// Even number of lines makes the infill zig-zag to exit on the other side of the region than where it starts.
|
||||
region.flips = (num_lines & 1) != 0;
|
||||
monotonous_regions.emplace_back(region);
|
||||
monotonic_regions.emplace_back(region);
|
||||
}
|
||||
i_intersection_seed = int(end - vline_seed.intersections.data()) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return monotonous_regions;
|
||||
return monotonic_regions;
|
||||
}
|
||||
|
||||
// Traverse path, calculate length of the draw for the purpose of optimization.
|
||||
// This function is very similar to polylines_from_paths() in the way how it traverses the path, but
|
||||
// polylines_from_paths() emits a path, while this function just calculates the path length.
|
||||
static float montonous_region_path_length(const MonotonousRegion ®ion, bool dir, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs)
|
||||
static float montonous_region_path_length(const MonotonicRegion ®ion, bool dir, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// From the initial point (i_vline, i_intersection), follow a path.
|
||||
int i_intersection = region.left_intersection_point(dir);
|
||||
|
|
@ -1822,15 +1946,15 @@ static float montonous_region_path_length(const MonotonousRegion ®ion, bool d
|
|||
return unscale<float>(total_length);
|
||||
}
|
||||
|
||||
static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs)
|
||||
static void connect_monotonic_regions(std::vector<MonotonicRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// Map from low intersection to left / right side of a monotonous region.
|
||||
using MapType = std::pair<SegmentIntersection*, MonotonousRegion*>;
|
||||
// Map from low intersection to left / right side of a monotonic region.
|
||||
using MapType = std::pair<SegmentIntersection*, MonotonicRegion*>;
|
||||
std::vector<MapType> map_intersection_to_region_start;
|
||||
std::vector<MapType> map_intersection_to_region_end;
|
||||
map_intersection_to_region_start.reserve(regions.size());
|
||||
map_intersection_to_region_end.reserve(regions.size());
|
||||
for (MonotonousRegion ®ion : regions) {
|
||||
for (MonotonicRegion ®ion : regions) {
|
||||
map_intersection_to_region_start.emplace_back(&segs[region.left.vline].intersections[region.left.low], ®ion);
|
||||
map_intersection_to_region_end.emplace_back(&segs[region.right.vline].intersections[region.right.low], ®ion);
|
||||
}
|
||||
|
|
@ -1840,7 +1964,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, c
|
|||
std::sort(map_intersection_to_region_end.begin(), map_intersection_to_region_end.end(), intersections_lower);
|
||||
|
||||
// Scatter links to neighboring regions.
|
||||
for (MonotonousRegion ®ion : regions) {
|
||||
for (MonotonicRegion ®ion : regions) {
|
||||
if (region.left.vline > 0) {
|
||||
auto &vline = segs[region.left.vline];
|
||||
auto &vline_left = segs[region.left.vline - 1];
|
||||
|
|
@ -1884,17 +2008,17 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, c
|
|||
// Sometimes a segment may indicate that it connects to a segment on the other side while the other does not.
|
||||
// This may be a valid case if one side contains runs of OUTER_LOW, INNER_LOW, {INNER_HIGH, INNER_LOW}*, INNER_HIGH, OUTER_HIGH,
|
||||
// where the part in the middle does not connect to the other side, but it will be extruded through.
|
||||
for (MonotonousRegion ®ion : regions) {
|
||||
for (MonotonicRegion ®ion : regions) {
|
||||
std::sort(region.left_neighbors.begin(), region.left_neighbors.end());
|
||||
std::sort(region.right_neighbors.begin(), region.right_neighbors.end());
|
||||
}
|
||||
for (MonotonousRegion ®ion : regions) {
|
||||
for (MonotonousRegion *neighbor : region.left_neighbors) {
|
||||
for (MonotonicRegion ®ion : regions) {
|
||||
for (MonotonicRegion *neighbor : region.left_neighbors) {
|
||||
auto it = std::lower_bound(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), ®ion);
|
||||
if (it == neighbor->right_neighbors.end() || *it != ®ion)
|
||||
neighbor->right_neighbors.insert(it, ®ion);
|
||||
}
|
||||
for (MonotonousRegion *neighbor : region.right_neighbors) {
|
||||
for (MonotonicRegion *neighbor : region.right_neighbors) {
|
||||
auto it = std::lower_bound(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), ®ion);
|
||||
if (it == neighbor->left_neighbors.end() || *it != ®ion)
|
||||
neighbor->left_neighbors.insert(it, ®ion);
|
||||
|
|
@ -1903,12 +2027,12 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, c
|
|||
|
||||
#ifndef NDEBUG
|
||||
// Verify symmetry of the left_neighbors / right_neighbors.
|
||||
for (MonotonousRegion ®ion : regions) {
|
||||
for (MonotonousRegion *neighbor : region.left_neighbors) {
|
||||
for (MonotonicRegion ®ion : regions) {
|
||||
for (MonotonicRegion *neighbor : region.left_neighbors) {
|
||||
assert(std::count(region.left_neighbors.begin(), region.left_neighbors.end(), neighbor) == 1);
|
||||
assert(std::find(neighbor->right_neighbors.begin(), neighbor->right_neighbors.end(), ®ion) != neighbor->right_neighbors.end());
|
||||
}
|
||||
for (MonotonousRegion *neighbor : region.right_neighbors) {
|
||||
for (MonotonicRegion *neighbor : region.right_neighbors) {
|
||||
assert(std::count(region.right_neighbors.begin(), region.right_neighbors.end(), neighbor) == 1);
|
||||
assert(std::find(neighbor->left_neighbors.begin(), neighbor->left_neighbors.end(), ®ion) != neighbor->left_neighbors.end());
|
||||
}
|
||||
|
|
@ -1916,7 +2040,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, c
|
|||
#endif /* NDEBUG */
|
||||
|
||||
// Fill in sum length of connecting lines of a region. This length is used for optimizing the infill path for minimum length.
|
||||
for (MonotonousRegion ®ion : regions) {
|
||||
for (MonotonicRegion ®ion : regions) {
|
||||
region.len1 = montonous_region_path_length(region, false, poly_with_offset, segs);
|
||||
region.len2 = montonous_region_path_length(region, true, poly_with_offset, segs);
|
||||
// Subtract the smaller length from the longer one, so we will optimize just with the positive difference of the two.
|
||||
|
|
@ -1934,7 +2058,7 @@ static void connect_monotonous_regions(std::vector<MonotonousRegion> ®ions, c
|
|||
// https://www.chalmers.se/en/departments/math/research/research-groups/optimization/OptimizationMasterTheses/MScThesis-RaadSalman-final.pdf
|
||||
// Algorithm 6.1 Lexicographic Path Preserving 3-opt
|
||||
// Optimize path while maintaining the ordering constraints.
|
||||
void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, const std::vector<SegmentedIntersectionLine> &segs)
|
||||
void monotonic_3_opt(std::vector<MonotonicRegionLink> &path, const std::vector<SegmentedIntersectionLine> &segs)
|
||||
{
|
||||
// When doing the 3-opt path preserving flips, one has to fulfill two constraints:
|
||||
//
|
||||
|
|
@ -1949,7 +2073,7 @@ void monotonous_3_opt(std::vector<MonotonousRegionLink> &path, const std::vector
|
|||
// then the precedence constraint verification is amortized inside the O(n^3) loop. Now which is better for our task?
|
||||
//
|
||||
// It is beneficial to also try flipping of the infill zig-zags, for which a prefix sum of both flipped and non-flipped paths over
|
||||
// MonotonousRegionLinks may be utilized, however updating the prefix sum has a linear complexity, the same complexity as doing the 3-opt
|
||||
// MonotonicRegionLinks may be utilized, however updating the prefix sum has a linear complexity, the same complexity as doing the 3-opt
|
||||
// exchange by copying the pieces.
|
||||
}
|
||||
|
||||
|
|
@ -1962,17 +2086,17 @@ inline void print_ant(const std::string& fmt, TArgs&&... args) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// Find a run through monotonous infill blocks using an 'Ant colony" optimization method.
|
||||
// Find a run through monotonic infill blocks using an 'Ant colony" optimization method.
|
||||
// http://www.scholarpedia.org/article/Ant_colony_optimization
|
||||
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)
|
||||
static std::vector<MonotonicRegionLink> chain_monotonic_regions(
|
||||
std::vector<MonotonicRegion> ®ions, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, std::mt19937_64 &rng)
|
||||
{
|
||||
// Number of left neighbors (regions that this region depends on, this region cannot be printed before the regions left of it are printed) + self.
|
||||
std::vector<int32_t> left_neighbors_unprocessed(regions.size(), 1);
|
||||
// Queue of regions, which have their left neighbors already printed.
|
||||
std::vector<MonotonousRegion*> queue;
|
||||
std::vector<MonotonicRegion*> queue;
|
||||
queue.reserve(regions.size());
|
||||
for (MonotonousRegion ®ion : regions)
|
||||
for (MonotonicRegion ®ion : regions)
|
||||
if (region.left_neighbors.empty())
|
||||
queue.emplace_back(®ion);
|
||||
else
|
||||
|
|
@ -1981,13 +2105,13 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
auto left_neighbors_unprocessed_initial = left_neighbors_unprocessed;
|
||||
auto queue_initial = queue;
|
||||
|
||||
std::vector<MonotonousRegionLink> path, best_path;
|
||||
std::vector<MonotonicRegionLink> path, best_path;
|
||||
path.reserve(regions.size());
|
||||
best_path.reserve(regions.size());
|
||||
float best_path_length = std::numeric_limits<float>::max();
|
||||
|
||||
struct NextCandidate {
|
||||
MonotonousRegion *region;
|
||||
MonotonicRegion *region;
|
||||
AntPath *link;
|
||||
AntPath *link_flipped;
|
||||
float probability;
|
||||
|
|
@ -2002,22 +2126,22 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
[®ions, &left_neighbors_unprocessed, &path, &queue]() {
|
||||
std::vector<unsigned char> regions_processed(regions.size(), false);
|
||||
std::vector<unsigned char> regions_in_queue(regions.size(), false);
|
||||
for (const MonotonousRegion *region : queue) {
|
||||
for (const MonotonicRegion *region : queue) {
|
||||
// This region is not processed yet, his predecessors are processed.
|
||||
assert(left_neighbors_unprocessed[region - regions.data()] == 1);
|
||||
regions_in_queue[region - regions.data()] = true;
|
||||
}
|
||||
for (const MonotonousRegionLink &link : path) {
|
||||
for (const MonotonicRegionLink &link : path) {
|
||||
assert(left_neighbors_unprocessed[link.region - regions.data()] == 0);
|
||||
regions_processed[link.region - regions.data()] = true;
|
||||
}
|
||||
for (size_t i = 0; i < regions_processed.size(); ++ i) {
|
||||
assert(! regions_processed[i] || ! regions_in_queue[i]);
|
||||
const MonotonousRegion ®ion = regions[i];
|
||||
const MonotonicRegion ®ion = regions[i];
|
||||
if (regions_processed[i] || regions_in_queue[i]) {
|
||||
assert(left_neighbors_unprocessed[i] == (regions_in_queue[i] ? 1 : 0));
|
||||
// All left neighbors should be processed already.
|
||||
for (const MonotonousRegion *left : region.left_neighbors) {
|
||||
for (const MonotonicRegion *left : region.left_neighbors) {
|
||||
assert(regions_processed[left - regions.data()]);
|
||||
assert(left_neighbors_unprocessed[left - regions.data()] == 0);
|
||||
}
|
||||
|
|
@ -2026,7 +2150,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
assert(left_neighbors_unprocessed[i] > 1);
|
||||
size_t num_predecessors_unprocessed = 0;
|
||||
bool has_left_last_on_path = false;
|
||||
for (const MonotonousRegion* left : region.left_neighbors) {
|
||||
for (const MonotonicRegion* left : region.left_neighbors) {
|
||||
size_t iprev = left - regions.data();
|
||||
if (regions_processed[iprev]) {
|
||||
assert(left_neighbors_unprocessed[iprev] == 0);
|
||||
|
|
@ -2058,7 +2182,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
// After how many rounds without an improvement to exit?
|
||||
constexpr int num_rounds_no_change_exit = 8;
|
||||
// With how many ants each of the run will be performed?
|
||||
const int num_ants = std::min<int>(regions.size(), 10);
|
||||
const int num_ants = std::min(int(regions.size()), 10);
|
||||
// Base (initial) pheromone level. This value will be adjusted based on the length of the first greedy path found.
|
||||
float pheromone_initial_deposit = 0.5f;
|
||||
// Evaporation rate of pheromones.
|
||||
|
|
@ -2080,18 +2204,18 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
|
||||
assert(validate_unprocessed());
|
||||
// Pick the last of the queue.
|
||||
MonotonousRegionLink path_end { queue.back(), false };
|
||||
MonotonicRegionLink path_end { queue.back(), false };
|
||||
queue.pop_back();
|
||||
-- left_neighbors_unprocessed[path_end.region - regions.data()];
|
||||
|
||||
float total_length = path_end.region->length(false);
|
||||
while (! queue.empty() || ! path_end.region->right_neighbors.empty()) {
|
||||
// Chain.
|
||||
MonotonousRegion ®ion = *path_end.region;
|
||||
MonotonicRegion ®ion = *path_end.region;
|
||||
bool dir = path_end.flipped;
|
||||
NextCandidate next_candidate;
|
||||
next_candidate.probability = 0;
|
||||
for (MonotonousRegion *next : region.right_neighbors) {
|
||||
for (MonotonicRegion *next : region.right_neighbors) {
|
||||
int &unprocessed = left_neighbors_unprocessed[next - regions.data()];
|
||||
assert(unprocessed > 1);
|
||||
if (left_neighbors_unprocessed[next - regions.data()] == 2) {
|
||||
|
|
@ -2106,7 +2230,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
}
|
||||
bool from_queue = next_candidate.probability == 0;
|
||||
if (from_queue) {
|
||||
for (MonotonousRegion *next : queue) {
|
||||
for (MonotonicRegion *next : queue) {
|
||||
AntPath &path1 = path_matrix(region, dir, *next, false);
|
||||
AntPath &path2 = path_matrix(region, dir, *next, true);
|
||||
if (path1.visibility > next_candidate.probability)
|
||||
|
|
@ -2116,7 +2240,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
}
|
||||
}
|
||||
// Move the other right neighbors with satisified constraints to the queue.
|
||||
for (MonotonousRegion *next : region.right_neighbors)
|
||||
for (MonotonicRegion *next : region.right_neighbors)
|
||||
if (-- left_neighbors_unprocessed[next - regions.data()] == 1 && next_candidate.region != next)
|
||||
queue.emplace_back(next);
|
||||
if (from_queue) {
|
||||
|
|
@ -2127,7 +2251,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
queue.pop_back();
|
||||
}
|
||||
// Extend the path.
|
||||
MonotonousRegion *next_region = next_candidate.region;
|
||||
MonotonicRegion *next_region = next_candidate.region;
|
||||
bool next_dir = next_candidate.dir;
|
||||
total_length += next_region->length(next_dir) + path_matrix(*path_end.region, path_end.flipped, *next_region, next_dir).length;
|
||||
path_end = { next_region, next_dir };
|
||||
|
|
@ -2136,11 +2260,11 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
}
|
||||
|
||||
// Set an initial pheromone value to 10% of the greedy path's value.
|
||||
pheromone_initial_deposit = 0.1 / total_length;
|
||||
pheromone_initial_deposit = 0.1f / total_length;
|
||||
path_matrix.update_inital_pheromone(pheromone_initial_deposit);
|
||||
}
|
||||
|
||||
// Probability (unnormalized) of traversing a link between two monotonous regions.
|
||||
// Probability (unnormalized) of traversing a link between two monotonic regions.
|
||||
auto path_probability = [pheromone_alpha, pheromone_beta](AntPath &path) {
|
||||
return pow(path.pheromone, pheromone_alpha) * pow(path.visibility, pheromone_beta);
|
||||
};
|
||||
|
|
@ -2163,10 +2287,10 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
left_neighbors_unprocessed = left_neighbors_unprocessed_initial;
|
||||
assert(validate_unprocessed());
|
||||
// Pick randomly the first from the queue at random orientation.
|
||||
//FIXME picking the 1st monotonous region should likely be done based on accumulated pheromone level as well,
|
||||
// but the inefficiency caused by the random pick of the 1st monotonous region is likely insignificant.
|
||||
//FIXME picking the 1st monotonic region should likely be done based on accumulated pheromone level as well,
|
||||
// but the inefficiency caused by the random pick of the 1st monotonic region is likely insignificant.
|
||||
int first_idx = std::uniform_int_distribution<>(0, int(queue.size()) - 1)(rng);
|
||||
path.emplace_back(MonotonousRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
|
||||
path.emplace_back(MonotonicRegionLink{ queue[first_idx], rng() > rng.max() / 2 });
|
||||
*(queue.begin() + first_idx) = std::move(queue.back());
|
||||
queue.pop_back();
|
||||
-- left_neighbors_unprocessed[path.back().region - regions.data()];
|
||||
|
|
@ -2182,12 +2306,12 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
|
||||
while (! queue.empty() || ! path.back().region->right_neighbors.empty()) {
|
||||
// Chain.
|
||||
MonotonousRegion ®ion = *path.back().region;
|
||||
MonotonicRegion ®ion = *path.back().region;
|
||||
bool dir = path.back().flipped;
|
||||
// Sort by distance to pt.
|
||||
next_candidates.clear();
|
||||
next_candidates.reserve(region.right_neighbors.size() * 2);
|
||||
for (MonotonousRegion *next : region.right_neighbors) {
|
||||
for (MonotonicRegion *next : region.right_neighbors) {
|
||||
int &unprocessed = left_neighbors_unprocessed[next - regions.data()];
|
||||
assert(unprocessed > 1);
|
||||
if (-- unprocessed == 1) {
|
||||
|
|
@ -2204,7 +2328,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
//FIXME add the queue items to the candidates? These are valid moves as well.
|
||||
if (num_direct_neighbors == 0) {
|
||||
// Add the queue candidates.
|
||||
for (MonotonousRegion *next : queue) {
|
||||
for (MonotonicRegion *next : queue) {
|
||||
assert(left_neighbors_unprocessed[next - regions.data()] == 1);
|
||||
AntPath &path1 = path_matrix(region, dir, *next, false);
|
||||
AntPath &path1_flipped = path_matrix(region, ! dir, *next, true);
|
||||
|
|
@ -2247,11 +2371,11 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
queue.pop_back();
|
||||
}
|
||||
// Extend the path.
|
||||
MonotonousRegion *next_region = take_path->region;
|
||||
MonotonicRegion *next_region = take_path->region;
|
||||
bool next_dir = take_path->dir;
|
||||
path.back().next = take_path->link;
|
||||
path.back().next_flipped = take_path->link_flipped;
|
||||
path.emplace_back(MonotonousRegionLink{ next_region, next_dir });
|
||||
path.emplace_back(MonotonicRegionLink{ next_region, next_dir });
|
||||
assert(left_neighbors_unprocessed[next_region - regions.data()] == 1);
|
||||
left_neighbors_unprocessed[next_region - regions.data()] = 0;
|
||||
print_ant("\tRegion (%1%:%2%,%3%) (%4%:%5%,%6%) length to prev %7%",
|
||||
|
|
@ -2279,14 +2403,14 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
}
|
||||
|
||||
// Perform 3-opt local optimization of the path.
|
||||
monotonous_3_opt(path, segs);
|
||||
monotonic_3_opt(path, segs);
|
||||
|
||||
// Measure path length.
|
||||
assert(! path.empty());
|
||||
float path_length = std::accumulate(path.begin(), path.end() - 1,
|
||||
path.back().region->length(path.back().flipped),
|
||||
[&path_matrix](const float l, const MonotonousRegionLink &r) {
|
||||
const MonotonousRegionLink &next = *(&r + 1);
|
||||
[&path_matrix](const float l, const MonotonicRegionLink &r) {
|
||||
const MonotonicRegionLink &next = *(&r + 1);
|
||||
return l + r.region->length(r.flipped) + path_matrix(*r.region, r.flipped, *next.region, next.flipped).length;
|
||||
});
|
||||
// Save the shortest path.
|
||||
|
|
@ -2309,7 +2433,7 @@ static std::vector<MonotonousRegionLink> chain_monotonous_regions(
|
|||
// Reinforce the path pheromones with the best path.
|
||||
float total_cost = best_path_length + float(EPSILON);
|
||||
for (size_t i = 0; i + 1 < path.size(); ++ i) {
|
||||
MonotonousRegionLink &link = path[i];
|
||||
MonotonicRegionLink &link = path[i];
|
||||
link.next->pheromone = (1.f - pheromone_evaporation) * link.next->pheromone + pheromone_evaporation / total_cost;
|
||||
}
|
||||
|
||||
|
|
@ -2324,7 +2448,7 @@ end:
|
|||
}
|
||||
|
||||
// Traverse path, produce polylines.
|
||||
static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, Polylines &polylines_out)
|
||||
static void polylines_from_paths(const std::vector<MonotonicRegionLink> &path, const ExPolygonWithOffset &poly_with_offset, const std::vector<SegmentedIntersectionLine> &segs, Polylines &polylines_out)
|
||||
{
|
||||
Polyline *polyline = nullptr;
|
||||
auto finish_polyline = [&polyline, &polylines_out]() {
|
||||
|
|
@ -2334,14 +2458,26 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
|
|||
// Handle nearly zero length edges.
|
||||
if (polyline->points.size() <= 1 ||
|
||||
(polyline->points.size() == 2 &&
|
||||
std::abs(polyline->points.front()(0) - polyline->points.back()(0)) < SCALED_EPSILON &&
|
||||
std::abs(polyline->points.front()(1) - polyline->points.back()(1)) < SCALED_EPSILON))
|
||||
std::abs(polyline->points.front().x() - polyline->points.back().x()) < SCALED_EPSILON &&
|
||||
std::abs(polyline->points.front().y() - polyline->points.back().y()) < SCALED_EPSILON))
|
||||
polylines_out.pop_back();
|
||||
else if (polylines_out.size() >= 2) {
|
||||
assert(polyline->points.size() >= 2);
|
||||
// Merge the two last polylines. An extrusion may have been split by an introduction of phony outer points on intersection lines
|
||||
// to cope with pinching of inner offset contours.
|
||||
Polyline &pl_prev = polylines_out[polylines_out.size() - 2];
|
||||
if (std::abs(polyline->points.front().x() - pl_prev.points.back().x()) < SCALED_EPSILON &&
|
||||
std::abs(polyline->points.front().y() - pl_prev.points.back().y()) < SCALED_EPSILON) {
|
||||
pl_prev.points.back() = (pl_prev.points.back() + polyline->points.front()) / 2;
|
||||
pl_prev.points.insert(pl_prev.points.end(), polyline->points.begin() + 1, polyline->points.end());
|
||||
polylines_out.pop_back();
|
||||
}
|
||||
}
|
||||
polyline = nullptr;
|
||||
};
|
||||
|
||||
for (const MonotonousRegionLink &path_segment : path) {
|
||||
MonotonousRegion ®ion = *path_segment.region;
|
||||
for (const MonotonicRegionLink &path_segment : path) {
|
||||
MonotonicRegion ®ion = *path_segment.region;
|
||||
bool dir = path_segment.flipped;
|
||||
|
||||
// From the initial point (i_vline, i_intersection), follow a path.
|
||||
|
|
@ -2350,8 +2486,8 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
|
|||
|
||||
if (polyline != nullptr && &path_segment != path.data()) {
|
||||
// Connect previous path segment with the new one.
|
||||
const MonotonousRegionLink &path_segment_prev = *(&path_segment - 1);
|
||||
const MonotonousRegion ®ion_prev = *path_segment_prev.region;
|
||||
const MonotonicRegionLink &path_segment_prev = *(&path_segment - 1);
|
||||
const MonotonicRegion ®ion_prev = *path_segment_prev.region;
|
||||
bool dir_prev = path_segment_prev.flipped;
|
||||
int i_vline_prev = region_prev.right.vline;
|
||||
const SegmentedIntersectionLine &vline_prev = segs[i_vline_prev];
|
||||
|
|
@ -2456,7 +2592,7 @@ static void polylines_from_paths(const std::vector<MonotonousRegionLink> &path,
|
|||
|
||||
if (polyline != nullptr) {
|
||||
// Finish the current vertical line,
|
||||
const MonotonousRegion ®ion = *path.back().region;
|
||||
const MonotonicRegion ®ion = *path.back().region;
|
||||
const SegmentedIntersectionLine &vline = segs[region.right.vline];
|
||||
const SegmentIntersection *ip = &vline.intersections[region.right_intersection_point(path.back().flipped)];
|
||||
assert(ip->is_inner());
|
||||
|
|
@ -2489,7 +2625,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
|
|||
surface->expolygon,
|
||||
- rotate_vector.first,
|
||||
float(scale_(this->overlap - (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing)),
|
||||
float(scale_(this->overlap - 0.5 * this->spacing)));
|
||||
float(scale_(this->overlap - 0.5f * this->spacing)));
|
||||
if (poly_with_offset.n_contours_inner == 0) {
|
||||
// Not a single infill line fits.
|
||||
//FIXME maybe one shall trigger the gap fill here?
|
||||
|
|
@ -2558,14 +2694,18 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
|
|||
svg.Close();
|
||||
#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.
|
||||
bool monotonous_infill = params.monotonous; // || params.density > 0.99;
|
||||
if (monotonous_infill) {
|
||||
std::vector<MonotonousRegion> regions = generate_montonous_regions(segs);
|
||||
connect_monotonous_regions(regions, poly_with_offset, segs);
|
||||
//FIXME this is a hack to get the monotonic infill rolling. We likely want a smarter switch, likely based on user decison.
|
||||
bool monotonic_infill = params.monotonic; // || params.density > 0.99;
|
||||
if (monotonic_infill) {
|
||||
// Sometimes the outer contour pinches the inner contour from both sides along a single vertical line.
|
||||
// This situation is not handled correctly by generate_montonous_regions().
|
||||
// Insert phony OUTER_HIGH / OUTER_LOW pairs at the position where the contour is pinched.
|
||||
pinch_contours_insert_phony_outer_intersections(segs);
|
||||
std::vector<MonotonicRegion> regions = generate_montonous_regions(segs);
|
||||
connect_monotonic_regions(regions, poly_with_offset, segs);
|
||||
if (! regions.empty()) {
|
||||
std::mt19937_64 rng;
|
||||
std::vector<MonotonousRegionLink> path = chain_monotonous_regions(regions, poly_with_offset, segs, rng);
|
||||
std::vector<MonotonicRegionLink> path = chain_monotonic_regions(regions, poly_with_offset, segs, rng);
|
||||
polylines_from_paths(path, poly_with_offset, segs, polylines_out);
|
||||
}
|
||||
} else
|
||||
|
|
@ -2616,13 +2756,13 @@ Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParam
|
|||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines FillMonotonous::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
Polylines FillMonotonic::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
FillParams params2 = params;
|
||||
params2.monotonous = true;
|
||||
params2.monotonic = 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");
|
||||
printf("FillMonotonic::fill_surface() failed to fill a region.\n");
|
||||
}
|
||||
return polylines_out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,11 @@ protected:
|
|||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillMonotonous : public FillRectilinear2
|
||||
class FillMonotonic : public FillRectilinear2
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillMonotonous(*this); };
|
||||
virtual ~FillMonotonous() = default;
|
||||
virtual Fill* clone() const { return new FillMonotonic(*this); };
|
||||
virtual ~FillMonotonic() = default;
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
virtual bool no_sort() const { return true; }
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,83 +0,0 @@
|
|||
#ifndef slic3r_FillRectilinear3_hpp_
|
||||
#define slic3r_FillRectilinear3_hpp_
|
||||
|
||||
#include "../libslic3r.h"
|
||||
|
||||
#include "FillBase.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Surface;
|
||||
|
||||
class FillRectilinear3 : public Fill
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillRectilinear3(*this); };
|
||||
virtual ~FillRectilinear3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
struct FillDirParams
|
||||
{
|
||||
FillDirParams(coordf_t spacing, double angle, coordf_t pattern_shift = 0.f) :
|
||||
spacing(spacing), angle(angle), pattern_shift(pattern_shift) {}
|
||||
coordf_t spacing;
|
||||
double angle;
|
||||
coordf_t pattern_shift;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, std::vector<FillDirParams> &fill_dir_params, Polylines &polylines_out);
|
||||
};
|
||||
|
||||
class FillGrid3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillGrid3(*this); };
|
||||
virtual ~FillGrid3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t /* idx */) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillTriangles3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillTriangles3(*this); };
|
||||
virtual ~FillTriangles3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t /* idx */) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillStars3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillStars3(*this); };
|
||||
virtual ~FillStars3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t /* idx */) const { return 0.f; }
|
||||
};
|
||||
|
||||
class FillCubic3 : public FillRectilinear3
|
||||
{
|
||||
public:
|
||||
virtual Fill* clone() const { return new FillCubic3(*this); };
|
||||
virtual ~FillCubic3() {}
|
||||
virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
virtual float _layer_angle(size_t /* idx */) const { return 0.f; }
|
||||
};
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif // slic3r_FillRectilinear3_hpp_
|
||||
Loading…
Add table
Add a link
Reference in a new issue