FDM Print refactoring:

Layer newly remembers bounding boxes of slices,
the bounding boxes are used by G-code generator & newly the support
generator.
Slices are stored as ExPolygons, not ExPolygonCollection.
This commit is contained in:
bubnikv 2019-10-01 17:17:08 +02:00
parent 0e325824b1
commit 564eddd99d
12 changed files with 70 additions and 166 deletions

View File

@ -13,12 +13,12 @@ typedef std::vector<ExPolygonCollection> ExPolygonCollections;
class ExPolygonCollection
{
public:
public:
ExPolygons expolygons;
ExPolygonCollection() {};
ExPolygonCollection() {}
ExPolygonCollection(const ExPolygon &expolygon);
ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {};
ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}
operator Points() const;
operator Polygons() const;
operator ExPolygons&();

View File

@ -117,11 +117,11 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
const Layer* layer1 = object->layers()[i * 2];
const Layer* layer2 = object->layers()[i * 2 + 1];
Polygons polys;
polys.reserve(layer1->slices.expolygons.size() + layer2->slices.expolygons.size());
for (const ExPolygon &expoly : layer1->slices.expolygons)
polys.reserve(layer1->slices.size() + layer2->slices.size());
for (const ExPolygon &expoly : layer1->slices)
//FIXME no holes?
polys.emplace_back(expoly.contour);
for (const ExPolygon &expoly : layer2->slices.expolygons)
for (const ExPolygon &expoly : layer2->slices)
//FIXME no holes?
polys.emplace_back(expoly.contour);
polygons_per_layer[i] = union_(polys);
@ -130,8 +130,8 @@ Polygons AvoidCrossingPerimeters::collect_contours_all_layers(const PrintObjectP
if (object->layers().size() & 1) {
const Layer *layer = object->layers().back();
Polygons polys;
polys.reserve(layer->slices.expolygons.size());
for (const ExPolygon &expoly : layer->slices.expolygons)
polys.reserve(layer->slices.size());
for (const ExPolygon &expoly : layer->slices)
//FIXME no holes?
polys.emplace_back(expoly.contour);
polygons_per_layer.back() = union_(polys);
@ -1802,11 +1802,8 @@ void GCode::process_layer(
// - for each island, we extrude perimeters first, unless user set the infill_first
// option
// (Still, we have to keep track of regions because we need to apply their config)
size_t n_slices = layer.slices.expolygons.size();
std::vector<BoundingBox> layer_surface_bboxes;
layer_surface_bboxes.reserve(n_slices);
for (const ExPolygon &expoly : layer.slices.expolygons)
layer_surface_bboxes.push_back(get_extents(expoly.contour));
size_t n_slices = layer.slices.size();
const std::vector<BoundingBox> &layer_surface_bboxes = layer.slices_bboxes;
// 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.
std::vector<size_t> slices_test_order;
@ -1822,7 +1819,7 @@ void GCode::process_layer(
const BoundingBox &bbox = layer_surface_bboxes[i];
return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
point(1) >= bbox.min(1) && point(1) < bbox.max(1) &&
layer.slices.expolygons[i].contour.contains(point);
layer.slices[i].contour.contains(point);
};
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
@ -2418,7 +2415,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
static int iRun = 0;
SVG svg(debug_out_path("GCode_extrude_loop-%d.svg", iRun ++));
if (m_layer->lower_layer != NULL)
svg.draw(m_layer->lower_layer->slices.expolygons);
svg.draw(m_layer->lower_layer->slices);
for (size_t i = 0; i < loop.paths.size(); ++ i)
svg.draw(loop.paths[i].as_polyline(), "red");
Polylines polylines;

View File

@ -47,8 +47,8 @@ void Layer::make_slices()
slices = union_ex(slices_p);
}
this->slices.expolygons.clear();
this->slices.expolygons.reserve(slices.size());
this->slices.clear();
this->slices.reserve(slices.size());
// prepare ordering points
Points ordering_points;
@ -61,7 +61,7 @@ void Layer::make_slices()
// populate slices vector
for (size_t i : order)
this->slices.expolygons.push_back(std::move(slices[i]));
this->slices.push_back(std::move(slices[i]));
}
// Merge typed slices into untyped slices. This method is used to revert the effects of detect_surfaces_type() called for posPrepareInfill.
@ -70,7 +70,7 @@ void Layer::merge_slices()
if (m_regions.size() == 1) {
// 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.
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
m_regions.front()->slices.set(this->slices, stInternal);
} else {
for (LayerRegion *layerm : m_regions)
// without safety offset, artifacts are generated (GH #2494)

View File

@ -110,7 +110,8 @@ public:
// also known as 'islands' (all regions and surface types are merged here)
// The slices are chained by the shortest traverse distance and this traversal
// order will be recovered by the G-code generator.
ExPolygonCollection slices;
ExPolygons slices;
std::vector<BoundingBox> slices_bboxes;
size_t region_count() const { return m_regions.size(); }
const LayerRegion* get_region(int idx) const { return m_regions.at(idx); }

View File

@ -140,7 +140,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
// Remove voids from fill_boundaries, that are not supported by the layer below.
if (lower_layer_covered == nullptr) {
lower_layer_covered = &lower_layer_covered_tmp;
lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons);
lower_layer_covered_tmp = to_polygons(lower_layer->slices);
}
if (! lower_layer_covered->empty())
voids = diff(voids, *lower_layer_covered);

View File

@ -3,7 +3,6 @@
#include "libslic3r.h"
#include <vector>
#include "ExPolygonCollection.hpp"
#include "Flow.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
@ -15,7 +14,7 @@ class PerimeterGenerator {
public:
// Inputs:
const SurfaceCollection *slices;
const ExPolygonCollection *lower_slices;
const ExPolygons *lower_slices;
double layer_height;
int layer_id;
Flow perimeter_flow;
@ -45,7 +44,7 @@ public:
ExtrusionEntityCollection* gap_fill,
// Infills without the gap fills
SurfaceCollection* fill_surfaces)
: slices(slices), lower_slices(NULL), layer_height(layer_height),
: slices(slices), lower_slices(nullptr), layer_height(layer_height),
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
overhang_flow(flow), solid_infill_flow(flow),
config(config), object_config(object_config), print_config(print_config),

View File

@ -328,17 +328,6 @@ unsigned int Print::num_object_instances() const
return instances;
}
void Print::_simplify_slices(double distance)
{
for (PrintObject *object : m_objects) {
for (Layer *layer : object->m_layers) {
layer->slices.simplify(distance);
for (LayerRegion *layerm : layer->regions())
layerm->slices.simplify(distance);
}
}
}
double Print::max_allowed_layer_height() const
{
double nozzle_diameter_max = 0.;
@ -1593,7 +1582,7 @@ void Print::_make_skirt()
for (const Layer *layer : object->m_layers) {
if (layer->print_z > skirt_height_z)
break;
for (const ExPolygon &expoly : layer->slices.expolygons)
for (const ExPolygon &expoly : layer->slices)
// Collect the outer contour points only, ignore holes for the calculation of the convex hull.
append(object_points, expoly.contour.points);
}
@ -1704,7 +1693,7 @@ void Print::_make_brim()
Polygons islands;
for (PrintObject *object : m_objects) {
Polygons object_islands;
for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons)
for (ExPolygon &expoly : object->m_layers.front()->slices)
object_islands.push_back(expoly.contour);
if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));

