WIP Refactoring of Layers: Sorting of infill extrusions into LayerIslands.

FIXME: Gap fill extrusions are currently not handled!
This commit is contained in:
Vojtech Bubnik 2022-11-02 17:20:23 +01:00
parent 409fae6183
commit 386cfae546
5 changed files with 175 additions and 25 deletions

View File

@ -244,6 +244,39 @@ auto cast(const BoundingBox3Base<Tin> &b)
b.max.template cast<Tout>()};
}
// Distance of a point to a bounding box. Zero inside and on the boundary, positive outside.
inline double bbox_point_distance(const BoundingBox &bbox, const Point &pt)
{
if (pt.x() < bbox.min.x())
return pt.y() < bbox.min.y() ? (bbox.min - pt).cast<double>().norm() :
pt.y() > bbox.max.y() ? (Point(bbox.min.x(), bbox.max.y()) - pt).cast<double>().norm() :
double(bbox.min.x() - pt.x());
else if (pt.x() > bbox.max.x())
return pt.y() < bbox.min.y() ? (Point(bbox.max.x(), bbox.min.y()) - pt).cast<double>().norm() :
pt.y() > bbox.max.y() ? (bbox.max - pt).cast<double>().norm() :
double(pt.x() - bbox.max.x());
else
return pt.y() < bbox.min.y() ? bbox.min.y() - pt.y() :
pt.y() > bbox.max.y() ? pt.y() - bbox.max.y() :
coord_t(0);
}
inline double bbox_point_distance_squared(const BoundingBox &bbox, const Point &pt)
{
if (pt.x() < bbox.min.x())
return pt.y() < bbox.min.y() ? (bbox.min - pt).cast<double>().squaredNorm() :
pt.y() > bbox.max.y() ? (Point(bbox.min.x(), bbox.max.y()) - pt).cast<double>().squaredNorm() :
Slic3r::sqr(double(bbox.min.x() - pt.x()));
else if (pt.x() > bbox.max.x())
return pt.y() < bbox.min.y() ? (Point(bbox.max.x(), bbox.min.y()) - pt).cast<double>().squaredNorm() :
pt.y() > bbox.max.y() ? (bbox.max - pt).cast<double>().squaredNorm() :
Slic3r::sqr<double>(pt.x() - bbox.max.x());
else
return Slic3r::sqr<double>(pt.y() < bbox.min.y() ? bbox.min.y() - pt.y() :
pt.y() > bbox.max.y() ? pt.y() - bbox.max.y() :
coord_t(0));
}
} // namespace Slic3r
// Serialization through the Cereal library

View File

