Ported make_perimeters() and infill() to C++/XS, use pure C++ threads, cherry picked from @alexrj 66591bcc556c01572ec7519b1f8cb4ee2d430685
This commit is contained in:
parent
3e8cafa857
commit
86c8207d31
@ -41,8 +41,12 @@ sub size {
|
|||||||
sub process {
|
sub process {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->status_cb->(20, "Generating perimeters");
|
||||||
$_->make_perimeters for @{$self->objects};
|
$_->make_perimeters for @{$self->objects};
|
||||||
|
|
||||||
|
$self->status_cb->(70, "Infilling layers");
|
||||||
$_->infill for @{$self->objects};
|
$_->infill for @{$self->objects};
|
||||||
|
|
||||||
$_->generate_support_material for @{$self->objects};
|
$_->generate_support_material for @{$self->objects};
|
||||||
$self->make_skirt;
|
$self->make_skirt;
|
||||||
$self->make_brim; # must come after make_skirt
|
$self->make_brim; # must come after make_skirt
|
||||||
|
@ -371,114 +371,11 @@ sub _slice_region {
|
|||||||
# 2) Increases an "extra perimeters" counter at region slices where needed.
|
# 2) Increases an "extra perimeters" counter at region slices where needed.
|
||||||
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
# 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||||
sub make_perimeters {
|
sub make_perimeters {
|
||||||
my $self = shift;
|
my ($self) = @_;
|
||||||
|
|
||||||
# prerequisites
|
# prerequisites
|
||||||
$self->slice;
|
$self->slice;
|
||||||
|
$self->_make_perimeters;
|
||||||
return if $self->step_done(STEP_PERIMETERS);
|
|
||||||
$self->set_step_started(STEP_PERIMETERS);
|
|
||||||
$self->print->status_cb->(20, "Generating perimeters");
|
|
||||||
|
|
||||||
# Merge region slices if they were split into types.
|
|
||||||
# FIXME this is using a safety offset, so the region slices will be slightly bigger with each iteration.
|
|
||||||
if ($self->typed_slices) {
|
|
||||||
$_->merge_slices for @{$self->layers};
|
|
||||||
$self->set_typed_slices(0);
|
|
||||||
$self->invalidate_step(STEP_PREPARE_INFILL);
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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 my $region_id (0 .. ($self->print->region_count-1)) {
|
|
||||||
my $region = $self->print->regions->[$region_id];
|
|
||||||
my $region_perimeters = $region->config->perimeters;
|
|
||||||
|
|
||||||
next if !$region->config->extra_perimeters;
|
|
||||||
next if $region_perimeters == 0;
|
|
||||||
next if $region->config->fill_density == 0;
|
|
||||||
|
|
||||||
for my $i (0 .. ($self->layer_count - 2)) {
|
|
||||||
my $layerm = $self->get_layer($i)->get_region($region_id);
|
|
||||||
my $upper_layerm = $self->get_layer($i+1)->get_region($region_id);
|
|
||||||
my $upper_layerm_polygons = [ map $_->p, @{$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} ];
|
|
||||||
my $total_loop_length = sum(map $_->length, @$upper_layerm_polygons) // 0;
|
|
||||||
|
|
||||||
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
|
||||||
my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER);
|
|
||||||
my $ext_perimeter_width = $ext_perimeter_flow->scaled_width;
|
|
||||||
my $ext_perimeter_spacing = $ext_perimeter_flow->scaled_spacing;
|
|
||||||
|
|
||||||
foreach my $slice (@{$layerm->slices}) {
|
|
||||||
while (1) {
|
|
||||||
# compute the total thickness of perimeters
|
|
||||||
my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2
|
|
||||||
+ ($region_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)
|
|
||||||
my $critical_area_depth = $perimeter_spacing*1.5;
|
|
||||||
my $critical_area = diff(
|
|
||||||
offset($slice->expolygon->arrayref, -$perimeters_thickness),
|
|
||||||
offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)),
|
|
||||||
);
|
|
||||||
|
|
||||||
# check whether a portion of the upper slices falls inside the critical area
|
|
||||||
my $intersection = intersection_ppl(
|
|
||||||
$upper_layerm_polygons,
|
|
||||||
$critical_area,
|
|
||||||
);
|
|
||||||
|
|
||||||
# only add an additional loop if at least 30% of the slice loop would benefit from it
|
|
||||||
my $total_intersection_length = sum(map $_->length, @$intersection) // 0;
|
|
||||||
last unless $total_intersection_length > $total_loop_length*0.3;
|
|
||||||
|
|
||||||
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($slice->extra_perimeters + 1);
|
|
||||||
}
|
|
||||||
Slic3r::debugf " adding %d more perimeter(s) at layer %d\n",
|
|
||||||
$slice->extra_perimeters, $layerm->layer->id
|
|
||||||
if $slice->extra_perimeters > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Slic3r::parallelize(
|
|
||||||
threads => $self->print->config->threads,
|
|
||||||
items => sub { 0 .. ($self->layer_count - 1) },
|
|
||||||
thread_cb => sub {
|
|
||||||
my $q = shift;
|
|
||||||
while (defined (my $i = $q->dequeue)) {
|
|
||||||
$self->get_layer($i)->make_perimeters;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
no_threads_cb => sub {
|
|
||||||
$_->make_perimeters for @{$self->layers};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
# 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);
|
|
||||||
|
|
||||||
$self->set_step_done(STEP_PERIMETERS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub prepare_infill {
|
sub prepare_infill {
|
||||||
@ -598,32 +495,7 @@ sub infill {
|
|||||||
|
|
||||||
# prerequisites
|
# prerequisites
|
||||||
$self->prepare_infill;
|
$self->prepare_infill;
|
||||||
|
$self->_infill;
|
||||||
return if $self->step_done(STEP_INFILL);
|
|
||||||
$self->set_step_started(STEP_INFILL);
|
|
||||||
$self->print->status_cb->(70, "Infilling layers");
|
|
||||||
|
|
||||||
Slic3r::parallelize(
|
|
||||||
threads => $self->print->config->threads,
|
|
||||||
items => sub { 0..$#{$self->layers} },
|
|
||||||
thread_cb => sub {
|
|
||||||
my $q = shift;
|
|
||||||
while (defined (my $i = $q->dequeue)) {
|
|
||||||
$self->get_layer($i)->make_fills;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
no_threads_cb => sub {
|
|
||||||
foreach my $layer (@{$self->layers}) {
|
|
||||||
$layer->make_fills;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
### we could free memory now, but this would make this step not idempotent
|
|
||||||
### Vojtech: Cannot release the fill_surfaces, they are used by the support generator.
|
|
||||||
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
|
|
||||||
|
|
||||||
$self->set_step_done(STEP_INFILL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub generate_support_material {
|
sub generate_support_material {
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
#define slic3r_Print_hpp_
|
#define slic3r_Print_hpp_
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#include <queue>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
#include "Flow.hpp"
|
#include "Flow.hpp"
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
@ -142,6 +144,8 @@ public:
|
|||||||
void process_external_surfaces();
|
void process_external_surfaces();
|
||||||
void discover_vertical_shells();
|
void discover_vertical_shells();
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
|
void _make_perimeters();
|
||||||
|
void _infill();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Print* _print;
|
Print* _print;
|
||||||
@ -151,7 +155,9 @@ private:
|
|||||||
// TODO: call model_object->get_bounding_box() instead of accepting
|
// TODO: call model_object->get_bounding_box() instead of accepting
|
||||||
// parameter
|
// parameter
|
||||||
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
|
PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox);
|
||||||
~PrintObject() {}
|
~PrintObject();
|
||||||
|
void _make_perimeters_do(std::queue<size_t>* queue, boost::mutex* queue_mutex);
|
||||||
|
void _infill_do(std::queue<size_t>* queue, boost::mutex* queue_mutex);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<PrintObject*> PrintObjectPtrs;
|
typedef std::vector<PrintObject*> PrintObjectPtrs;
|
||||||
|
@ -910,4 +910,189 @@ PrintObject::bridge_over_infill()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PrintObject::_make_perimeters()
|
||||||
|
{
|
||||||
|
if (this->state.is_done(posPerimeters)) return;
|
||||||
|
this->state.set_started(posPerimeters);
|
||||||
|
|
||||||
|
// merge slices if they were split into types
|
||||||
|
if (this->typed_slices) {
|
||||||
|
FOREACH_LAYER(this, layer_it)
|
||||||
|
(*layer_it)->merge_slices();
|
||||||
|
this->typed_slices = false;
|
||||||
|
this->state.invalidate(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
|
||||||
|
FOREACH_REGION(this->_print, region_it) {
|
||||||
|
size_t region_id = region_it - this->_print->regions.begin();
|
||||||
|
const PrintRegion ®ion = **region_it;
|
||||||
|
|
||||||
|
|
||||||
|
if (!region.config.extra_perimeters
|
||||||
|
|| region.config.perimeters == 0
|
||||||
|
|| region.config.fill_density == 0) continue;
|
||||||
|
|
||||||
|
for (size_t i = 0; i <= (this->layer_count()-2); ++i) {
|
||||||
|
LayerRegion &layerm = *this->get_layer(i)->get_region(region_id);
|
||||||
|
const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(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} ];
|
||||||
|
double total_loop_length = 0;
|
||||||
|
for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it)
|
||||||
|
total_loop_length += it->length();
|
||||||
|
|
||||||
|
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 (Surfaces::iterator slice = layerm.slices.surfaces.begin();
|
||||||
|
slice != layerm.slices.surfaces.end(); ++slice) {
|
||||||
|
while (true) {
|
||||||
|
// compute the total thickness of perimeters
|
||||||
|
const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
|
||||||
|
+ (region.config.perimeters-1 + region.config.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 = perimeter_spacing * 1.5;
|
||||||
|
const Polygons critical_area = diff(
|
||||||
|
offset(slice->expolygon, -perimeters_thickness),
|
||||||
|
offset(slice->expolygon, -(perimeters_thickness + critical_area_depth))
|
||||||
|
);
|
||||||
|
|
||||||
|
// check whether a portion of the upper slices falls inside the critical area
|
||||||
|
const Polylines intersection = intersection_pl(
|
||||||
|
upper_layerm_polygons,
|
||||||
|
critical_area
|
||||||
|
);
|
||||||
|
|
||||||
|
// only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||||
|
{
|
||||||
|
double total_intersection_length = 0;
|
||||||
|
for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it)
|
||||||
|
total_intersection_length += it->length();
|
||||||
|
if (total_intersection_length <= 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->id();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// queue all the layer numbers
|
||||||
|
std::queue<size_t> queue;
|
||||||
|
boost::mutex queue_mutex;
|
||||||
|
for (size_t i = 0; i < this->layer_count(); ++i)
|
||||||
|
queue.push(i);
|
||||||
|
|
||||||
|
boost::thread_group workers;
|
||||||
|
for (int i = 0; i < this->_print->config.threads; i++)
|
||||||
|
workers.add_thread(new boost::thread(&Slic3r::PrintObject::_make_perimeters_do, this, &queue, &queue_mutex));
|
||||||
|
workers.join_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
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->state.set_done(posPerimeters);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PrintObject::_make_perimeters_do(std::queue<size_t>* queue, boost::mutex* queue_mutex)
|
||||||
|
{
|
||||||
|
//std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t layer_id;
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(*queue_mutex);
|
||||||
|
if (queue->empty()) return;
|
||||||
|
layer_id = queue->front();
|
||||||
|
queue->pop();
|
||||||
|
}
|
||||||
|
//std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl;
|
||||||
|
this->get_layer(layer_id)->make_perimeters();
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PrintObject::_infill()
|
||||||
|
{
|
||||||
|
if (this->state.is_done(posInfill)) return;
|
||||||
|
this->state.set_started(posInfill);
|
||||||
|
|
||||||
|
{
|
||||||
|
// queue all the layer numbers
|
||||||
|
std::queue<size_t> queue;
|
||||||
|
boost::mutex queue_mutex;
|
||||||
|
for (size_t i = 0; i < this->layer_count(); ++i)
|
||||||
|
queue.push(i);
|
||||||
|
|
||||||
|
boost::thread_group workers;
|
||||||
|
for (int i = 0; i < this->_print->config.threads; i++)
|
||||||
|
workers.add_thread(new boost::thread(&Slic3r::PrintObject::_infill_do, this, &queue, &queue_mutex));
|
||||||
|
workers.join_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we could free memory now, but this would make this step not idempotent
|
||||||
|
### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers};
|
||||||
|
*/
|
||||||
|
|
||||||
|
this->state.set_done(posInfill);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PrintObject::_infill_do(std::queue<size_t>* queue, boost::mutex* queue_mutex)
|
||||||
|
{
|
||||||
|
//std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
size_t layer_id;
|
||||||
|
{
|
||||||
|
boost::lock_guard<boost::mutex> l(*queue_mutex);
|
||||||
|
if (queue->empty()) return;
|
||||||
|
layer_id = queue->front();
|
||||||
|
queue->pop();
|
||||||
|
}
|
||||||
|
//std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl;
|
||||||
|
this->get_layer(layer_id)->make_fills();
|
||||||
|
boost::this_thread::interruption_point();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,8 @@ _constant()
|
|||||||
void process_external_surfaces();
|
void process_external_surfaces();
|
||||||
void discover_vertical_shells();
|
void discover_vertical_shells();
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
|
void _make_perimeters();
|
||||||
|
void _infill();
|
||||||
|
|
||||||
int ptr()
|
int ptr()
|
||||||
%code%{ RETVAL = (int)(intptr_t)THIS; %};
|
%code%{ RETVAL = (int)(intptr_t)THIS; %};
|
||||||
|
Loading…
Reference in New Issue
Block a user