View File

@ -180,7 +180,6 @@ private:
void _slice(const std::vector<coordf_t> &layer_height_profile);
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
void _make_perimeters();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
@ -383,7 +382,6 @@ private:
void _make_skirt();
void _make_brim();
void _make_wipe_tower();
void _simplify_slices(double distance);
// Declared here to have access to Model / ModelObject / ModelInstance
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);

View File

@ -117,6 +117,19 @@ void PrintObject::slice()
// Simplify slices if required.
if (m_print->config().resolution)
this->_simplify_slices(scale_(this->print()->config().resolution));
// Update bounding boxes
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
Layer &layer = *m_layers[layer_idx];
layer.slices_bboxes.clear();
layer.slices_bboxes.reserve(layer.slices.size());
for (const ExPolygon &expoly : layer.slices)
layer.slices_bboxes.emplace_back(get_extents(expoly));
}
});
if (m_layers.empty())
throw std::runtime_error("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
this->set_done(posSlice);
@ -865,7 +878,7 @@ void PrintObject::process_external_surfaces()
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
polygons_append(voids, offset(surface.expolygon, unsupported_width));
}
surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids);
surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices), voids);
}
}
);
@ -975,8 +988,8 @@ void PrintObject::discover_vertical_shells()
polygons_append(cache.holes, offset(offset_ex(layer.slices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing));
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
{
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices.expolygons));
svg.draw(layer.slices.expolygons, "blue");
Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices));
svg.draw(layer.slices, "blue");
svg.draw(union_ex(cache.holes), "red");
svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05));
svg.Close();
@ -2141,7 +2154,7 @@ std::string PrintObject::_fix_slicing_errors()
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end";
// remove empty layers from bottom
while (! m_layers.empty() && m_layers.front()->slices.expolygons.empty()) {
while (! m_layers.empty() && m_layers.front()->slices.empty()) {
delete m_layers.front();
m_layers.erase(m_layers.begin());
m_layers.front()->lower_layer = nullptr;
@ -2168,115 +2181,17 @@ void PrintObject::_simplify_slices(double distance)
Layer *layer = m_layers[layer_idx];
for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx)
layer->m_regions[region_idx]->slices.simplify(distance);
layer->slices.simplify(distance);
{
ExPolygons simplified;
for (const ExPolygon& expoly : layer->slices)
expoly.simplify(distance, &simplified);
layer->slices = std::move(simplified);
}
}
});
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end";
}
void PrintObject::_make_perimeters()
{
if (! this->set_started(posPerimeters))
return;
BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info();
// merge slices if they were split into types
if (this->typed_slices) {
for (Layer *layer : m_layers)
layer->merge_slices();
this->typed_slices = false;
this->invalidate_step(posPrepareInfill);
}
// compare each layer to the one below, and mark those slices needing
// one additional inner perimeter, like the top of domed objects-
// this algorithm makes sure that at least one perimeter is overlapping
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size() - 1),
[this, &region, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
LayerRegion &layerm = *m_layers[layer_idx]->regions()[region_id];
const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->regions()[region_id];
const Polygons upper_layerm_polygons = upper_layerm.slices;
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
const double total_loop_length = total_length(upper_layerm_polygons);
const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing();
const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter);
const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width();
const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing();
for (Surface &slice : layerm.slices.surfaces) {
for (;;) {
// compute the total thickness of perimeters
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
+ (region.config().perimeters-1 + slice.extra_perimeters) * perimeter_spacing;
// define a critical area where we don't want the upper slice to fall into
// (it should either lay over our perimeters or outside this area)
const coord_t critical_area_depth = coord_t(perimeter_spacing * 1.5);
const Polygons critical_area = diff(
offset(slice.expolygon, float(- perimeters_thickness)),
offset(slice.expolygon, float(- perimeters_thickness - critical_area_depth))
);
// check whether a portion of the upper slices falls inside the critical area
const Polylines intersection = intersection_pl(to_polylines(upper_layerm_polygons), critical_area);
// only add an additional loop if at least 30% of the slice loop would benefit from it
if (total_length(intersection) <= total_loop_length*0.3)
break;
/*
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"extra.svg",
no_arrows => 1,
expolygons => union_ex($critical_area),
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
);
}
*/
++ slice.extra_perimeters;
}
#ifdef DEBUG
if (slice.extra_perimeters > 0)
printf(" adding %d more perimeter(s) at layer %zu\n", slice.extra_perimeters, layer_idx);
#endif
}
}
});
BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end";
}
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
m_layers[layer_idx]->make_perimeters();
}
);
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
/*
simplify slices (both layer and region slices),
we only need the max resolution for perimeters
### This makes this method not-idempotent, so we keep it disabled for now.
###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION);
*/
this->set_done(posPerimeters);
}
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
// so it acts as an internal support. It maintains all other infill types intact.
// Here the internal surfaces and perimeters have to be supported by the sparse infill.
@ -2302,7 +2217,7 @@ void PrintObject::clip_fill_surfaces()
// Detect things that we need to support.
// Cummulative slices.
Polygons slices;
polygons_append(slices, layer->slices.expolygons);
polygons_append(slices, layer->slices);
// Cummulative fill surfaces.
Polygons fill_surfaces;
// Solid surfaces to be supported.