@ -117,6 +117,26 @@ ExPolygon::has_boundary_point(const Point &point) const
return false;
}
// Projection of a point onto the polygon.
Point ExPolygon::point_projection(const Point &point) const
{
if (this->holes.empty()) {
return this->contour.point_projection(point);
} else {
double dist_min2 = std::numeric_limits<double>::max();
Point closest_pt_min;
for (size_t i = 0; i < this->num_contours(); ++ i) {
Point closest_pt = this->contour_or_hole(i).point_projection(point);
double d2 = (closest_pt - point).cast<double>().squaredNorm();
if (d2 < dist_min2) {
dist_min2 = d2;
closest_pt_min = closest_pt;
}
}
return closest_pt_min;
}
}
bool ExPolygon::overlaps(const ExPolygon &other) const
{
#if 0

View File

@ -53,6 +53,8 @@ public:
bool contains(const Point &point) const;
bool contains_b(const Point &point) const;
bool has_boundary_point(const Point &point) const;
// Projection of a point onto the polygon.
Point point_projection(const Point &point) const;
// Does this expolygon overlap another expolygon?
// Either the ExPolygons intersect, or one is fully inside the other,

View File

@ -322,6 +322,92 @@ void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill>
}
#endif
static void insert_fills_into_islands(Layer &layer, uint32_t fill_region_id, uint32_t fill_begin, uint32_t fill_end)
{
if (fill_begin < fill_end) {
// Sort the extrusion range into its LayerIsland.
// Traverse the slices in an increasing order of bounding box size, so that the islands inside another islands are tested first,
// so we can just test a point inside ExPolygon::contour and we may skip testing the holes.
auto point_inside_surface = [&layer](const size_t lslice_idx, const Point &point) {
const BoundingBox &bbox = layer.lslices_ex[lslice_idx].bbox;
return point.x() >= bbox.min.x() && point.x() < bbox.max.x() &&
point.y() >= bbox.min.y() && point.y() < bbox.max.y() &&
layer.lslices[lslice_idx].contour.contains(point);
};
Point point = layer.get_region(fill_region_id)->fills().entities[fill_begin]->first_point();
int lslice_idx = int(layer.lslices_ex.size()) - 1;
for (; lslice_idx >= 0; -- lslice_idx)
if (point_inside_surface(lslice_idx, point))
break;
assert(lslice_idx >= 0);
if (lslice_idx >= 0) {
LayerSlice &lslice = layer.lslices_ex[lslice_idx];
// Find an island.
LayerIsland *island = nullptr;
if (lslice.islands.size() == 1) {
// Cool, just save the extrusions in there.
island = &lslice.islands.front();
} else {
// The infill was created for one of the infills.
// In case of ironing, the infill may not fall into any of the infill expolygons either.
// In case of some numerical error, the infill may not fall into any of the infill expolygons either.
// 1) Try an exact test, it should be cheaper than a closest region test.
for (LayerIsland &li : lslice.islands) {
const BoundingBoxes &bboxes = li.fill_expolygons_composite() ?
layer.get_region(li.perimeters.region())->fill_expolygons_composite_bboxes() :
layer.get_region(li.fill_region_id)->fill_expolygons_bboxes();
const ExPolygons &expolygons = li.fill_expolygons_composite() ?
layer.get_region(li.perimeters.region())->fill_expolygons_composite() :
layer.get_region(li.fill_region_id)->fill_expolygons();
for (uint32_t fill_expolygon_id : li.fill_expolygons)
if (bboxes[fill_expolygon_id].contains(point) && expolygons[fill_expolygon_id].contains(point)) {
island = &li;
goto found;
}
}
// 2) Find closest fill_expolygon, branch and bound by distance to bounding box.
{
struct Island {
uint32_t island_idx;
uint32_t expolygon_idx;
double distance2;
};
std::vector<Island> islands_sorted;
for (uint32_t island_idx = 0; island_idx < uint32_t(lslice.islands.size()); ++ island_idx) {
const LayerIsland &li = lslice.islands[island_idx];
const BoundingBoxes &bboxes = li.fill_expolygons_composite() ?
layer.get_region(li.perimeters.region())->fill_expolygons_composite_bboxes() :
layer.get_region(li.fill_region_id)->fill_expolygons_bboxes();
for (uint32_t fill_expolygon_id : li.fill_expolygons)
islands_sorted.push_back({ island_idx, fill_expolygon_id, bbox_point_distance_squared(bboxes[fill_expolygon_id], point) });
}
std::sort(islands_sorted.begin(), islands_sorted.end(), [](auto &l, auto &r){ return l.distance2 < r.distance2; });
auto dist_min2 = std::numeric_limits<double>::max();
for (uint32_t sorted_bbox_idx = 0; sorted_bbox_idx < uint32_t(islands_sorted.size()); ++ sorted_bbox_idx) {
const Island &isl = islands_sorted[sorted_bbox_idx];
if (isl.distance2 > dist_min2)
// Branch & bound condition.
break;
LayerIsland &li = lslice.islands[isl.island_idx];
const ExPolygons &expolygons = li.fill_expolygons_composite() ?
layer.get_region(li.perimeters.region())->fill_expolygons_composite() :
layer.get_region(li.fill_region_id)->fill_expolygons();
double d2 = (expolygons[isl.expolygon_idx].point_projection(point) - point).cast<double>().squaredNorm();
if (d2 < dist_min2) {
dist_min2 = d2;
island = &li;
}
}
}
found:;
}
assert(island);
if (island)
island->fills.push_back(LayerExtrusionRange{ fill_region_id, { fill_begin, fill_end }});
}
}
}
// friend to Layer
void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator)
{
@ -382,6 +468,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter()) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
LayerRegion &layerm = *m_regions[surface_fill.region_id];
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
params.density = float(0.01 * surface_fill.params.density);
@ -390,7 +478,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution;
params.use_arachne = perimeter_generator == PerimeterGeneratorType::Arachne && surface_fill.params.pattern == ipConcentric;
params.layer_height = m_regions[surface_fill.region_id]->layer()->height;
params.layer_height = layerm.layer()->height;
for (ExPolygon &expoly : surface_fill.expolygons) {
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
@ -421,7 +509,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
}
// Save into layer.
ExtrusionEntityCollection* eec = nullptr;
m_regions[surface_fill.region_id]->m_fills.entities.push_back(eec = new ExtrusionEntityCollection());
auto fill_begin = uint32_t(layerm.fills().size());
layerm.m_fills.entities.push_back(eec = new ExtrusionEntityCollection());
// Only concentric fills are not sorted.
eec->no_sort = f->no_sort();
if (params.use_arachne) {
@ -445,10 +534,14 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
surface_fill.params.extrusion_role,
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height());
}
insert_fills_into_islands(*this, uint32_t(surface_fill.region_id), fill_begin, uint32_t(layerm.fills().size()));
}
}
}
//FIXME Don't copy thin fill extrusions into fills, just use these thin fill extrusions
// from the G-code export directly.
#if 0
// add thin fill regions
// Unpacks the collection, creates multiple collections per path.
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
@ -459,6 +552,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
layerm->m_fills.entities.push_back(&collection);
collection.entities.push_back(thin_fill->clone());
}
#endif
#ifndef NDEBUG
for (LayerRegion *layerm : m_regions)
@ -519,7 +613,8 @@ void Layer::make_ironing()
this->angle == rhs.angle;
}
LayerRegion *layerm = nullptr;
LayerRegion *layerm;
uint32_t region_id;
// IdeaMaker: ironing
// ironing flowrate (5% percent)
@ -539,8 +634,8 @@ void Layer::make_ironing()
std::vector<IroningParams> by_extruder;
double default_layer_height = this->object()->config().layer_height;
for (LayerRegion *layerm : m_regions)
if (! layerm->slices().empty()) {
for (uint32_t region_id = 0; region_id < uint32_t(this->regions().size()); ++region_id)
if (LayerRegion *layerm = this->get_region(region_id); ! layerm->slices().empty()) {
IroningParams ironing_params;
const PrintRegionConfig &config = layerm->region().config();
if (config.ironing &&
@ -564,6 +659,7 @@ void Layer::make_ironing()
ironing_params.speed = config.ironing_speed;
ironing_params.angle = config.fill_angle * M_PI / 180.;
ironing_params.layerm = layerm;
ironing_params.region_id = region_id;
by_extruder.emplace_back(ironing_params);
}
}
@ -659,6 +755,7 @@ void Layer::make_ironing()
}
if (! polylines.empty()) {
// Save into layer.
auto fill_begin = uint32_t(ironing_params.layerm->fills().size());
ExtrusionEntityCollection *eec = nullptr;
ironing_params.layerm->m_fills.entities.push_back(eec = new ExtrusionEntityCollection());
// Don't sort the ironing infill lines as they are monotonicly ordered.
@ -667,6 +764,7 @@ void Layer::make_ironing()
eec->entities, std::move(polylines),
erIroning,
flow_mm3_per_mm, extrusion_width, float(extrusion_height));
insert_fills_into_islands(*this, ironing_params.region_id, fill_begin, uint32_t(ironing_params.layerm->fills().size()));
}
}

