WIP Refactoring of Layers: Sorting of infill extrusions into LayerIslands.
FIXME: Gap fill extrusions are currently not handled!
This commit is contained in:
parent
409fae6183
commit
386cfae546
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user