View File

@ -445,8 +445,8 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
Polygons collect_slices_outer(const Layer &layer)
{
Polygons out;
out.reserve(out.size() + layer.slices.expolygons.size());
for (const ExPolygon &expoly : layer.slices.expolygons)
out.reserve(out.size() + layer.slices.size());
for (const ExPolygon &expoly : layer.slices)
out.emplace_back(expoly.contour);
return out;
}
@ -907,9 +907,13 @@ namespace SupportMaterialInternal {
polyline.extend_start(fw);
polyline.extend_end(fw);
// Is the straight perimeter segment supported at both sides?
if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
// Offset a polyline into a thick line.
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
for (size_t i = 0; i < lower_layer.slices.size(); ++ i)
if (lower_layer.slices_bboxes[i].contains(polyline.first_point()) && lower_layer.slices_bboxes[i].contains(polyline.last_point()) &&
lower_layer.slices[i].contains(polyline.first_point()) && lower_layer.slices[i].contains(polyline.last_point())) {
// Offset a polyline into a thick line.
polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
break;
}
}
bridges = union_(bridges);
}
@ -994,7 +998,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// inflate the polygons over and over.
Polygons &covered = buildplate_covered[layer_id];
covered = buildplate_covered[layer_id - 1];
polygons_append(covered, offset(lower_layer.slices.expolygons, scale_(0.01)));
polygons_append(covered, offset(lower_layer.slices, scale_(0.01)));
covered = union_(covered, false); // don't apply the safety offset.
}
}
@ -1023,7 +1027,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons);
Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices);
// Offset of the lower layer, to trim the support polygons with to calculate dense supports.
float no_interface_offset = 0.f;
if (layer_id == 0) {
@ -1162,7 +1166,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = (slices_margin_offset == 0.f) ?
lower_layer_polygons :
offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
offset2(to_polygons(lower_layer.slices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@ -1468,7 +1472,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
svg.draw(union_ex(top, false), "blue", 0.5f);
svg.draw(union_ex(projection_raw, true), "red", 0.5f);
svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f));
svg.draw(layer.slices.expolygons, "green", 0.5f);
svg.draw(layer.slices, "green", 0.5f);
}
#endif /* SLIC3R_DEBUG */
@ -1568,8 +1572,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
Polygons &layer_support_area = layer_support_areas[layer_id];
task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] {
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
// Polygons trimming = union_(to_polygons(layer.slices.expolygons), touching, true);
Polygons trimming = offset(layer.slices.expolygons, float(SCALED_EPSILON));
// Polygons trimming = union_(to_polygons(layer.slices), touching, true);
Polygons trimming = offset(layer.slices, float(SCALED_EPSILON));
projection = diff(projection_raw, trimming, false);
#ifdef SLIC3R_DEBUG
{
@ -2101,7 +2105,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers()[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
polygons_append(polygons_trimming, offset(object_layer.slices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
@ -2214,7 +2218,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer),
offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
offset(m_object->layers().front()->slices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
}

View File

@ -3,6 +3,7 @@
%{
#include <xsinit.h>
#include "libslic3r/Layer.hpp"
#include "libslic3r/ExPolygonCollection.hpp"
%}
%name{Slic3r::Layer::Region} class LayerRegion {
@ -59,8 +60,8 @@
Ref<LayerRegion> get_region(int idx);
Ref<LayerRegion> add_region(PrintRegion* print_region);
Ref<ExPolygonCollection> slices()
%code%{ RETVAL = &THIS->slices; %};
ExPolygonCollection* slices()
%code%{ RETVAL = new ExPolygonCollection(THIS->slices); %};
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};
@ -108,8 +109,8 @@
Ref<LayerRegion> get_region(int idx);
Ref<LayerRegion> add_region(PrintRegion* print_region);
Ref<ExPolygonCollection> slices()
%code%{ RETVAL = &THIS->slices; %};
ExPolygonCollection* slices()
%code%{ RETVAL = new ExPolygonCollection(THIS->slices); %};
void export_region_slices_to_svg(const char *path);
void export_region_fill_surfaces_to_svg(const char *path);

View File

@ -19,7 +19,7 @@
~PerimeterGenerator();
void set_lower_slices(ExPolygonCollection* lower_slices)
%code{% THIS->lower_slices = lower_slices; %};
%code{% THIS->lower_slices = &lower_slices->expolygons; %};
void set_layer_id(int layer_id)
%code{% THIS->layer_id = layer_id; %};
void set_perimeter_flow(Flow* flow)