View File

@ -32,35 +32,33 @@ namespace FillLightning {
template<typename T>
class IndexRange
{
private:
// Just a bare minimum functionality iterator required by range-for loop.
template<typename T>
class IteratorType {
public:
T operator*() const { return m_idx; }
bool operator!=(const IteratorType &rhs) const { return m_idx != rhs.m_idx; }
void operator++() { ++ m_idx; }
private:
friend class IndexRange<T>;
IteratorType(T idx) : m_idx(idx) {}
T m_idx;
};
// Index of the first extrusion in LayerRegion.
T m_begin { 0 };
// Index of the last extrusion in LayerRegion.
T m_end { 0 };
public:
IndexRange(T ibegin, T iend) : m_begin(ibegin), m_end(iend) {}
IndexRange() = default;
using Iterator = IteratorType<T>;
// Just a bare minimum functionality iterator required by range-for loop.
class Iterator {
public:
T operator*() const { return m_idx; }
bool operator!=(const Iterator &rhs) const { return m_idx != rhs.m_idx; }
void operator++() { ++ m_idx; }
private:
friend class IndexRange<T>;
Iterator(T idx) : m_idx(idx) {}
T m_idx;
};
Iterator begin() const { assert(m_begin <= m_end); return Iterator(m_begin); };
Iterator end() const { assert(m_begin <= m_end); return Iterator(m_end); };
bool empty() const { assert(m_begin <= m_end); return m_begin >= m_end; }
T size() const { assert(m_begin <= m_end); return m_end - m_begin; }
private:
// Index of the first extrusion in LayerRegion.
T m_begin { 0 };
// Index of the last extrusion in LayerRegion.
T m_end { 0 };
};
using ExtrusionRange = IndexRange<uint32_t>;
@ -70,7 +68,6 @@ using ExPolygonRange = IndexRange<uint32_t>;
class LayerExtrusionRange : public ExtrusionRange
{
public:
LayerExtrusionRange(uint32_t iregion, uint32_t ibegin, uint32_t iend) : m_region(iregion), ExtrusionRange(ibegin, iend) {}
LayerExtrusionRange(uint32_t iregion, ExtrusionRange extrusion_range) : m_region(iregion), ExtrusionRange(extrusion_range) {}
LayerExtrusionRange() = default;