2014-05-06 08:07:18 +00:00
|
|
|
#include "Layer.hpp"
|
2014-08-03 16:41:09 +00:00
|
|
|
#include "ClipperUtils.hpp"
|
|
|
|
#include "Geometry.hpp"
|
|
|
|
#include "Print.hpp"
|
2016-11-02 09:47:00 +00:00
|
|
|
#include "Fill/Fill.hpp"
|
2016-09-26 11:44:23 +00:00
|
|
|
#include "SVG.hpp"
|
2014-05-06 08:07:18 +00:00
|
|
|
|
2017-03-03 11:53:05 +00:00
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
|
2014-05-06 08:07:18 +00:00
|
|
|
namespace Slic3r {
|
|
|
|
|
|
|
|
Layer::~Layer()
|
|
|
|
{
|
2017-05-30 16:33:17 +00:00
|
|
|
this->lower_layer = this->upper_layer = nullptr;
|
2018-09-11 12:04:47 +00:00
|
|
|
for (LayerRegion *region : m_regions)
|
2017-05-30 16:33:17 +00:00
|
|
|
delete region;
|
2018-09-11 12:04:47 +00:00
|
|
|
m_regions.clear();
|
2014-05-06 08:07:18 +00:00
|
|
|
}
|
|
|
|
|
2018-12-14 16:17:51 +00:00
|
|
|
// Test whether whether there are any slices assigned to this layer.
|
|
|
|
bool Layer::empty() const
|
|
|
|
{
|
|
|
|
for (const LayerRegion *layerm : m_regions)
|
|
|
|
if (layerm != nullptr && ! layerm->slices.empty())
|
|
|
|
// Non empty layer.
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-05-30 16:33:17 +00:00
|
|
|
LayerRegion* Layer::add_region(PrintRegion* print_region)
|
2014-05-06 08:07:18 +00:00
|
|
|
{
|
2018-09-11 12:04:47 +00:00
|
|
|
m_regions.emplace_back(new LayerRegion(this, print_region));
|
|
|
|
return m_regions.back();
|
2014-05-06 08:07:18 +00:00
|
|
|
}
|
|
|
|
|
2014-08-03 16:41:09 +00:00
|
|
|
// merge all regions' slices to get islands
|
2017-05-30 16:33:17 +00:00
|
|
|
void Layer::make_slices()
|
2014-08-03 16:41:09 +00:00
|
|
|
{
|
|
|
|
ExPolygons slices;
|
2018-09-11 12:04:47 +00:00
|
|
|
if (m_regions.size() == 1) {
|
2014-08-03 16:41:09 +00:00
|
|
|
// optimization: if we only have one region, take its slices
|
2018-09-11 12:04:47 +00:00
|
|
|
slices = m_regions.front()->slices;
|
2014-08-03 16:41:09 +00:00
|
|
|
} else {
|
|
|
|
Polygons slices_p;
|
2018-11-02 19:41:49 +00:00
|
|
|
for (LayerRegion *layerm : m_regions)
|
|
|
|
polygons_append(slices_p, to_polygons(layerm->slices));
|
2016-12-13 18:22:23 +00:00
|
|
|
slices = union_ex(slices_p);
|
2014-08-03 16:41:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this->slices.expolygons.clear();
|
|
|
|
this->slices.expolygons.reserve(slices.size());
|
|
|
|
|
|
|
|
// prepare ordering points
|
|
|
|
Points ordering_points;
|
|
|
|
ordering_points.reserve(slices.size());
|
2017-04-05 06:59:03 +00:00
|
|
|
for (const ExPolygon &ex : slices)
|
|
|
|
ordering_points.push_back(ex.contour.first_point());
|
2014-08-03 16:41:09 +00:00
|
|
|
|
|
|
|
// sort slices
|
|
|
|
std::vector<Points::size_type> order;
|
|
|
|
Slic3r::Geometry::chained_path(ordering_points, order);
|
|
|
|
|
|
|
|
// populate slices vector
|
2017-04-05 06:59:03 +00:00
|
|
|
for (size_t i : order)
|
2018-11-02 19:41:49 +00:00
|
|
|
this->slices.expolygons.push_back(std::move(slices[i]));
|
2014-08-03 16:41:09 +00:00
|
|
|
}
|
|
|
|
|
2019-03-05 10:54:04 +00:00
|
|
|
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
|
2017-05-30 16:33:17 +00:00
|
|
|
void Layer::merge_slices()
|
2015-04-16 18:44:55 +00:00
|
|
|
{
|
2018-09-11 12:04:47 +00:00
|
|
|
if (m_regions.size() == 1) {
|
2016-11-20 11:38:59 +00:00
|
|
|
// Optimization, also more robust. Don't merge classified pieces of layerm->slices,
|
|
|
|
// but use the non-split islands of a layer. For a single region print, these shall be equal.
|
2018-09-11 12:04:47 +00:00
|
|
|
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
|
2016-11-20 11:38:59 +00:00
|
|
|
} else {
|
2018-11-02 19:41:49 +00:00
|
|
|
for (LayerRegion *layerm : m_regions)
|
2016-11-20 11:38:59 +00:00
|
|
|
// without safety offset, artifacts are generated (GH #2494)
|
2018-11-02 19:41:49 +00:00
|
|
|
layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal);
|
2015-04-16 18:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 10:54:04 +00:00
|
|
|
ExPolygons Layer::merged(float offset_scaled) const
|
|
|
|
{
|
|
|
|
assert(offset_scaled >= 0.f);
|
|
|
|
// If no offset is set, apply EPSILON offset before union, and revert it afterwards.
|
|
|
|
float offset_scaled2 = 0;
|
|
|
|
if (offset_scaled == 0.f) {
|
|
|
|
offset_scaled = float( EPSILON);
|
|
|
|
offset_scaled2 = float(- EPSILON);
|
|
|
|
}
|
|
|
|
Polygons polygons;
|
|
|
|
for (LayerRegion *layerm : m_regions)
|
|
|
|
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
|
|
|
|
ExPolygons out = union_ex(polygons);
|
|
|
|
if (offset_scaled2 != 0.f)
|
|
|
|
out = offset_ex(out, offset_scaled2);
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2016-09-13 11:30:00 +00:00
|
|
|
// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters.
|
|
|
|
// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region.
|
|
|
|
// The resulting fill surface is split back among the originating regions.
|
2017-05-30 16:33:17 +00:00
|
|
|
void Layer::make_perimeters()
|
2015-12-02 18:32:57 +00:00
|
|
|
{
|
2017-03-03 11:53:05 +00:00
|
|
|
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id();
|
2015-12-02 18:32:57 +00:00
|
|
|
|
|
|
|
// keep track of regions whose perimeters we have already generated
|
2019-03-06 09:21:10 +00:00
|
|
|
std::vector<unsigned char> done(m_regions.size(), false);
|
2015-12-02 18:32:57 +00:00
|
|
|
|
2018-11-02 19:41:49 +00:00
|
|
|
for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) {
|
2018-09-11 12:04:47 +00:00
|
|
|
size_t region_id = layerm - m_regions.begin();
|
2019-03-06 09:21:10 +00:00
|
|
|
if (done[region_id])
|
|
|
|
continue;
|
2017-03-03 11:53:05 +00:00
|
|
|
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
|
2019-03-06 09:21:10 +00:00
|
|
|
done[region_id] = true;
|
2018-09-11 12:04:47 +00:00
|
|
|
const PrintRegionConfig &config = (*layerm)->region()->config();
|
2015-12-02 18:32:57 +00:00
|
|
|
|
|
|
|
// find compatible regions
|
|
|
|
LayerRegionPtrs layerms;
|
|
|
|
layerms.push_back(*layerm);
|
2018-09-11 12:04:47 +00:00
|
|
|
for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) {
|
2015-12-02 18:32:57 +00:00
|
|
|
LayerRegion* other_layerm = *it;
|
2018-09-11 12:04:47 +00:00
|
|
|
const PrintRegionConfig &other_config = other_layerm->region()->config();
|
2015-12-02 18:32:57 +00:00
|
|
|
|
|
|
|
if (config.perimeter_extruder == other_config.perimeter_extruder
|
|
|
|
&& config.perimeters == other_config.perimeters
|
|
|
|
&& config.perimeter_speed == other_config.perimeter_speed
|
2017-07-14 15:30:54 +00:00
|
|
|
&& config.external_perimeter_speed == other_config.external_perimeter_speed
|
2015-12-02 18:32:57 +00:00
|
|
|
&& config.gap_fill_speed == other_config.gap_fill_speed
|
|
|
|
&& config.overhangs == other_config.overhangs
|
2019-06-26 11:26:49 +00:00
|
|
|
&& config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width")
|
2015-12-02 18:32:57 +00:00
|
|
|
&& config.thin_walls == other_config.thin_walls
|
|
|
|
&& config.external_perimeters_first == other_config.external_perimeters_first) {
|
|
|
|
layerms.push_back(other_layerm);
|
2019-03-06 09:21:10 +00:00
|
|
|
done[it - m_regions.begin()] = true;
|
2015-12-02 18:32:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (layerms.size() == 1) { // optimization
|
|
|
|
(*layerm)->fill_surfaces.surfaces.clear();
|
2016-11-17 22:22:59 +00:00
|
|
|
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces);
|
|
|
|
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
|
2015-12-02 18:32:57 +00:00
|
|
|
} else {
|
|
|
|
SurfaceCollection new_slices;
|
2016-11-17 22:22:59 +00:00
|
|
|
{
|
|
|
|
// group slices (surfaces) according to number of extra perimeters
|
2019-03-06 09:21:10 +00:00
|
|
|
std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ]
|
|
|
|
for (LayerRegion *layerm : layerms)
|
|
|
|
for (Surface &surface : layerm->slices.surfaces)
|
|
|
|
slices[surface.extra_perimeters].emplace_back(surface);
|
2016-11-17 22:22:59 +00:00
|
|
|
// merge the surfaces assigned to each group
|
2019-03-06 09:21:10 +00:00
|
|
|
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
|
|
|
|
new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front());
|
2015-12-02 18:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// make perimeters
|
|
|
|
SurfaceCollection fill_surfaces;
|
2016-11-17 22:22:59 +00:00
|
|
|
(*layerm)->make_perimeters(new_slices, &fill_surfaces);
|
2016-09-26 11:44:23 +00:00
|
|
|
|
2015-12-02 18:32:57 +00:00
|
|
|
// assign fill_surfaces to each layer
|
2016-09-26 11:44:23 +00:00
|
|
|
if (!fill_surfaces.surfaces.empty()) {
|
2015-12-02 18:32:57 +00:00
|
|
|
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
|
2016-09-26 11:44:23 +00:00
|
|
|
// Separate the fill surfaces.
|
2016-11-17 22:22:59 +00:00
|
|
|
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
|
|
|
|
(*l)->fill_expolygons = expp;
|
2018-11-02 19:45:23 +00:00
|
|
|
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
|
2015-12-02 18:32:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-03-03 11:53:05 +00:00
|
|
|
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done";
|
2015-12-02 18:32:57 +00:00
|
|
|
}
|
|
|
|
|
2016-11-02 09:47:00 +00:00
|
|
|
void Layer::make_fills()
|
|
|
|
{
|
|
|
|
#ifdef SLIC3R_DEBUG
|
|
|
|
printf("Making fills for layer " PRINTF_ZU "\n", this->id());
|
|
|
|
#endif
|
2018-09-11 12:04:47 +00:00
|
|
|
for (LayerRegion *layerm : m_regions) {
|
2018-03-28 15:05:31 +00:00
|
|
|
layerm->fills.clear();
|
|
|
|
make_fill(*layerm, layerm->fills);
|
2016-11-02 09:47:00 +00:00
|
|
|
#ifndef NDEBUG
|
2018-09-21 19:16:09 +00:00
|
|
|
for (size_t i = 0; i < layerm->fills.entities.size(); ++ i)
|
2018-03-28 15:05:31 +00:00
|
|
|
assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL);
|
2016-11-02 09:47:00 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-14 11:15:32 +00:00
|
|
|
void Layer::export_region_slices_to_svg(const char *path) const
|
2016-09-26 11:44:23 +00:00
|
|
|
{
|
|
|
|
BoundingBox bbox;
|
2018-09-11 12:04:47 +00:00
|
|
|
for (const auto *region : m_regions)
|
|
|
|
for (const auto &surface : region->slices.surfaces)
|
|
|
|
bbox.merge(get_extents(surface.expolygon));
|
2016-09-26 11:44:23 +00:00
|
|
|
Point legend_size = export_surface_type_legend_to_svg_box_size();
|
2018-08-17 13:53:43 +00:00
|
|
|
Point legend_pos(bbox.min(0), bbox.max(1));
|
|
|
|
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
|
2016-09-26 11:44:23 +00:00
|
|
|
|
|
|
|
SVG svg(path, bbox);
|
|
|
|
const float transparency = 0.5f;
|
2018-09-11 12:04:47 +00:00
|
|
|
for (const auto *region : m_regions)
|
|
|
|
for (const auto &surface : region->slices.surfaces)
|
|
|
|
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
|
2016-09-26 11:44:23 +00:00
|
|
|
export_surface_type_legend_to_svg(svg, legend_pos);
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
|
2017-09-14 11:15:32 +00:00
|
|
|
void Layer::export_region_slices_to_svg_debug(const char *name) const
|
2016-09-26 11:44:23 +00:00
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
this->export_region_slices_to_svg(debug_out_path("Layer-slices-%s-%d.svg", name, idx ++).c_str());
|
2016-09-26 11:44:23 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 11:15:32 +00:00
|
|
|
void Layer::export_region_fill_surfaces_to_svg(const char *path) const
|
2016-09-26 11:44:23 +00:00
|
|
|
{
|
|
|
|
BoundingBox bbox;
|
2018-09-11 12:04:47 +00:00
|
|
|
for (const auto *region : m_regions)
|
|
|
|
for (const auto &surface : region->slices.surfaces)
|
|
|
|
bbox.merge(get_extents(surface.expolygon));
|
2016-09-26 11:44:23 +00:00
|
|
|
Point legend_size = export_surface_type_legend_to_svg_box_size();
|
2018-08-17 13:53:43 +00:00
|
|
|
Point legend_pos(bbox.min(0), bbox.max(1));
|
|
|
|
bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
|
2016-09-26 11:44:23 +00:00
|
|
|
|
|
|
|
SVG svg(path, bbox);
|
|
|
|
const float transparency = 0.5f;
|
2018-09-11 12:04:47 +00:00
|
|
|
for (const auto *region : m_regions)
|
|
|
|
for (const auto &surface : region->slices.surfaces)
|
|
|
|
svg.draw(surface.expolygon, surface_type_to_color_name(surface.surface_type), transparency);
|
2016-09-26 11:44:23 +00:00
|
|
|
export_surface_type_legend_to_svg(svg, legend_pos);
|
|
|
|
svg.Close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Export to "out/LayerRegion-name-%d.svg" with an increasing index with every export.
|
2017-09-14 11:15:32 +00:00
|
|
|
void Layer::export_region_fill_surfaces_to_svg_debug(const char *name) const
|
2016-09-26 11:44:23 +00:00
|
|
|
{
|
|
|
|
static size_t idx = 0;
|
2016-10-21 08:18:01 +00:00
|
|
|
this->export_region_fill_surfaces_to_svg(debug_out_path("Layer-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
|
2016-09-26 11:44:23 +00:00
|
|
|
}
|
2014-05-06 08:07:18 +00:00
|
|
|
|
|
|
|
}
|