Ported the G-code generator from Perl to C++.

Removed GCode.pm
Removed the Perl bindigns for AvoidCrossingPerimeters, OozePrevention, SpiralVase, Wipe
Changed the std::set of extruder IDs to vector of IDs.
Removed some MSVC compiler warnings, removed obnoxious compiler warnings when compiling the Perl bindings.
This commit is contained in:
bubnikv 2017-05-03 18:28:22 +02:00
parent 72ae3585e4
commit e90279c513
52 changed files with 1362 additions and 1632 deletions

View File

@ -71,7 +71,6 @@ use Slic3r::Point;
use Slic3r::Polygon;
use Slic3r::Polyline;
use Slic3r::Print;
use Slic3r::Print::GCode;
use Slic3r::Print::Object;
use Slic3r::Print::Simple;
use Slic3r::Surface;
@ -174,11 +173,8 @@ sub thread_cleanup {
# Therefore the Filler instances shall be released at the end of the thread.
# *Slic3r::Filler::DESTROY = sub {};
*Slic3r::GCode::DESTROY = sub {};
*Slic3r::GCode::AvoidCrossingPerimeters::DESTROY = sub {};
*Slic3r::GCode::OozePrevention::DESTROY = sub {};
*Slic3r::GCode::PlaceholderParser::DESTROY = sub {};
*Slic3r::GCode::Sender::DESTROY = sub {};
*Slic3r::GCode::Wipe::DESTROY = sub {};
*Slic3r::GCode::Writer::DESTROY = sub {};
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
*Slic3r::Geometry::BoundingBoxf::DESTROY = sub {};

View File

@ -76,34 +76,17 @@ sub export_gcode {
{
# open output gcode file if we weren't supplied a file-handle
my ($fh, $tempfile);
if ($params{output_fh}) {
$fh = $params{output_fh};
} else {
$tempfile = "$output_file.tmp";
Slic3r::open(\$fh, ">", $tempfile)
or die "Failed to open $tempfile for writing\n";
# enable UTF-8 output since user might have entered Unicode characters in fields like notes
binmode $fh, ':utf8';
}
Slic3r::Print::GCode->new(
print => $self,
fh => $fh,
)->export;
# close our gcode file
close $fh;
if ($tempfile) {
my $i;
for ($i = 0; $i < 5; $i += 1) {
last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file));
# Wait for 1/4 seconds and try to rename once again.
select(undef, undef, undef, 0.25);
}
Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5);
my $tempfile = "$output_file.tmp";
my $gcode = Slic3r::GCode->new();
my $result = $gcode->do_export($self, $tempfile);
die $result . "\n" if ($result ne '');
my $i;
for ($i = 0; $i < 5; $i += 1) {
last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file));
# Wait for 1/4 seconds and try to rename once again.
select(undef, undef, undef, 0.25);
}
Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5);
}
# run post-processing scripts

View File

@ -1,712 +0,0 @@
package Slic3r::Print::GCode;
use Moo;
has 'print' => (is => 'ro', required => 1, handles => [qw(objects placeholder_parser config)]);
has 'fh' => (is => 'ro', required => 1);
has '_gcodegen' => (is => 'rw');
has '_cooling_buffer' => (is => 'rw');
has '_spiral_vase' => (is => 'rw');
has '_pressure_equalizer' => (is => 'rw');
has '_skirt_done' => (is => 'rw', default => sub { {} }); # print_z => 1
has '_brim_done' => (is => 'rw');
# Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
has '_second_layer_things_done' => (is => 'rw');
has '_last_obj_copy' => (is => 'rw');
use List::Util qw(first sum min max);
use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(X Y scale unscale chained_path convex_hull);
use Slic3r::Geometry::Clipper qw(JT_SQUARE union_ex offset);
sub BUILD {
my ($self) = @_;
{
# estimate the total number of layer changes
# TODO: only do this when M73 is enabled
my $layer_count;
if ($self->config->complete_objects) {
$layer_count = sum(map { $_->total_layer_count * @{$_->copies} } @{$self->objects});
} else {
# if sequential printing is not enable, all copies of the same object share the same layer change command(s)
$layer_count = sum(map { $_->total_layer_count } @{$self->objects});
}
# set up our helper object: This is a C++ Slic3r::GCode instance.
my $gcodegen = Slic3r::GCode->new;
$self->_gcodegen($gcodegen);
$gcodegen->set_placeholder_parser($self->placeholder_parser);
# Tell the G-code generator, how many times the $gcodegen->change_layer() will be called.
# $gcodegen->change_layer() in turn increments the progress bar status.
$gcodegen->set_layer_count($layer_count);
$gcodegen->set_enable_cooling_markers(1);
$gcodegen->apply_print_config($self->config);
$gcodegen->set_extruders($self->print->extruders);
# initialize autospeed
{
# get the minimum cross-section used in the print
my @mm3_per_mm = ();
foreach my $object (@{$self->print->objects}) {
foreach my $region_id (0..$#{$self->print->regions}) {
my $region = $self->print->get_region($region_id);
foreach my $layer (@{$object->layers}) {
my $layerm = $layer->get_region($region_id);
if ($region->config->get_abs_value('perimeter_speed') == 0
|| $region->config->get_abs_value('small_perimeter_speed') == 0
|| $region->config->get_abs_value('external_perimeter_speed') == 0
|| $region->config->get_abs_value('bridge_speed') == 0) {
push @mm3_per_mm, $layerm->perimeters->min_mm3_per_mm;
}
if ($region->config->get_abs_value('infill_speed') == 0
|| $region->config->get_abs_value('solid_infill_speed') == 0
|| $region->config->get_abs_value('top_solid_infill_speed') == 0
|| $region->config->get_abs_value('bridge_speed') == 0) {
push @mm3_per_mm, $layerm->fills->min_mm3_per_mm;
}
}
}
if ($object->config->get_abs_value('support_material_speed') == 0
|| $object->config->get_abs_value('support_material_interface_speed') == 0) {
foreach my $layer (@{$object->support_layers}) {
push @mm3_per_mm, $layer->support_fills->min_mm3_per_mm;
}
}
}
# filter out 0-width segments
@mm3_per_mm = grep $_ > 0.000001, @mm3_per_mm;
if (@mm3_per_mm) {
my $min_mm3_per_mm = min(@mm3_per_mm);
# In order to honor max_print_speed we need to find a target volumetric
# speed that we can use throughout the print. So we define this target
# volumetric speed as the volumetric speed produced by printing the
# smallest cross-section at the maximum speed: any larger cross-section
# will need slower feedrates.
my $volumetric_speed = $min_mm3_per_mm * $self->config->max_print_speed;
# limit such volumetric speed with max_volumetric_speed if set
if ($self->config->max_volumetric_speed > 0) {
$volumetric_speed = min(
$volumetric_speed,
$self->config->max_volumetric_speed,
);
}
$gcodegen->set_volumetric_speed($volumetric_speed);
}
}
}
$self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new($self->_gcodegen));
$self->_spiral_vase(Slic3r::GCode::SpiralVase->new($self->config))
if $self->config->spiral_vase;
$self->_pressure_equalizer(Slic3r::GCode::PressureEqualizer->new($self->config))
if ($self->config->max_volumetric_extrusion_rate_slope_positive > 0 ||
$self->config->max_volumetric_extrusion_rate_slope_negative > 0);
$self->_gcodegen->set_enable_extrusion_role_markers(defined $self->_pressure_equalizer);
}
# Export a G-code for the complete print.
sub export {
my ($self) = @_;
my $fh = $self->fh;
my $gcodegen = $self->_gcodegen;
# Write information on the generator.
my @lt = localtime;
printf $fh "; generated by Slic3r $Slic3r::VERSION on %04d-%02d-%02d at %02d:%02d:%02d\n\n",
$lt[5] + 1900, $lt[4]+1, $lt[3], $lt[2], $lt[1], $lt[0];
# Write notes (content of the Print Settings tab -> Notes)
print $fh "; $_\n" foreach split /\R/, $self->config->notes;
print $fh "\n" if $self->config->notes;
# Write some terse information on the slicing parameters.
my $first_object = $self->objects->[0];
my $layer_height = $first_object->config->layer_height;
for my $region_id (0..$#{$self->print->regions}) {
my $region = $self->print->regions->[$region_id];
printf $fh "; external perimeters extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; perimeters extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; infill extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; solid infill extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; top infill extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; support material extrusion width = %.2fmm\n",
$self->objects->[0]->support_material_flow->width
if $self->print->has_support_material;
printf $fh "; first layer extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1, -1, $self->objects->[0])->width
if $region->config->first_layer_extrusion_width;
print $fh "\n";
}
# prepare the helper object for replacing placeholders in custom G-code and output filename
$self->placeholder_parser->update_timestamp;
# disable fan
print $fh $gcodegen->writer->set_fan(0, 1)
if $self->config->cooling && $self->config->disable_fan_first_layers;
# set bed temperature
if ((my $temp = $self->config->first_layer_bed_temperature) && $self->config->start_gcode !~ /M(?:190|140)/i) {
printf $fh $gcodegen->writer->set_bed_temperature($temp, 1);
}
# set extruder(s) temperature before and after start G-code
$self->_print_first_layer_extruder_temperatures(0);
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->start_gcode);
$self->_print_first_layer_extruder_temperatures(1);
# set other general things
print $fh $gcodegen->preamble;
# initialize a motion planner for object-to-object travel moves
if ($self->config->avoid_crossing_perimeters) {
my $distance_from_objects = scale 1;
# compute the offsetted convex hull for each object and repeat it for each copy.
my @islands_p = ();
foreach my $object (@{$self->objects}) {
# discard objects only containing thin walls (offset would fail on an empty polygon)
my @polygons = map $_->contour, map @{$_->slices}, @{$object->layers};
next if !@polygons;
# translate convex hull for each object copy and append it to the islands array
foreach my $copy (@{ $object->_shifted_copies }) {
my @copy_islands_p = map $_->clone, @polygons;
$_->translate(@$copy) for @copy_islands_p;
push @islands_p, @copy_islands_p;
}
}
$gcodegen->avoid_crossing_perimeters->init_external_mp(union_ex(\@islands_p));
}
# calculate wiping points if needed
if ($self->config->ooze_prevention) {
my @skirt_points = map @$_, map @$_, @{$self->print->skirt};
if (@skirt_points) {
my $outer_skirt = convex_hull(\@skirt_points);
my @skirts = ();
foreach my $extruder_id (@{$self->print->extruders}) {
my $extruder_offset = $self->config->get_at('extruder_offset', $extruder_id);
push @skirts, my $s = $outer_skirt->clone;
$s->translate(-scale($extruder_offset->x), -scale($extruder_offset->y)); #)
}
my $convex_hull = convex_hull([ map @$_, @skirts ]);
$gcodegen->ooze_prevention->set_enable(1);
$gcodegen->ooze_prevention->set_standby_points(
[ map @{$_->equally_spaced_points(scale 10)}, @{offset([$convex_hull], scale 3)} ]
);
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"ooze_prevention.svg",
red_polygons => \@skirts,
polygons => [$outer_skirt],
points => $gcodegen->ooze_prevention->standby_points,
);
}
}
}
# set initial extruder only after custom start G-code
print $fh $gcodegen->set_extruder($self->print->extruders->[0]);
# do all objects for each layer
if ($self->config->complete_objects) {
# print objects from the smallest to the tallest to avoid collisions
# when moving onto next object starting point
my @obj_idx = sort { $self->objects->[$a]->size->z <=> $self->objects->[$b]->size->z } 0..($self->print->object_count - 1);
my $finished_objects = 0;
for my $obj_idx (@obj_idx) {
my $object = $self->objects->[$obj_idx];
for my $copy (@{ $self->objects->[$obj_idx]->_shifted_copies }) {
# move to the origin position for the copy we're going to print.
# this happens before Z goes down to layer 0 again, so that
# no collision happens hopefully.
if ($finished_objects > 0) {
$gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
$gcodegen->set_enable_cooling_markers(0); # we're not filtering these moves through CoolingBuffer
$gcodegen->avoid_crossing_perimeters->set_use_external_mp_once(1);
print $fh $gcodegen->retract;
print $fh $gcodegen->travel_to(
Slic3r::Point->new(0,0),
EXTR_ROLE_NONE,
'move to origin position for next object',
);
$gcodegen->set_enable_cooling_markers(1);
# disable motion planner when traveling to first object point
$gcodegen->avoid_crossing_perimeters->set_disable_once(1);
}
# Order layers by print_z, support layers preceding the object layers.
my @layers = sort
{ ($a->print_z == $b->print_z) ? ($a->isa('Slic3r::Layer::Support') ? -1 : 0) : $a->print_z <=> $b->print_z }
@{$object->layers}, @{$object->support_layers};
for my $layer (@layers) {
# if we are printing the bottom layer of an object, and we have already finished
# another one, set first layer temperatures. this happens before the Z move
# is triggered, so machine has more time to reach such temperatures
if ($layer->id == 0 && $finished_objects > 0) {
printf $fh $gcodegen->writer->set_bed_temperature($self->config->first_layer_bed_temperature),
if $self->config->first_layer_bed_temperature;
# Set first layer extruder
$self->_print_first_layer_extruder_temperatures(0);
}
$self->process_layer($layer, [$copy]);
}
$self->flush_filters;
$finished_objects++;
# Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
# Reset it when starting another object from 1st layer.
$self->_second_layer_things_done(0);
}
}
} else {
# order objects using a nearest neighbor search
my @obj_idx = @{chained_path([ map Slic3r::Point->new(@{$_->_shifted_copies->[0]}), @{$self->objects} ])};
# sort layers by Z
# All extrusion moves with the same top layer height are extruded uninterrupted,
# object extrusion moves are performed first, then the support.
my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx
foreach my $obj_idx (0 .. ($self->print->object_count - 1)) {
my $object = $self->objects->[$obj_idx];
# Collect the object layers by z, support layers first, object layers second.
foreach my $layer (@{$object->support_layers}, @{$object->layers}) {
$layers{ $layer->print_z } ||= [];
$layers{ $layer->print_z }[$obj_idx] ||= [];
push @{$layers{ $layer->print_z }[$obj_idx]}, $layer;
}
}
foreach my $print_z (sort { $a <=> $b } keys %layers) {
foreach my $obj_idx (@obj_idx) {
foreach my $layer (@{ $layers{$print_z}[$obj_idx] // [] }) {
$self->process_layer($layer, $layer->object->_shifted_copies);
}
}
}
$self->flush_filters;
}
# write end commands to file
print $fh $gcodegen->retract; # TODO: process this retract through PressureRegulator in order to discharge fully
print $fh $gcodegen->writer->set_fan(0);
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode);
print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100%
print $fh $gcodegen->writer->postamble;
# get filament stats
$self->print->clear_filament_stats;
$self->print->total_used_filament(0);
$self->print->total_extruded_volume(0);
$self->print->total_weight(0);
$self->print->total_cost(0);
foreach my $extruder (@{$gcodegen->writer->extruders}) {
my $used_filament = $extruder->used_filament;
my $extruded_volume = $extruder->extruded_volume;
my $filament_weight = $extruded_volume * $extruder->filament_density / 1000;
my $filament_cost = $filament_weight * ($extruder->filament_cost / 1000);
$self->print->set_filament_stats($extruder->id, $used_filament);
printf $fh "; filament used = %.1fmm (%.1fcm3)\n",
$used_filament, $extruded_volume/1000;
if ($filament_weight > 0) {
$self->print->total_weight($self->print->total_weight + $filament_weight);
printf $fh "; filament used = %.1fg\n",
$filament_weight;
if ($filament_cost > 0) {
$self->print->total_cost($self->print->total_cost + $filament_cost);
printf $fh "; filament cost = %.1f\n",
$filament_cost;
}
}
$self->print->total_used_filament($self->print->total_used_filament + $used_filament);
$self->print->total_extruded_volume($self->print->total_extruded_volume + $extruded_volume);
}
printf $fh "; total filament cost = %.1f\n",
$self->print->total_cost;
# append full config
print $fh "\n";
foreach my $config ($self->print->config, $self->print->default_object_config, $self->print->default_region_config) {
foreach my $opt_key (sort @{$config->get_keys}) {
next if $Slic3r::Config::Options->{$opt_key}{shortcut};
printf $fh "; %s = %s\n", $opt_key, $config->serialize($opt_key);
}
}
}
# Write 1st layer extruder temperatures into the G-code.
# Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
# FIXME this does not work correctly for multi-extruder, single heater configuration as it emits multiple preheat commands for the same heater.
# M104 - Set Extruder Temperature
# M109 - Set Extruder Temperature and Wait
sub _print_first_layer_extruder_temperatures {
my ($self, $wait) = @_;
return if $self->config->start_gcode =~ /M(?:109|104)/i;
for my $t (@{$self->print->extruders}) {
my $temp = $self->config->get_at('first_layer_temperature', $t);
$temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention;
printf {$self->fh} $self->_gcodegen->writer->set_temperature($temp, $wait, $t) if $temp > 0;
}
}
# Called per object's layer.
# First a $gcode string is collected,
# then filtered and finally written to a file $fh.
#FIXME If printing multiple objects at once, this incorrectly applies cooling logic to a single object's layer instead
# of all the objects printed.
sub process_layer {
my $self = shift;
my ($layer, $object_copies) = @_;
my $gcode = "";
my $object = $layer->object;
$self->_gcodegen->config->apply_static($object->config);
# check whether we're going to apply spiralvase logic
if (defined $self->_spiral_vase) {
$self->_spiral_vase->set_enable(
($layer->id > 0 || $self->print->config->brim_width == 0)
&& ($layer->id >= $self->print->config->skirt_height && !$self->print->has_infinite_skirt)
&& !defined(first { $_->region->config->bottom_solid_layers > $layer->id } @{$layer->regions})
&& !defined(first { $_->perimeters->items_count > 1 } @{$layer->regions})
&& !defined(first { $_->fills->items_count > 0 } @{$layer->regions})
);
}
# if we're going to apply spiralvase to this layer, disable loop clipping
$self->_gcodegen->set_enable_loop_clipping(!defined $self->_spiral_vase || !$self->_spiral_vase->enable);
if (!$self->_second_layer_things_done && $layer->id == 1) {
# Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent
# first_layer_temperature vs. temperature settings.
for my $extruder (@{$self->_gcodegen->writer->extruders}) {
my $temperature = $self->config->get_at('temperature', $extruder->id);
$gcode .= $self->_gcodegen->writer->set_temperature($temperature, 0, $extruder->id)
if $temperature && $temperature != $self->config->get_at('first_layer_temperature', $extruder->id);
}
$gcode .= $self->_gcodegen->writer->set_bed_temperature($self->print->config->bed_temperature)
if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature;
# Mark the temperature transition from 1st to 2nd layer to be finished.
$self->_second_layer_things_done(1);
}
# set new layer - this will change Z and force a retraction if retract_layer_change is enabled
if ($self->print->config->before_layer_gcode) {
my $pp = $self->_gcodegen->placeholder_parser->clone;
$pp->set('layer_num' => $self->_gcodegen->layer_index + 1);
$pp->set('layer_z' => $layer->print_z);
$gcode .= $pp->process($self->print->config->before_layer_gcode) . "\n";
}
$gcode .= $self->_gcodegen->change_layer($layer->as_layer); # this will increase $self->_gcodegen->layer_index
if ($self->print->config->layer_gcode) {
my $pp = $self->_gcodegen->placeholder_parser->clone;
$pp->set('layer_num' => $self->_gcodegen->layer_index);
$pp->set('layer_z' => $layer->print_z);
$gcode .= $pp->process($self->print->config->layer_gcode) . "\n";
}
# Extrude skirt at the print_z of the raft layers and normal object layers
# not at the print_z of the interlaced support material layers.
#FIXME this will print the support 1st, skirt 2nd and an object 3rd
# if they are at the same print_z, it is not the 1st print layer and the support is printed before object.
if (
# Not enough skirt layers printed yer
((values %{$self->_skirt_done}) < $self->print->config->skirt_height || $self->print->has_infinite_skirt)
# This print_z has not been extruded yet
&& !$self->_skirt_done->{$layer->print_z}
# and this layer is the 1st layer, or it is an object layer, or it is a raft layer.
&& ($layer->id == 0 || !$layer->isa('Slic3r::Layer::Support') || $layer->id < $object->config->raft_layers)) {
$self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0));
$self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(1);
my @extruder_ids = map { $_->id } @{$self->_gcodegen->writer->extruders};
$gcode .= $self->_gcodegen->set_extruder($extruder_ids[0]);
# skip skirt if we have a large brim
if ($layer->id < $self->print->config->skirt_height || $self->print->has_infinite_skirt) {
my $skirt_flow = $self->print->skirt_flow;
# distribute skirt loops across all extruders
my @skirt_loops = @{$self->print->skirt};
for my $i (0 .. $#skirt_loops) {
# when printing layers > 0 ignore 'min_skirt_length' and
# just use the 'skirts' setting; also just use the current extruder
last if ($layer->id > 0) && ($i >= $self->print->config->skirts);
my $extruder_id = $extruder_ids[($i/@extruder_ids) % @extruder_ids];
$gcode .= $self->_gcodegen->set_extruder($extruder_id)
if $layer->id == 0;
# adjust flow according to this layer's layer height
my $loop = $skirt_loops[$i]->clone;
{
my $layer_skirt_flow = $skirt_flow->clone;
$layer_skirt_flow->set_height($layer->height);
my $mm3_per_mm = $layer_skirt_flow->mm3_per_mm;
foreach my $path (@$loop) {
$path->height($layer->height);
$path->mm3_per_mm($mm3_per_mm);
}
}
$gcode .= $self->_gcodegen->extrude_loop($loop, 'skirt', $object->config->support_material_speed);
}
}
$self->_skirt_done->{$layer->print_z} = 1;
$self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(0);
# allow a straight travel move to the first object point if this is the first layer
# (but don't in next layers)
if ($layer->id == 0) {
$self->_gcodegen->avoid_crossing_perimeters->set_disable_once(1);
}
}
# extrude brim
if (!$self->_brim_done) {
$gcode .= $self->_gcodegen->set_extruder($self->print->regions->[0]->config->perimeter_extruder-1);
$self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0));
$self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(1);
$gcode .= $self->_gcodegen->extrude_loop($_, 'brim', $object->config->support_material_speed)
for @{$self->print->brim};
$self->_brim_done(1);
$self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(0);
# allow a straight travel move to the first object point
$self->_gcodegen->avoid_crossing_perimeters->set_disable_once(1);
}
for my $copy (@$object_copies) {
# when starting a new object, use the external motion planner for the first travel move
$self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp_once(1) if ($self->_last_obj_copy // '') ne "$copy";
$self->_last_obj_copy("$copy");
$self->_gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
# extrude support material before other things because it might use a lower Z
# and also because we avoid travelling on other things when printing it
if ($layer->isa('Slic3r::Layer::Support') && $layer->support_fills->count > 0) {
if ($object->config->support_material_extruder == $object->config->support_material_interface_extruder) {
# Both the support and the support interface are printed with the same extruder, therefore
# the interface may be interleaved with the support base.
# Don't change extruder if the extruder is set to 0. Use the current extruder instead.
$gcode .= $self->_gcodegen->extrude_support(
$layer->support_fills->chained_path_from($self->_gcodegen->last_pos, 0),
$object->config->support_material_extruder);
} else {
# Extrude the support base before support interface for two reasons.
# 1) Support base may be extruded with the current extruder (extruder ID 0)
# and the support interface may be printed with the solube material,
# then one wants to avoid the base being printed with the soluble material.
# 2) It is likely better to print the interface after the base as the interface is
# often printed by bridges and it is convenient to have the base printed already,
# so the bridges may stick to it.
$gcode .= $self->_gcodegen->extrude_support(
$layer->support_fills->chained_path_from(
$self->_gcodegen->last_pos, 0, EXTR_ROLE_SUPPORTMATERIAL),
$object->config->support_material_extruder);
# Extrude the support interface.
$gcode .= $self->_gcodegen->extrude_support(
$layer->support_fills->chained_path_from(
$self->_gcodegen->last_pos, 0, EXTR_ROLE_SUPPORTMATERIAL_INTERFACE),
$object->config->support_material_interface_extruder);
}
}
# We now define a strategy for building perimeters and fills. The separation
# between regions doesn't matter in terms of printing order, as we follow
# another logic instead:
# - we group all extrusions by extruder so that we minimize toolchanges
# - we start from the last used extruder
# - for each extruder, we group extrusions by island
# - 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)
# group extrusions by extruder and then by island
my %by_extruder = (); # extruder_id => [ { perimeters => \@perimeters, infill => \@infill } ]
my $n_slices = $#{$layer->slices};
my @layer_surface_bboxes = ();
for my $i (0 .. $n_slices) {
push @layer_surface_bboxes, $layer->slices->[$i]->contour->bounding_box;
}
my $point_inside_surface = sub {
my ($i, $point) = @_;
my $bbox = $layer_surface_bboxes[$i];
return
$point->x >= $bbox->x_min && $point->x < $bbox->x_max &&
$point->y >= $bbox->y_min && $point->y < $bbox->y_max &&
$layer->slices->[$i]->contour->contains_point($point);
};
foreach my $region_id (0..($self->print->region_count-1)) {
my $layerm = $layer->regions->[$region_id] or next;
my $region = $self->print->get_region($region_id);
# process perimeters
{
my $extruder_id = $region->config->perimeter_extruder-1;
foreach my $perimeter_coll (@{$layerm->perimeters}) {
next if $perimeter_coll->empty; # this shouldn't happen but first_point() would fail
# init by_extruder item only if we actually use the extruder
$by_extruder{$extruder_id} //= [];
# $perimeter_coll is an ExtrusionPath::Collection object representing a single slice
for my $i (0 .. $n_slices) {
if (
# $perimeter_coll->first_point does not fit inside any slice
$i == $n_slices
# $perimeter_coll->first_point fits inside ith slice
|| $point_inside_surface->($i, $perimeter_coll->first_point)) {
$by_extruder{$extruder_id}[$i] //= { perimeters => {} };
$by_extruder{$extruder_id}[$i]{perimeters}{$region_id} //= [];
push @{ $by_extruder{$extruder_id}[$i]{perimeters}{$region_id} }, @$perimeter_coll;
last;
}
}
}
}
# process infill
# $layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection),
# each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
# throughout the code). We can redefine the order of such Collections but we have to
# do each one completely at once.
foreach my $fill (@{$layerm->fills}) {
next if $fill->empty; # this shouldn't happen but first_point() would fail
# init by_extruder item only if we actually use the extruder
my $extruder_id = $fill->[0]->is_solid_infill
? $region->config->solid_infill_extruder-1
: $region->config->infill_extruder-1;
$by_extruder{$extruder_id} //= [];
# $fill is an ExtrusionPath::Collection object
for my $i (0 .. $n_slices) {
if ($i == $n_slices
|| $point_inside_surface->($i, $fill->first_point)) {
$by_extruder{$extruder_id}[$i] //= { infill => {} };
$by_extruder{$extruder_id}[$i]{infill}{$region_id} //= [];
push @{ $by_extruder{$extruder_id}[$i]{infill}{$region_id} }, $fill;
last;
}
}
}
} # for regions
# tweak extruder ordering to save toolchanges
my @extruders = sort keys %by_extruder;
if (@extruders > 1) {
my $last_extruder_id = $self->_gcodegen->writer->extruder->id;
if (exists $by_extruder{$last_extruder_id}) {
@extruders = (
$last_extruder_id,
grep $_ != $last_extruder_id, @extruders,
);
}
}
foreach my $extruder_id (@extruders) {
$gcode .= $self->_gcodegen->set_extruder($extruder_id);
foreach my $island (@{ $by_extruder{$extruder_id} }) {
if ($self->print->config->infill_first) {
$gcode .= $self->_extrude_infill($island->{infill} // {});
$gcode .= $self->_extrude_perimeters($island->{perimeters} // {});
} else {
$gcode .= $self->_extrude_perimeters($island->{perimeters} // {});
$gcode .= $self->_extrude_infill($island->{infill} // {});
}
}
}
} # for object copies
# apply spiral vase post-processing if this layer contains suitable geometry
# (we must feed all the G-code into the post-processor, including the first
# bottom non-spiral layers otherwise it will mess with positions)
# we apply spiral vase at this stage because it requires a full layer
$gcode = $self->_spiral_vase->process_layer($gcode)
if defined $self->_spiral_vase;
# apply cooling logic; this may alter speeds
$gcode = $self->_cooling_buffer->append(
$gcode,
$layer->object->ptr . ref($layer), # differentiate $obj_id between normal layers and support layers
$layer->id,
$layer->print_z,
) if defined $self->_cooling_buffer;
$gcode = $self->filter($gcode);
print {$self->fh} $gcode if defined($gcode);
}
# Extrude perimeters: Decide where to put seams (hide or align seams).
sub _extrude_perimeters {
my ($self, $entities_by_region) = @_;
my $gcode = "";
foreach my $region_id (sort keys %$entities_by_region) {
$self->_gcodegen->config->apply_static($self->print->get_region($region_id)->config);
$gcode .= $self->_gcodegen->extrude($_, 'perimeter', -1)
for @{ $entities_by_region->{$region_id} };
}
return $gcode;
}
# Chain the paths hierarchically by a greedy algorithm to minimize a travel distance.
sub _extrude_infill {
my ($self, $entities_by_region) = @_;
my $gcode = "";
foreach my $region_id (sort keys %$entities_by_region) {
$self->_gcodegen->config->apply_static($self->print->get_region($region_id)->config);
my $collection = Slic3r::ExtrusionPath::Collection->new(@{ $entities_by_region->{$region_id} });
for my $fill (@{$collection->chained_path_from($self->_gcodegen->last_pos, 0)}) {
if ($fill->isa('Slic3r::ExtrusionPath::Collection')) {
$gcode .= $self->_gcodegen->extrude($_, 'infill', -1)
for @{$fill->chained_path_from($self->_gcodegen->last_pos, 0)};
} else {
$gcode .= $self->_gcodegen->extrude($fill, 'infill', -1) ;
}
}
}
return $gcode;
}
sub flush_filters {
my ($self) = @_;
print {$self->fh} $self->filter($self->_cooling_buffer->flush, 1);
}
sub filter {
my ($self, $gcode, $flush) = @_;
$flush //= 0;
# apply pressure equalization if enabled;
# print "G-code before filter:\n", $gcode;
$gcode = $self->_pressure_equalizer->process($gcode, $flush)
if defined $self->_pressure_equalizer;
# print "G-code after filter:\n", $gcode;
return $gcode;
}
1;

View File

@ -1,6 +1,7 @@
package Slic3r::Test;
use strict;
use warnings;
use Cwd 'abs_path';
require Exporter;
our @ISA = qw(Exporter);
@ -202,10 +203,19 @@ sub gcode {
$print = $print->print if $print->isa('Slic3r::Test::Print');
my $fh = IO::Scalar->new(\my $gcode);
# Write the resulting G-code into a temporary file.
my $gcode_temp_path = abs_path($0) . '.gcode.temp';
$print->process;
$print->export_gcode(output_fh => $fh, quiet => 1);
$fh->close;
$print->export_gcode(output_file => $gcode_temp_path, quiet => 1);
# Read the temoprary G-code file.
my $gcode;
{
local $/;
open my $fh, '<', $gcode_temp_path or die "Test.pm: can't open $gcode_temp_path: $!";
$gcode = <$fh>;
}
# Remove the temp file.
unlink $gcode_temp_path;
return $gcode;
}

View File

@ -304,8 +304,6 @@ $j
--use-relative-e-distances Enable this to get relative E values (default: no)
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
--use-volumetric-e Express E in cubic millimeters and prepend M200 (default: no)
--gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported
by all firmwares)
--gcode-comments Make G-code verbose by adding comments (default: no)
Filament options:

View File

@ -32,7 +32,7 @@ $config->set('disable_fan_first_layers', 0);
{
my $buffer = buffer($config);
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time + 1);
my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0.4) . $buffer->flush;
my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0) . $buffer->flush;
like $gcode, qr/F3000/, 'speed is not altered when elapsed time is greater than slowdown threshold';
}
@ -44,7 +44,7 @@ $config->set('disable_fan_first_layers', 0);
"G1 F3000;_EXTRUDE_SET_SPEED\n" .
"G1 X100 E1\n" .
"G1 E4 F400",
0, 0, 0.4
0, 0, 0
) . $buffer->flush;
unlike $gcode, qr/F3000/, 'speed is altered when elapsed time is lower than slowdown threshold';
like $gcode, qr/F2500/, 'speed is not altered for travel moves';
@ -54,7 +54,7 @@ $config->set('disable_fan_first_layers', 0);
{
my $buffer = buffer($config);
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time + 1);
my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0.4) . $buffer->flush;
my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0) . $buffer->flush;
unlike $gcode, qr/M106/, 'fan is not activated when elapsed time is greater than fan threshold';
}
@ -64,7 +64,7 @@ $config->set('disable_fan_first_layers', 0);
for my $obj_id (0 .. 1) {
# use an elapsed time which is < the slowdown threshold but greater than it when summed twice
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0.4);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0);
}
$gcode .= $buffer->flush;
like $gcode, qr/F3000/, 'slowdown is computed on all objects printing at same Z';
@ -77,7 +77,7 @@ $config->set('disable_fan_first_layers', 0);
for my $obj_id (0 .. 1) {
# use an elapsed time which is < the threshold but greater than it when summed twice
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0);
}
}
$gcode .= $buffer->flush;
@ -91,7 +91,7 @@ $config->set('disable_fan_first_layers', 0);
for my $obj_id (0 .. 1) {
# use an elapsed time which is < the threshold even when summed twice
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time/2 - 1);
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights
$gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0);
}
}
$gcode .= $buffer->flush;

View File

@ -514,7 +514,6 @@ xsp/Flow.xsp
xsp/GCode.xsp
xsp/GCodeSender.xsp
xsp/GCodeWriter.xsp
xsp/GCodePressureEqualizer.xsp
xsp/Geometry.xsp
xsp/GUI.xsp
xsp/GUI_3DScene.xsp

View File

@ -278,10 +278,7 @@ for my $class (qw(
Slic3r::Filler
Slic3r::Flow
Slic3r::GCode
Slic3r::GCode::AvoidCrossingPerimeters
Slic3r::GCode::OozePrevention
Slic3r::GCode::PlaceholderParser
Slic3r::GCode::Wipe
Slic3r::GCode::Writer
Slic3r::Geometry::BoundingBox
Slic3r::Geometry::BoundingBoxf

View File

@ -37,6 +37,9 @@ public:
Polylines unsupported_edges(double angle = -1) const;
private:
// Suppress warning "assignment operator could not be generated"
BridgeDetector& operator=(const BridgeDetector &);
void initialize();
struct BridgeDirection {

View File

@ -34,7 +34,7 @@ class ConfigOption {
virtual int getInt() const { return 0; };
virtual double getFloat() const { return 0; };
virtual bool getBool() const { return false; };
virtual void setInt(int val) {};
virtual void setInt(int /* val */) { };
friend bool operator== (const ConfigOption &a, const ConfigOption &b);
friend bool operator!= (const ConfigOption &a, const ConfigOption &b);
};
@ -142,7 +142,7 @@ class ConfigOptionInt : public ConfigOptionSingle<int>
{
public:
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(_value) {};
ConfigOptionInt(double _value) : ConfigOptionSingle<int>(int(floor(_value + 0.5))) {};
int getInt() const { return this->value; };
void setInt(int val) { this->value = val; };

View File

@ -326,7 +326,7 @@ namespace boost { namespace polygon {
}
// Get the winding direction of the polygon
static inline winding_direction winding(const Slic3r::ExPolygon& t) {
static inline winding_direction winding(const Slic3r::ExPolygon& /* t */) {
return unknown_winding;
}
};
@ -391,8 +391,8 @@ namespace boost { namespace polygon {
}
//don't worry about these, just return false from them
static inline bool clean(const Slic3r::ExPolygons& polygon_set) { return false; }
static inline bool sorted(const Slic3r::ExPolygons& polygon_set) { return false; }
static inline bool clean(const Slic3r::ExPolygons& /* polygon_set */) { return false; }
static inline bool sorted(const Slic3r::ExPolygons& /* polygon_set */) { return false; }
};
template <>

View File

@ -4,12 +4,12 @@ namespace Slic3r {
Extruder::Extruder(unsigned int id, GCodeConfig *config)
: id(id),
config(config)
m_config(config)
{
reset();
// cache values that are going to be called often
if (config->use_volumetric_e) {
if (m_config->use_volumetric_e) {
this->e_per_mm3 = this->extrusion_multiplier();
} else {
this->e_per_mm3 = this->extrusion_multiplier()
@ -31,7 +31,7 @@ double
Extruder::extrude(double dE)
{
// in case of relative E distances we always reset to 0 before any output
if (this->config->use_relative_e_distances)
if (m_config->use_relative_e_distances)
this->E = 0;
this->E += dE;
@ -50,7 +50,7 @@ double
Extruder::retract(double length, double restart_extra)
{
// in case of relative E distances we always reset to 0 before any output
if (this->config->use_relative_e_distances)
if (m_config->use_relative_e_distances)
this->E = 0;
double to_retract = length - this->retracted;
@ -84,7 +84,7 @@ Extruder::e_per_mm(double mm3_per_mm) const
double
Extruder::extruded_volume() const
{
if (this->config->use_volumetric_e) {
if (m_config->use_volumetric_e) {
// Any current amount of retraction should not affect used filament, since
// it represents empty volume in the nozzle. We add it back to E.
return this->absolute_E + this->retracted;
@ -96,7 +96,7 @@ Extruder::extruded_volume() const
double
Extruder::used_filament() const
{
if (this->config->use_volumetric_e) {
if (m_config->use_volumetric_e) {
return this->extruded_volume() / (this->filament_diameter() * this->filament_diameter() * PI/4);
}
@ -108,61 +108,61 @@ Extruder::used_filament() const
double
Extruder::filament_diameter() const
{
return this->config->filament_diameter.get_at(this->id);
return m_config->filament_diameter.get_at(this->id);
}
double
Extruder::filament_density() const
{
return this->config->filament_density.get_at(this->id);
return m_config->filament_density.get_at(this->id);
}
double
Extruder::filament_cost() const
{
return this->config->filament_cost.get_at(this->id);
return m_config->filament_cost.get_at(this->id);
}
double
Extruder::extrusion_multiplier() const
{
return this->config->extrusion_multiplier.get_at(this->id);
return m_config->extrusion_multiplier.get_at(this->id);
}
double
Extruder::retract_length() const
{
return this->config->retract_length.get_at(this->id);
return m_config->retract_length.get_at(this->id);
}
double
Extruder::retract_lift() const
{
return this->config->retract_lift.get_at(this->id);
return m_config->retract_lift.get_at(this->id);
}
int
Extruder::retract_speed() const
{
return this->config->retract_speed.get_at(this->id);
return m_config->retract_speed.get_at(this->id);
}
double
Extruder::retract_restart_extra() const
{
return this->config->retract_restart_extra.get_at(this->id);
return m_config->retract_restart_extra.get_at(this->id);
}
double
Extruder::retract_length_toolchange() const
{
return this->config->retract_length_toolchange.get_at(this->id);
return m_config->retract_length_toolchange.get_at(this->id);
}
double
Extruder::retract_restart_extra_toolchange() const
{
return this->config->retract_restart_extra_toolchange.get_at(this->id);
return m_config->retract_restart_extra_toolchange.get_at(this->id);
}
}

View File

@ -9,7 +9,7 @@ namespace Slic3r {
class Extruder
{
public:
public:
unsigned int id;
double E;
double absolute_E;
@ -20,7 +20,8 @@ class Extruder
Extruder(unsigned int id, GCodeConfig *config);
virtual ~Extruder() {}
void reset();
void reset();
double extrude(double dE);
double retract(double length, double restart_extra);
double unretract();
@ -34,15 +35,26 @@ class Extruder
double extrusion_multiplier() const;
double retract_length() const;
double retract_lift() const;
int retract_speed() const;
int retract_speed() const;
double retract_restart_extra() const;
double retract_length_toolchange() const;
double retract_restart_extra_toolchange() const;
private:
GCodeConfig *config;
static Extruder key(unsigned int id) { return Extruder(id); }
private:
// Private constructor to create a key for a search in std::set.
Extruder(unsigned int id) : id(id) {}
GCodeConfig *m_config;
};
// Sort Extruder objects by the extruder id by default.
inline bool operator==(const Extruder &e1, const Extruder &e2) { return e1.id == e2.id; }
inline bool operator!=(const Extruder &e1, const Extruder &e2) { return e1.id != e2.id; }
inline bool operator< (const Extruder &e1, const Extruder &e2) { return e1.id < e2.id; }
inline bool operator> (const Extruder &e1, const Extruder &e2) { return e1.id > e2.id; }
}
#endif

View File

@ -59,7 +59,7 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
{
// Instantiating the Flow class to get the line spacing.
// Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
Flow flow(this->width, this->height, 0.f, this->is_bridge());
Flow flow(this->width, this->height, 0.f, is_bridge(this->role()));
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
}
@ -226,7 +226,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
min = dist;
path_idx = path - this->paths.begin();
}
if (prefer_non_overhang && ! path->is_bridge() && dist < min_non_overhang) {
if (prefer_non_overhang && ! is_bridge(path->role()) && dist < min_non_overhang) {
p_non_overhang = p_tmp;
min_non_overhang = dist;
path_idx_non_overhang = path - this->paths.begin();
@ -291,7 +291,7 @@ ExtrusionLoop::has_overhang_point(const Point &point) const
if (pos != -1) {
// point belongs to this path
// we consider it overhang only if it's not an endpoint
return (path->is_bridge() && pos > 0 && pos != (int)(path->polyline.points.size())-1);
return (is_bridge(path->role()) && pos > 0 && pos != (int)(path->polyline.points.size())-1);
}
}
return false;

View File

@ -29,6 +29,33 @@ enum ExtrusionRole {
erMixed,
};
inline bool is_perimeter(ExtrusionRole role)
{
return role == erPerimeter
|| role == erExternalPerimeter
|| role == erOverhangPerimeter;
}
inline bool is_infill(ExtrusionRole role)
{
return role == erBridgeInfill
|| role == erInternalInfill
|| role == erSolidInfill
|| role == erTopSolidInfill;
}
inline bool is_solid_infill(ExtrusionRole role)
{
return role == erBridgeInfill
|| role == erSolidInfill
|| role == erTopSolidInfill;
}
inline bool is_bridge(ExtrusionRole role) {
return role == erBridgeInfill
|| role == erOverhangPerimeter;
}
/* Special flags describing loop */
enum ExtrusionLoopRole {
elrDefault,
@ -101,26 +128,6 @@ public:
void simplify(double tolerance);
virtual double length() const;
virtual ExtrusionRole role() const { return m_role; }
bool is_perimeter() const {
return this->m_role == erPerimeter
|| this->m_role == erExternalPerimeter
|| this->m_role == erOverhangPerimeter;
}
bool is_infill() const {
return this->m_role == erBridgeInfill
|| this->m_role == erInternalInfill
|| this->m_role == erSolidInfill
|| this->m_role == erTopSolidInfill;
}
bool is_solid_infill() const {
return this->m_role == erBridgeInfill
|| this->m_role == erSolidInfill
|| this->m_role == erTopSolidInfill;
}
bool is_bridge() const {
return this->m_role == erBridgeInfill
|| this->m_role == erOverhangPerimeter;
}
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@ -167,22 +174,6 @@ public:
Point last_point() const { return this->paths.back().polyline.points.back(); }
virtual double length() const;
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
bool is_perimeter() const {
return this->paths.front().role() == erPerimeter
|| this->paths.front().role() == erExternalPerimeter
|| this->paths.front().role() == erOverhangPerimeter;
}
bool is_infill() const {
return this->paths.front().role() == erBridgeInfill
|| this->paths.front().role() == erInternalInfill
|| this->paths.front().role() == erSolidInfill
|| this->paths.front().role() == erTopSolidInfill;
}
bool is_solid_infill() const {
return this->paths.front().role() == erBridgeInfill
|| this->paths.front().role() == erSolidInfill
|| this->paths.front().role() == erTopSolidInfill;
}
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@ -230,22 +221,6 @@ public:
bool has_overhang_point(const Point &point) const;
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
ExtrusionLoopRole loop_role() const { return m_loop_role; }
bool is_perimeter() const {
return this->paths.front().role() == erPerimeter
|| this->paths.front().role() == erExternalPerimeter
|| this->paths.front().role() == erOverhangPerimeter;
}
bool is_infill() const {
return this->paths.front().role() == erBridgeInfill
|| this->paths.front().role() == erInternalInfill
|| this->paths.front().role() == erSolidInfill
|| this->paths.front().role() == erTopSolidInfill;
}
bool is_solid_infill() const {
return this->paths.front().role() == erBridgeInfill
|| this->paths.front().role() == erSolidInfill
|| this->paths.front().role() == erTopSolidInfill;
}
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;

View File

@ -95,8 +95,14 @@ ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool
this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices);
}
void
ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
{
ExtrusionEntityCollection coll;
this->chained_path_from(start_near, &coll, no_reverse, role);
return coll;
}
void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const
{
if (this->no_sort) {
*retval = *this;

View File

@ -59,6 +59,7 @@ public:
void remove(size_t i);
ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector<size_t>* orig_indices = nullptr) const;
ExtrusionEntityCollection chained_path_from(Point start_near, bool no_reverse = false, ExtrusionRole role = erMixed) const;
void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, ExtrusionRole role = erMixed, std::vector<size_t>* orig_indices = nullptr) const;
void reverse();
Point first_point() const { return this->entities.front()->first_point(); }
@ -78,6 +79,8 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();

View File

@ -1,4 +1,5 @@
#include "Flow.hpp"
#include "Print.hpp"
#include <cmath>
#include <assert.h>
@ -113,4 +114,39 @@ float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float heig
#endif
}
Flow support_material_flow(const PrintObject *object, float layer_height)
{
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
false);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{
return Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : object->config.first_layer_height.get_abs_value(object->config.layer_height.value),
false);
}
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
{
return Flow::new_from_config_width(
frSupportMaterialInterface,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config.layer_height.value),
false);
}
}

View File

@ -7,6 +7,8 @@
namespace Slic3r {
class PrintObject;
// Extra spacing of bridge threads, in mm.
#define BRIDGE_EXTRA_SPACING 0.05
@ -59,6 +61,10 @@ public:
static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio);
};
extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f);
extern Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height = 0.f);
extern Flow support_material_interface_flow(const PrintObject *object, float layer_height = 0.f);
}
#endif

View File

@ -147,6 +147,9 @@ struct AMFParserContext
Instance *m_instance;
// Generic string buffer for vertices, face indices, metadata etc.
std::string m_value[3];
private:
AMFParserContext& operator=(AMFParserContext&);
};
void AMFParserContext::startElement(const char *name, const char **atts)
@ -307,7 +310,7 @@ void AMFParserContext::characters(const XML_Char *s, int len)
}
}
void AMFParserContext::endElement(const char *name)
void AMFParserContext::endElement(const char * /* name */)
{
switch (m_path.back()) {

File diff suppressed because it is too large Load Diff

View File

@ -10,16 +10,20 @@
#include "PlaceholderParser.hpp"
#include "Print.hpp"
#include "PrintConfig.hpp"
#include "GCode/CoolingBuffer.hpp"
#include "GCode/PressureEqualizer.hpp"
#include "GCode/SpiralVase.hpp"
#include "EdgeGrid.hpp"
#include <string>
namespace Slic3r {
// Forward declarations.
class GCode;
namespace EdgeGrid { class Grid; }
class AvoidCrossingPerimeters {
public:
public:
// this flag triggers the use of the external configuration space
bool use_external_mp;
@ -35,7 +39,7 @@ class AvoidCrossingPerimeters {
void init_layer_mp(const ExPolygons &islands);
Polyline travel_to(GCode &gcodegen, Point point);
private:
private:
MotionPlanner* _external_mp;
MotionPlanner* _layer_mp;
};
@ -65,76 +69,141 @@ public:
};
class GCode {
public:
public:
GCode() :
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_extrusion_role_markers(false),
m_enable_analyzer_markers(false),
m_layer_count(0),
m_layer_index(-1),
m_layer(nullptr),
m_first_layer(false),
m_elapsed_time(0.0),
m_volumetric_speed(0),
m_last_pos_defined(false),
m_last_extrusion_role(erNone),
m_brim_done(false),
m_second_layer_things_done(false),
m_last_obj_copy(Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
{}
~GCode() {}
bool do_export(FILE *file, Print &print);
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Pointf& origin() const { return m_origin; }
void set_origin(const Pointf &pointf);
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); }
const Point& last_pos() const { return m_last_pos; }
Pointf point_to_gcode(const Point &point) const;
const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; }
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
float get_reset_elapsed_time() { float t = m_elapsed_time; m_elapsed_time = 0.f; return t; }
// For Perl bindings, to be used exclusively by unit tests.
unsigned int layer_count() const { return m_layer_count; }
void set_layer_count(unsigned int value) { m_layer_count = value; }
float elapsed_time() const { return m_elapsed_time; }
void set_elapsed_time(float value) { m_elapsed_time = value; }
void apply_print_config(const PrintConfig &print_config);
private:
void process_layer(FILE *file, const Print &print, const Layer &layer, const Points &object_copies);
void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; }
bool last_pos_defined() const { return m_last_pos_defined; }
void set_extruders(const std::vector<unsigned int> &extruder_ids);
std::string preamble();
std::string change_layer(const Layer &layer);
std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1);
std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1);
std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1);
std::string extrude(ExtrusionPath path, std::string description = "", double speed = -1);
struct ByExtruder
{
struct ToExtrude {
ExtrusionEntityCollection perimeters;
ExtrusionEntityCollection infills;
};
std::vector<ToExtrude> by_region;
};
std::string extrude_perimeters(const Print &print, const std::vector<ByExtruder::ToExtrude> &by_region);
std::string extrude_infill(const Print &print, const std::vector<ByExtruder::ToExtrude> &by_region);
std::string extrude_support(const ExtrusionEntityCollection &support_fills, unsigned int extruder_id);
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone);
std::string retract(bool toolchange = false);
std::string unretract();
std::string set_extruder(unsigned int extruder_id);
/* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to()
methods. */
Pointf origin;
FullPrintConfig config;
GCodeWriter writer;
PlaceholderParser* placeholder_parser;
OozePrevention ooze_prevention;
Wipe wipe;
AvoidCrossingPerimeters avoid_crossing_perimeters;
bool enable_loop_clipping;
Pointf m_origin;
FullPrintConfig m_config;
GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser;
OozePrevention m_ooze_prevention;
Wipe m_wipe;
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
bool m_enable_loop_clipping;
// If enabled, the G-code generator will put following comments at the ends
// of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END
// Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module.
bool enable_cooling_markers;
bool m_enable_cooling_markers;
// Markers for the Pressure Equalizer to recognize the extrusion type.
// The Pressure Equalizer removes the markers from the final G-code.
bool enable_extrusion_role_markers;
bool m_enable_extrusion_role_markers;
// Extended markers for the G-code Analyzer.
// The G-code Analyzer will remove these comments from the final G-code.
bool enable_analyzer_markers;
bool m_enable_analyzer_markers;
// How many times will change_layer() be called?
// change_layer() will update the progress bar.
size_t layer_count;
unsigned int m_layer_count;
// Progress bar indicator. Increments from -1 up to layer_count.
int layer_index;
int m_layer_index;
// Current layer processed. Insequential printing mode, only a single copy will be printed.
// In non-sequential mode, all its copies will be printed.
const Layer* layer;
std::map<const PrintObject*,Point> _seam_position;
const Layer* m_layer;
std::map<const PrintObject*,Point> m_seam_position;
// Distance Field structure to
EdgeGrid::Grid *_lower_layer_edge_grid;
bool first_layer; // this flag triggers first layer speeds
// Used by the CoolingBuffer.pm Perl module to calculate time spent per layer change.
std::unique_ptr<EdgeGrid::Grid> m_lower_layer_edge_grid;
// this flag triggers first layer speeds
bool m_first_layer;
// Used by the CoolingBuffer G-code filter to calculate time spent per layer change.
// This value is not quite precise. First it only accouts for extrusion moves and travel moves,
// it does not account for wipe, retract / unretract moves.
// second it does not account for the velocity profiles of the printer.
float elapsed_time; // seconds
double volumetric_speed;
float m_elapsed_time; // seconds
double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active?
ExtrusionRole _last_extrusion_role;
ExtrusionRole m_last_extrusion_role;
GCode();
~GCode();
const Point& last_pos() const;
void set_last_pos(const Point &pos);
bool last_pos_defined() const;
void apply_print_config(const PrintConfig &print_config);
void set_extruders(const std::vector<unsigned int> &extruder_ids);
void set_origin(const Pointf &pointf);
std::string preamble();
std::string change_layer(const Layer &layer);
std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1);
std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1);
std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1);
std::string extrude(ExtrusionPath path, std::string description = "", double speed = -1);
std::string extrude_support(const ExtrusionEntityCollection *support_fills, unsigned int extruder_id);
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone);
std::string retract(bool toolchange = false);
std::string unretract();
std::string set_extruder(unsigned int extruder_id);
Pointf point_to_gcode(const Point &point);
Point m_last_pos;
bool m_last_pos_defined;
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
std::unique_ptr<SpiralVase> m_spiral_vase;
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
// Heights at which the skirt has already been extruded.
std::set<coordf_t> m_skirt_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
bool m_brim_done;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
bool m_second_layer_things_done;
// Index of a last object copy extruded. -1 for not set yet.
Point m_last_obj_copy;
private:
Point _last_pos;
bool _last_pos_defined;
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, bool wait);
std::string filter(std::string &&gcode, bool flush);
};
}

View File

@ -90,7 +90,7 @@ static inline int parse_int(const char *&line)
char *endptr = NULL;
long result = strtol(line, &endptr, 10);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw std::runtime_error("GCodePressureEqualizer: Error parsing an int");
throw std::runtime_error("GCodeAnalyzer: Error parsing an int");
line = endptr;
return int(result);
};
@ -102,7 +102,7 @@ static inline float parse_float(const char *&line)
char *endptr = NULL;
float result = strtof(line, &endptr);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw std::runtime_error("GCodePressureEqualizer: Error parsing a float");
throw std::runtime_error("GCodeAnalyzer: Error parsing a float");
line = endptr;
return result;
};
@ -171,7 +171,7 @@ bool GCodeAnalyzer::process_line(const char *line, const size_t len)
assert(false);
}
if (i == -1)
throw std::runtime_error(std::string("GCodePressureEqualizer: Invalid axis for G0/G1: ") + axis);
throw std::runtime_error(std::string("GCodeAnalyzer: Invalid axis for G0/G1: ") + axis);
buf.pos_provided[i] = true;
new_pos[i] = parse_float(line);
if (i == 3 && m_config->use_relative_e_distances.value)
@ -235,7 +235,7 @@ bool GCodeAnalyzer::process_line(const char *line, const size_t len)
set = true;
break;
default:
throw std::runtime_error(std::string("GCodePressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
throw std::runtime_error(std::string("GCodeAnalyzer: Incorrect axis in a G92 G-code: ") + axis);
}
eatws(line);
}

View File

@ -1,3 +1,4 @@
#include "../GCode.hpp"
#include "CoolingBuffer.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
@ -5,28 +6,26 @@
namespace Slic3r {
std::string
CoolingBuffer::append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z)
std::string CoolingBuffer::append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support)
{
std::string out;
if (this->_last_z.find(obj_id) != this->_last_z.end()) {
// A layer was finished, Z of the object's layer changed. Process the layer.
size_t signature = object_id * 2 + is_support ? 1 : 0;
if (m_object_ids_visited.find(signature) != m_object_ids_visited.end())
// For a single print_z, a combination of (object_id, is_support) could repeat once only.
// If the combination of (object_id, is_support) reappears, this must be for another print_z,
// therefore a layer has to be finalized.
out = this->flush();
}
this->_layer_id = layer_id;
this->_last_z[obj_id] = print_z;
this->_gcode += gcode;
m_object_ids_visited.insert(signature);
m_layer_id = layer_id;
m_gcode += gcode;
// This is a very rough estimate of the print time,
// not taking into account the acceleration curves generated by the printer firmware.
this->_elapsed_time += this->_gcodegen->elapsed_time;
this->_gcodegen->elapsed_time = 0;
m_elapsed_time += m_gcodegen.get_reset_elapsed_time();
return out;
}
void
apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
void apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
{
// find pos of F
size_t pos = line.find_first_of('F');
@ -51,36 +50,34 @@ apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
}
}
std::string
CoolingBuffer::flush()
std::string CoolingBuffer::flush()
{
GCode &gg = *this->_gcodegen;
const FullPrintConfig &config = m_gcodegen.config();
std::string gcode = this->_gcode;
float elapsed = this->_elapsed_time;
this->_gcode = "";
this->_elapsed_time = 0;
this->_last_z.clear(); // reset the whole table otherwise we would compute overlapping times
std::string gcode = m_gcode;
float elapsed = m_elapsed_time;
m_gcode.clear();
m_elapsed_time = 0.;
int fan_speed = gg.config.fan_always_on ? gg.config.min_fan_speed.value : 0;
int fan_speed = config.fan_always_on ? config.min_fan_speed.value : 0;
float speed_factor = 1.0;
if (gg.config.cooling) {
if (config.cooling) {
#ifdef SLIC3R_DEBUG
printf("Layer %zu estimated printing time: %f seconds\n", this->_layer_id, elapsed);
printf("Layer %zu estimated printing time: %f seconds\n", m_layer_id, elapsed);
#endif
if (elapsed < (float)gg.config.slowdown_below_layer_time) {
if (elapsed < (float)config.slowdown_below_layer_time) {
// Layer time very short. Enable the fan to a full throttle and slow down the print
// (stretch the layer print time to slowdown_below_layer_time).
fan_speed = gg.config.max_fan_speed;
speed_factor = elapsed / (float)gg.config.slowdown_below_layer_time;
} else if (elapsed < (float)gg.config.fan_below_layer_time) {
fan_speed = config.max_fan_speed;
speed_factor = elapsed / (float)config.slowdown_below_layer_time;
} else if (elapsed < (float)config.fan_below_layer_time) {
// Layer time quite short. Enable the fan proportionally according to the current layer time.
fan_speed = gg.config.max_fan_speed
- (gg.config.max_fan_speed - gg.config.min_fan_speed)
* (elapsed - (float)gg.config.slowdown_below_layer_time)
/ (gg.config.fan_below_layer_time - gg.config.slowdown_below_layer_time);
fan_speed = config.max_fan_speed
- (config.max_fan_speed - config.min_fan_speed)
* (elapsed - (float)config.slowdown_below_layer_time)
/ (config.fan_below_layer_time - config.slowdown_below_layer_time);
}
#ifdef SLIC3R_DEBUG
@ -94,13 +91,14 @@ CoolingBuffer::flush()
std::string new_gcode;
std::istringstream ss(gcode);
std::string line;
bool bridge_fan_start = false;
bool bridge_fan_start = false;
float min_print_speed = float(config.min_print_speed * 60.);
while (std::getline(ss, line)) {
if (boost::starts_with(line, "G1")
&& boost::contains(line, ";_EXTRUDE_SET_SPEED")
&& !boost::contains(line, ";_WIPE")
&& !bridge_fan_start) {
apply_speed_factor(line, speed_factor, this->_min_print_speed);
apply_speed_factor(line, speed_factor, min_print_speed);
boost::replace_first(line, ";_EXTRUDE_SET_SPEED", "");
}
bridge_fan_start = boost::contains(line, ";_BRIDGE_FAN_START");
@ -109,22 +107,23 @@ CoolingBuffer::flush()
gcode = new_gcode;
}
}
if (this->_layer_id < gg.config.disable_fan_first_layers)
if (m_layer_id < config.disable_fan_first_layers)
fan_speed = 0;
gcode = gg.writer.set_fan(fan_speed) + gcode;
gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode;
// bridge fan speed
if (!gg.config.cooling || gg.config.bridge_fan_speed == 0 || this->_layer_id < gg.config.disable_fan_first_layers) {
if (!config.cooling || config.bridge_fan_speed == 0 || m_layer_id < config.disable_fan_first_layers) {
boost::replace_all(gcode, ";_BRIDGE_FAN_START", "");
boost::replace_all(gcode, ";_BRIDGE_FAN_END", "");
} else {
boost::replace_all(gcode, ";_BRIDGE_FAN_START", gg.writer.set_fan(gg.config.bridge_fan_speed, true));
boost::replace_all(gcode, ";_BRIDGE_FAN_END", gg.writer.set_fan(fan_speed, true));
boost::replace_all(gcode, ";_BRIDGE_FAN_START", m_gcodegen.writer().set_fan(config.bridge_fan_speed, true));
boost::replace_all(gcode, ";_BRIDGE_FAN_END", m_gcodegen.writer().set_fan(fan_speed, true));
}
boost::replace_all(gcode, ";_WIPE", "");
boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", "");
m_object_ids_visited.clear();
return gcode;
}

View File

@ -2,12 +2,14 @@
#define slic3r_CoolingBuffer_hpp_
#include "libslic3r.h"
#include "GCode.hpp"
#include <map>
#include <string>
namespace Slic3r {
class GCode;
class Layer;
/*
A standalone G-code filter, to control cooling of the print.
The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
@ -15,23 +17,20 @@ and the print is modified to stretch over a minimum layer time.
*/
class CoolingBuffer {
public:
CoolingBuffer(GCode &gcodegen)
: _gcodegen(&gcodegen), _elapsed_time(0.), _layer_id(0)
{
this->_min_print_speed = this->_gcodegen->config.min_print_speed * 60;
};
std::string append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z);
public:
CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_elapsed_time(0.), m_layer_id(0) {}
std::string append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support);
std::string flush();
GCode* gcodegen() { return this->_gcodegen; };
GCode* gcodegen() { return &m_gcodegen; };
private:
GCode* _gcodegen;
std::string _gcode;
float _elapsed_time;
size_t _layer_id;
std::map<std::string,float> _last_z;
float _min_print_speed;
private:
CoolingBuffer& operator=(const CoolingBuffer&);
GCode& m_gcodegen;
std::string m_gcode;
float m_elapsed_time;
size_t m_layer_id;
std::set<size_t> m_object_ids_visited;
};
}

View File

@ -9,17 +9,17 @@
namespace Slic3r {
GCodePressureEqualizer::GCodePressureEqualizer(const Slic3r::GCodeConfig *config) :
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) :
m_config(config)
{
reset();
}
GCodePressureEqualizer::~GCodePressureEqualizer()
PressureEqualizer::~PressureEqualizer()
{
}
void GCodePressureEqualizer::reset()
void PressureEqualizer::reset()
{
circular_buffer_pos = 0;
circular_buffer_size = 100;
@ -69,7 +69,7 @@ void GCodePressureEqualizer::reset()
line_idx = 0;
}
const char* GCodePressureEqualizer::process(const char *szGCode, bool flush)
const char* PressureEqualizer::process(const char *szGCode, bool flush)
{
// Reset length of the output_buffer.
output_buffer_length = 0;
@ -147,7 +147,7 @@ static inline int parse_int(const char *&line)
char *endptr = NULL;
long result = strtol(line, &endptr, 10);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw std::runtime_error("GCodePressureEqualizer: Error parsing an int");
throw std::runtime_error("PressureEqualizer: Error parsing an int");
line = endptr;
return int(result);
};
@ -159,13 +159,13 @@ static inline float parse_float(const char *&line)
char *endptr = NULL;
float result = strtof(line, &endptr);
if (endptr == NULL || !is_ws_or_eol(*endptr))
throw std::runtime_error("GCodePressureEqualizer: Error parsing a float");
throw std::runtime_error("PressureEqualizer: Error parsing a float");
line = endptr;
return result;
};
#define EXTRUSION_ROLE_TAG ";_EXTRUSION_ROLE:"
bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf)
bool PressureEqualizer::process_line(const char *line, const size_t len, GCodeLine &buf)
{
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
line += strlen(EXTRUSION_ROLE_TAG);
@ -228,7 +228,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC
assert(false);
}
if (i == -1)
throw std::runtime_error(std::string("GCodePressureEqualizer: Invalid axis for G0/G1: ") + axis);
throw std::runtime_error(std::string("GCode::PressureEqualizer: Invalid axis for G0/G1: ") + axis);
buf.pos_provided[i] = true;
new_pos[i] = parse_float(line);
if (i == 3 && m_config->use_relative_e_distances.value)
@ -297,7 +297,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC
set = true;
break;
default:
throw std::runtime_error(std::string("GCodePressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
throw std::runtime_error(std::string("GCode::PressureEqualizer: Incorrect axis in a G92 G-code: ") + axis);
}
eatws(line);
}
@ -355,7 +355,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC
return true;
}
void GCodePressureEqualizer::output_gcode_line(GCodeLine &line)
void PressureEqualizer::output_gcode_line(GCodeLine &line)
{
if (! line.modified) {
push_to_output(line.raw.data(), line.raw_length, true);
@ -453,7 +453,7 @@ void GCodePressureEqualizer::output_gcode_line(GCodeLine &line)
}
}
void GCodePressureEqualizer::adjust_volumetric_rate()
void PressureEqualizer::adjust_volumetric_rate()
{
if (circular_buffer_items < 2)
return;
@ -563,7 +563,7 @@ void GCodePressureEqualizer::adjust_volumetric_rate()
}
}
void GCodePressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol)
void PressureEqualizer::push_axis_to_output(const char axis, const float value, bool add_eol)
{
char buf[2048];
int len = sprintf(buf,
@ -572,7 +572,7 @@ void GCodePressureEqualizer::push_axis_to_output(const char axis, const float va
push_to_output(buf, len, add_eol);
}
void GCodePressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
void PressureEqualizer::push_to_output(const char *text, const size_t len, bool add_eol)
{
// New length of the output buffer content.
size_t len_new = output_buffer_length + len + 1;
@ -604,7 +604,7 @@ void GCodePressureEqualizer::push_to_output(const char *text, const size_t len,
output_buffer[output_buffer_length] = 0;
}
void GCodePressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
void PressureEqualizer::push_line_to_output(const GCodeLine &line, const float new_feedrate, const char *comment)
{
push_to_output("G1", 2, false);
for (char i = 0; i < 3; ++ i)

View File

@ -9,11 +9,11 @@ namespace Slic3r {
// Processes a G-code. Finds changes in the volumetric extrusion speed and adjusts the transitions
// between these paths to limit fast changes in the volumetric extrusion speed.
class GCodePressureEqualizer
class PressureEqualizer
{
public:
GCodePressureEqualizer(const Slic3r::GCodeConfig *config);
~GCodePressureEqualizer();
PressureEqualizer(const Slic3r::GCodeConfig *config);
~PressureEqualizer();
void reset();

View File

@ -1,4 +1,5 @@
#include "SpiralVase.hpp"
#include "GCode.hpp"
#include <sstream>
namespace Slic3r {

View File

@ -2,7 +2,6 @@
#define slic3r_SpiralVase_hpp_
#include "libslic3r.h"
#include "GCode.hpp"
#include "GCodeReader.hpp"
namespace Slic3r {

View File

@ -23,8 +23,8 @@ GCodeWriter::apply_print_config(const PrintConfig &print_config)
void
GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
{
for (std::vector<unsigned int>::const_iterator i = extruder_ids.begin(); i != extruder_ids.end(); ++i)
this->extruders.insert( std::pair<unsigned int,Extruder>(*i, Extruder(*i, &this->config)) );
for (unsigned int extruder_id : extruder_ids)
this->extruders.insert(Extruder(extruder_id, &this->config));
/* we enable support for multiple extruder if any extruder greater than 0 is used
(even if prints only uses that one) since we need to output Tx commands
@ -194,9 +194,10 @@ GCodeWriter::reset_e(bool force)
|| FLAVOR_IS(gcfSailfish))
return "";
if (this->_extruder != NULL) {
if (this->_extruder->E == 0 && !force) return "";
this->_extruder->E = 0;
if (this->_extruder != nullptr) {
if (this->_extruder->E == 0. && ! force)
return "";
this->_extruder->E = 0.;
}
if (!this->_extrusion_axis.empty() && !this->config.use_relative_e_distances) {
@ -226,25 +227,10 @@ GCodeWriter::update_progress(unsigned int num, unsigned int tot, bool allow_100)
return gcode.str();
}
bool
GCodeWriter::need_toolchange(unsigned int extruder_id) const
{
// return false if this extruder was already selected
return (this->_extruder == NULL) || (this->_extruder->id != extruder_id);
}
std::string
GCodeWriter::set_extruder(unsigned int extruder_id)
{
if (!this->need_toolchange(extruder_id)) return "";
return this->toolchange(extruder_id);
}
std::string
GCodeWriter::toolchange(unsigned int extruder_id)
std::string GCodeWriter::toolchange(unsigned int extruder_id)
{
// set the new extruder
this->_extruder = &this->extruders.find(extruder_id)->second;
this->_extruder = const_cast<Extruder*>(&*this->extruders.find(Extruder::key(extruder_id)));
// return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing

View File

@ -12,17 +12,25 @@ namespace Slic3r {
class GCodeWriter {
public:
GCodeConfig config;
std::map<unsigned int,Extruder> extruders;
std::set<Extruder> extruders;
bool multiple_extruders;
GCodeWriter()
: multiple_extruders(false), _extrusion_axis("E"), _extruder(NULL),
_last_acceleration(0), _last_fan_speed(0), _lifted(0)
{};
Extruder* extruder() const { return this->_extruder; }
GCodeWriter() :
multiple_extruders(false), _extrusion_axis("E"), _extruder(nullptr),
_last_acceleration(0), _last_fan_speed(0), _lifted(0)
{}
Extruder* extruder() { return this->_extruder; }
const Extruder* extruder() const { return this->_extruder; }
std::string extrusion_axis() const { return this->_extrusion_axis; }
void apply_print_config(const PrintConfig &print_config);
void set_extruders(const std::vector<unsigned int> &extruder_ids);
void apply_print_config(const PrintConfig &print_config);
void set_extruders(const std::vector<unsigned int> &extruder_ids);
std::vector<unsigned int> extruder_ids() const {
std::vector<unsigned int> out;
out.reserve(extruders.size());
for (const auto e : extruders)
out.push_back(e.id);
return out;
}
std::string preamble();
std::string postamble() const;
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const;
@ -31,14 +39,17 @@ public:
std::string set_acceleration(unsigned int acceleration);
std::string reset_e(bool force = false);
std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const;
bool need_toolchange(unsigned int extruder_id) const;
std::string set_extruder(unsigned int extruder_id);
// return false if this extruder was already selected
bool need_toolchange(unsigned int extruder_id) const
{ return (this->_extruder == nullptr) || (this->_extruder->id != extruder_id); }
std::string set_extruder(unsigned int extruder_id)
{ return this->need_toolchange(extruder_id) ? this->toolchange(extruder_id) : ""; }
std::string toolchange(unsigned int extruder_id);
std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const;
std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string());
std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string());
std::string travel_to_z(double z, const std::string &comment = std::string());
bool will_move_z(double z) const;
bool will_move_z(double z) const;
std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string());
std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string());
std::string retract();
@ -46,14 +57,15 @@ public:
std::string unretract();
std::string lift();
std::string unlift();
Pointf3 get_position() const { return this->_pos; }
Pointf3 get_position() const { return this->_pos; }
private:
std::string _extrusion_axis;
Extruder* _extruder;
unsigned int _last_acceleration;
unsigned int _last_fan_speed;
double _lifted;
Pointf3 _pos;
std::string _extrusion_axis;
Extruder* _extruder;
unsigned int _last_acceleration;
unsigned int _last_fan_speed;
double _lifted;
Pointf3 _pos;
std::string _travel_to_z(double z, const std::string &comment);
std::string _retract(double length, double restart_extra, const std::string &comment);

View File

@ -97,23 +97,34 @@ PlaceholderParser::apply_env_variables()
}
}
void
PlaceholderParser::set(const std::string &key, const std::string &value)
void PlaceholderParser::set(const std::string &key, const std::string &value)
{
this->_single[key] = value;
this->_multiple.erase(key);
}
void
PlaceholderParser::set(const std::string &key, int value)
void PlaceholderParser::set(const std::string &key, int value)
{
std::ostringstream ss;
ss << value;
this->set(key, ss.str());
}
void
PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
void PlaceholderParser::set(const std::string &key, unsigned int value)
{
std::ostringstream ss;
ss << value;
this->set(key, ss.str());
}
void PlaceholderParser::set(const std::string &key, double value)
{
std::ostringstream ss;
ss << value;
this->set(key, ss.str());
}
void PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
{
if (values.empty()) {
this->_multiple.erase(key);
@ -124,8 +135,7 @@ PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
}
}
std::string
PlaceholderParser::process(std::string str) const
std::string PlaceholderParser::process(std::string str) const
{
// replace single options, like [foo]
for (t_strstr_map::const_iterator it = this->_single.begin(); it != this->_single.end(); ++it) {
@ -154,8 +164,7 @@ PlaceholderParser::process(std::string str) const
return str;
}
bool
PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const
bool PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const
{
bool found = false;
for (std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos; ) {

View File

@ -15,7 +15,7 @@ typedef std::map<std::string, std::vector<std::string> > t_strstrs_map;
class PlaceholderParser
{
public:
public:
t_strstr_map _single;
t_strstrs_map _multiple;
@ -25,10 +25,12 @@ class PlaceholderParser
void apply_env_variables();
void set(const std::string &key, const std::string &value);
void set(const std::string &key, int value);
void set(const std::string &key, unsigned int value);
void set(const std::string &key, double value);
void set(const std::string &key, std::vector<std::string> values);
std::string process(std::string str) const;
private:
private:
bool find_and_replace(std::string &source, std::string const &find, std::string const &replace) const;
};

View File

@ -32,7 +32,7 @@ class Point
coord_t y;
Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
Point(int _x, int _y): x(_x), y(_y) {};
Point(long long _x, long long _y): x(_x), y(_y) {}; // for Clipper
Point(long long _x, long long _y): x(coord_t(_x)), y(coord_t(_y)) {}; // for Clipper
Point(double x, double y);
static Point new_scale(coordf_t x, coordf_t y) {
return Point(scale_(x), scale_(y));

View File

@ -93,6 +93,23 @@ inline void polygons_rotate(Polygons &polys, double angle)
p->rotate(angle);
}
inline Points to_points(const Polygon &poly)
{
return poly.points;
}
inline Points to_points(const Polygons &polys)
{
size_t n_points = 0;
for (size_t i = 0; i < polys.size(); ++ i)
n_points += polys[i].points.size();
Points points;
points.reserve(n_points);
for (const Polygon &poly : polys)
append(points, poly.points);
return points;
}
inline Lines to_lines(const Polygon &poly)
{
Lines lines;
@ -179,7 +196,7 @@ namespace boost { namespace polygon {
}
// Get the winding direction of the polygon
static inline winding_direction winding(const Slic3r::Polygon& t) {
static inline winding_direction winding(const Slic3r::Polygon& /* t */) {
return unknown_winding;
}
};
@ -220,8 +237,8 @@ namespace boost { namespace polygon {
}
//don't worry about these, just return false from them
static inline bool clean(const Slic3r::Polygons& polygon_set) { return false; }
static inline bool sorted(const Slic3r::Polygons& polygon_set) { return false; }
static inline bool clean(const Slic3r::Polygons& /* polygon_set */) { return false; }
static inline bool sorted(const Slic3r::Polygons& /* polygon_set */) { return false; }
};
template <>

View File

@ -30,7 +30,7 @@ Print::~Print()
void
Print::clear_objects()
{
for (int i = this->objects.size()-1; i >= 0; --i)
for (int i = int(this->objects.size())-1; i >= 0; --i)
this->delete_object(i);
this->clear_regions();
@ -255,70 +255,65 @@ Print::step_done(PrintObjectStep step) const
}
// returns 0-based indices of used extruders
std::set<size_t>
Print::object_extruders() const
std::vector<unsigned int> Print::object_extruders() const
{
std::set<size_t> extruders;
std::vector<unsigned int> extruders;
FOREACH_REGION(this, region) {
// these checks reflect the same logic used in the GUI for enabling/disabling
// extruder selection fields
if ((*region)->config.perimeters.value > 0 || this->config.brim_width.value > 0)
extruders.insert((*region)->config.perimeter_extruder - 1);
extruders.push_back((*region)->config.perimeter_extruder - 1);
if ((*region)->config.fill_density.value > 0)
extruders.insert((*region)->config.infill_extruder - 1);
extruders.push_back((*region)->config.infill_extruder - 1);
if ((*region)->config.top_solid_layers.value > 0 || (*region)->config.bottom_solid_layers.value > 0)
extruders.insert((*region)->config.solid_infill_extruder - 1);
extruders.push_back((*region)->config.solid_infill_extruder - 1);
}
std::sort(extruders.begin(), extruders.end());
extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end());
return extruders;
}
// returns 0-based indices of used extruders
std::set<size_t>
Print::support_material_extruders() const
std::vector<unsigned int> Print::support_material_extruders() const
{
std::set<size_t> extruders;
std::vector<unsigned int> extruders;
bool support_uses_current_extruder = false;
FOREACH_OBJECT(this, object) {
if ((*object)->has_support_material()) {
if ((*object)->config.support_material_extruder == 0)
for (PrintObject *object : this->objects) {
if (object->has_support_material()) {
if (object->config.support_material_extruder == 0)
support_uses_current_extruder = true;
else
extruders.insert((*object)->config.support_material_extruder - 1);
if ((*object)->config.support_material_interface_extruder == 0)
extruders.push_back(object->config.support_material_extruder - 1);
if (object->config.support_material_interface_extruder == 0)
support_uses_current_extruder = true;
else
extruders.insert((*object)->config.support_material_interface_extruder - 1);
extruders.push_back(object->config.support_material_interface_extruder - 1);
}
}
if (support_uses_current_extruder) {
if (support_uses_current_extruder)
// Add all object extruders to the support extruders as it is not know which one will be used to print supports.
std::set<size_t> object_extruders = this->object_extruders();
extruders.insert(object_extruders.begin(), object_extruders.end());
}
append(extruders, this->object_extruders());
std::sort(extruders.begin(), extruders.end());
extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end());
return extruders;
}
// returns 0-based indices of used extruders
std::set<size_t>
Print::extruders() const
std::vector<unsigned int> Print::extruders() const
{
std::set<size_t> extruders = this->object_extruders();
std::set<size_t> s_extruders = this->support_material_extruders();
extruders.insert(s_extruders.begin(), s_extruders.end());
std::vector<unsigned int> extruders = this->object_extruders();
append(extruders, this->support_material_extruders());
std::sort(extruders.begin(), extruders.end());
extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end());
return extruders;
}
void
Print::_simplify_slices(double distance)
void Print::_simplify_slices(double distance)
{
FOREACH_OBJECT(this, object) {
FOREACH_LAYER(*object, layer) {
@ -330,23 +325,17 @@ Print::_simplify_slices(double distance)
}
}
double
Print::max_allowed_layer_height() const
double Print::max_allowed_layer_height() const
{
std::vector<double> nozzle_diameter;
std::set<size_t> extruders = this->extruders();
for (std::set<size_t>::const_iterator e = extruders.begin(); e != extruders.end(); ++e) {
nozzle_diameter.push_back(this->config.nozzle_diameter.get_at(*e));
}
return *std::max_element(nozzle_diameter.begin(), nozzle_diameter.end());
double nozzle_diameter_max = 0.;
for (unsigned int extruder_id : this->extruders())
nozzle_diameter_max = std::max(nozzle_diameter_max, this->config.nozzle_diameter.get_at(extruder_id));
return nozzle_diameter_max;
}
/* Caller is responsible for supplying models whose objects don't collide
and have explicit instance positions */
void
Print::add_model_object(ModelObject* model_object, int idx)
void Print::add_model_object(ModelObject* model_object, int idx)
{
DynamicPrintConfig object_config = model_object->config; // clone
object_config.normalize();
@ -617,9 +606,9 @@ Print::validate() const
convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, jtRound, scale_(0.1)).front();
// now we check that no instance of convex_hull intersects any of the previously checked object instances
for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) {
for (const Point &copy : object->_shifted_copies) {
Polygon p = convex_hull;
p.translate(*copy);
p.translate(copy);
if (! intersection(a, p).empty())
return "Some objects are too close; your extruder will collide with them.";
polygons_append(a, p);
@ -654,13 +643,13 @@ Print::validate() const
{
// find the smallest nozzle diameter
std::set<size_t> extruders = this->extruders();
std::vector<unsigned int> extruders = this->extruders();
if (extruders.empty())
return "The supplied settings will cause an empty print.";
std::set<double> nozzle_diameters;
for (std::set<size_t>::iterator it = extruders.begin(); it != extruders.end(); ++it)
nozzle_diameters.insert(this->config.nozzle_diameter.get_at(*it));
std::vector<double> nozzle_diameters;
for (unsigned int extruder_id : extruders)
nozzle_diameters.push_back(this->config.nozzle_diameter.get_at(extruder_id));
double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end());
FOREACH_OBJECT(this, i_object) {

View File

@ -117,7 +117,7 @@ public:
ModelObject* model_object() { return this->_model_object; }
const ModelObject* model_object() const { return this->_model_object; }
Points copies() const { return this->_copies; }
const Points& copies() const { return this->_copies; }
bool add_copy(const Pointf &point);
bool delete_last_copy();
bool delete_all_copies();
@ -249,9 +249,9 @@ public:
Flow brim_flow() const;
Flow skirt_flow() const;
std::set<size_t> object_extruders() const;
std::set<size_t> support_material_extruders() const;
std::set<size_t> extruders() const;
std::vector<unsigned int> object_extruders() const;
std::vector<unsigned int> support_material_extruders() const;
std::vector<unsigned int> extruders() const;
void _simplify_slices(double distance);
double max_allowed_layer_height() const;
bool has_support_material() const;

View File

@ -44,7 +44,7 @@ SlicingParameters SlicingParameters::create_from_config(
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::set<size_t> &object_extruders)
const std::vector<unsigned int> &object_extruders)
{
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
object_config.layer_height.value :
@ -84,9 +84,9 @@ SlicingParameters SlicingParameters::create_from_config(
params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, 0));
params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, 0));
} else {
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) {
params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, *it_extruder));
params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, *it_extruder));
for (unsigned int extruder_id : object_extruders) {
params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, extruder_id));
params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, extruder_id));
}
}
params.min_layer_height = std::min(params.min_layer_height, params.layer_height);
@ -113,8 +113,8 @@ SlicingParameters SlicingParameters::create_from_config(
//FIXME It is expected, that the 1st layer of the object is printed with a bridging flow over a full raft. Shall it not be vice versa?
coordf_t average_object_extruder_dmr = 0.;
if (! object_extruders.empty()) {
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder);
for (unsigned int extruder_id : object_extruders)
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(extruder_id);
average_object_extruder_dmr /= coordf_t(object_extruders.size());
}
params.first_object_layer_height = average_object_extruder_dmr;

View File

@ -26,7 +26,7 @@ struct SlicingParameters
const PrintConfig &print_config,
const PrintObjectConfig &object_config,
coordf_t object_height,
const std::set<size_t> &object_extruders);
const std::vector<unsigned int> &object_extruders);
// Has any raft layers?
bool has_raft() const { return raft_layers() > 0; }

View File

@ -145,32 +145,10 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
m_print_config (&object->print()->config),
m_object_config (&object->config),
m_slicing_params (slicing_params),
m_first_layer_flow (Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->print()->config.first_layer_extrusion_width.value > 0) ? object->print()->config.first_layer_extrusion_width : object->config.support_material_extrusion_width,
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
float(slicing_params.first_print_layer_height),
false)),
m_support_material_flow (Flow::new_from_config_width(
frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width.value > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_extruder-1)),
float(slicing_params.layer_height),
false)),
m_support_material_interface_flow(Flow::new_from_config_width(
frSupportMaterialInterface,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(object->config.support_material_extrusion_width > 0) ? object->config.support_material_extrusion_width : object->config.extrusion_width,
// if object->config.support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config.nozzle_diameter.get_at(object->config.support_material_interface_extruder-1)),
float(slicing_params.layer_height),
false)),
m_support_layer_height_min (0.01)
m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))),
m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))),
m_support_material_interface_flow(support_material_interface_flow(object, float(slicing_params.layer_height))),
m_support_layer_height_min(0.01)
{
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
m_support_layer_height_min = 1000000.;

View File

@ -9,7 +9,7 @@ namespace Slic3r {
class SurfaceCollection
{
public:
public:
Surfaces surfaces;
SurfaceCollection() {};

View File

@ -14,15 +14,10 @@ REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
REGISTER_CLASS(ExtrusionSimulator, "ExtrusionSimulator");
REGISTER_CLASS(Filler, "Filler");
REGISTER_CLASS(Flow, "Flow");
REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters");
REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer");
REGISTER_CLASS(OozePrevention, "GCode::OozePrevention");
REGISTER_CLASS(SpiralVase, "GCode::SpiralVase");
REGISTER_CLASS(Wipe, "GCode::Wipe");
REGISTER_CLASS(GCode, "GCode");
REGISTER_CLASS(GCodeSender, "GCode::Sender");
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
REGISTER_CLASS(GCodePressureEqualizer, "GCode::PressureEqualizer");
REGISTER_CLASS(Layer, "Layer");
REGISTER_CLASS(SupportLayer, "Layer::Support");
REGISTER_CLASS(LayerRegion, "Layer::Region");

View File

@ -1,6 +1,11 @@
#ifndef _xsinit_h_
#define _xsinit_h_
#ifdef _MSC_VER
// Disable some obnoxious warnings given by Visual Studio with the default warning level 4.
#pragma warning(disable: 4100 4127 4189 4244 4267 4700 4702 4800)
#endif
// undef some macros set by Perl which cause compilation errors on Win32
#undef read
#undef seekdir

View File

@ -29,9 +29,6 @@
%code{% RETVAL = THIS->has_overhang_point(*point); %};
ExtrusionRole role() const;
ExtrusionLoopRole loop_role() const;
bool is_perimeter();
bool is_infill();
bool is_solid_infill();
Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing();
%{

View File

@ -16,9 +16,6 @@
void append(ExtrusionPath* path)
%code{% THIS->paths.push_back(*path); %};
double length();
bool is_perimeter();
bool is_infill();
bool is_solid_infill();
Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing();
Clone<Polyline> polyline()

View File

@ -23,10 +23,8 @@
void simplify(double tolerance);
double length();
ExtrusionRole role() const;
bool is_perimeter();
bool is_infill();
bool is_solid_infill();
bool is_bridge();
bool is_bridge()
%code{% RETVAL = is_bridge(THIS->role()); %};
Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing();
%{

View File

@ -4,175 +4,44 @@
#include <xsinit.h>
#include "libslic3r/GCode.hpp"
#include "libslic3r/GCode/CoolingBuffer.hpp"
#include "libslic3r/GCode/SpiralVase.hpp"
%}
%name{Slic3r::GCode::AvoidCrossingPerimeters} class AvoidCrossingPerimeters {
AvoidCrossingPerimeters();
~AvoidCrossingPerimeters();
void init_external_mp(ExPolygons islands);
void init_layer_mp(ExPolygons islands);
Clone<Polyline> travel_to(GCode* gcode, Point* point)
%code{% RETVAL = THIS->travel_to(*gcode, *point); %};
bool use_external_mp()
%code{% RETVAL = THIS->use_external_mp; %};
void set_use_external_mp(bool value)
%code{% THIS->use_external_mp = value; %};
bool use_external_mp_once()
%code{% RETVAL = THIS->use_external_mp_once; %};
void set_use_external_mp_once(bool value)
%code{% THIS->use_external_mp_once = value; %};
bool disable_once()
%code{% RETVAL = THIS->disable_once; %};
void set_disable_once(bool value)
%code{% THIS->disable_once = value; %};
};
%name{Slic3r::GCode::OozePrevention} class OozePrevention {
OozePrevention();
~OozePrevention();
bool enable()
%code{% RETVAL = THIS->enable; %};
void set_enable(bool value)
%code{% THIS->enable = value; %};
Points standby_points()
%code{% RETVAL = THIS->standby_points; %};
void set_standby_points(Points points)
%code{% THIS->standby_points = points; %};
std::string pre_toolchange(GCode* gcodegen)
%code{% RETVAL = THIS->pre_toolchange(*gcodegen); %};
std::string post_toolchange(GCode* gcodegen)
%code{% RETVAL = THIS->post_toolchange(*gcodegen); %};
};
%name{Slic3r::GCode::Wipe} class Wipe {
Wipe();
~Wipe();
bool has_path();
void reset_path();
std::string wipe(GCode* gcodegen, bool toolchange = false)
%code{% RETVAL = THIS->wipe(*gcodegen, toolchange); %};
bool enable()
%code{% RETVAL = THIS->enable; %};
void set_enable(bool value)
%code{% THIS->enable = value; %};
Ref<Polyline> path()
%code{% RETVAL = &(THIS->path); %};
void set_path(Polyline* value)
%code{% THIS->path = *value; %};
};
%name{Slic3r::GCode::CoolingBuffer} class CoolingBuffer {
CoolingBuffer(GCode* gcode)
%code{% RETVAL = new CoolingBuffer(*gcode); %};
~CoolingBuffer();
Ref<GCode> gcodegen();
std::string append(std::string gcode, std::string obj_id, size_t layer_id, float print_z);
std::string append(std::string gcode, size_t object_id, size_t layer_id, bool support_layer);
std::string flush();
};
%name{Slic3r::GCode::SpiralVase} class SpiralVase {
SpiralVase(StaticPrintConfig* config)
%code{% RETVAL = new SpiralVase(*dynamic_cast<PrintConfig*>(config)); %};
~SpiralVase();
bool enable()
%code{% RETVAL = THIS->enable; %};
void set_enable(bool enable)
%code{% THIS->enable = enable; %};
std::string process_layer(std::string gcode);
};
%name{Slic3r::GCode} class GCode {
GCode();
~GCode();
std::string do_export(Print *print, const char *path)
%code{%
FILE *file = fopen(path, "wb");
if (file == nullptr) {
RETVAL = std::string("Failed to open ") + path + " for writing.";
} else {
THIS->do_export(file, *print);
fclose(file);
RETVAL = std::string();
}
%};
Ref<Pointf> origin()
%code{% RETVAL = &(THIS->origin); %};
Ref<StaticPrintConfig> config()
%code{% RETVAL = &(THIS->config); %};
Ref<GCodeWriter> writer()
%code{% RETVAL = &(THIS->writer); %};
Ref<PlaceholderParser> placeholder_parser()
%code{% RETVAL = THIS->placeholder_parser; %};
void set_placeholder_parser(PlaceholderParser* ptr)
%code{% THIS->placeholder_parser = ptr; %};
Ref<OozePrevention> ooze_prevention()
%code{% RETVAL = &(THIS->ooze_prevention); %};
Ref<Wipe> wipe()
%code{% RETVAL = &(THIS->wipe); %};
Ref<AvoidCrossingPerimeters> avoid_crossing_perimeters()
%code{% RETVAL = &(THIS->avoid_crossing_perimeters); %};
bool enable_loop_clipping()
%code{% RETVAL = THIS->enable_loop_clipping; %};
void set_enable_loop_clipping(bool value)
%code{% THIS->enable_loop_clipping = value; %};
bool enable_cooling_markers()
%code{% RETVAL = THIS->enable_cooling_markers; %};
void set_enable_cooling_markers(bool value)
%code{% THIS->enable_cooling_markers = value; %};
bool enable_extrusion_role_markers()
%code{% RETVAL = THIS->enable_extrusion_role_markers; %};
void set_enable_extrusion_role_markers(bool value)
%code{% THIS->enable_extrusion_role_markers = value; %};
int layer_count()
%code{% RETVAL = THIS->layer_count; %};
void set_layer_count(int value)
%code{% THIS->layer_count = value; %};
int layer_index()
%code{% RETVAL = THIS->layer_index; %};
void set_layer_index(int value)
%code{% THIS->layer_index = value; %};
bool has_layer()
%code{% RETVAL = THIS->layer != NULL; %};
Ref<Layer> layer()
%code{% RETVAL = THIS->layer; %};
void set_layer(Layer* ptr)
%code{% THIS->layer = ptr; %};
bool first_layer()
%code{% RETVAL = THIS->first_layer; %};
void set_first_layer(bool value)
%code{% THIS->first_layer = value; %};
float elapsed_time()
%code{% RETVAL = THIS->elapsed_time; %};
void set_elapsed_time(float value)
%code{% THIS->elapsed_time = value; %};
bool last_pos_defined();
%code{% RETVAL = &(THIS->origin()); %};
void set_origin(Pointf* pointf)
%code{% THIS->set_origin(*pointf); %};
Ref<Point> last_pos()
%code{% RETVAL = &(THIS->last_pos()); %};
void set_last_pos(Point* pos)
%code{% THIS->set_last_pos(*pos); %};
double volumetric_speed()
%code{% RETVAL = THIS->volumetric_speed; %};
void set_volumetric_speed(double value)
%code{% THIS->volumetric_speed = value; %};
unsigned int layer_count() const;
void set_layer_count(unsigned int value);
float elapsed_time() const;
void set_elapsed_time(float value);
void apply_print_config(StaticPrintConfig* print_config)
%code{%
@ -182,40 +51,7 @@
CONFESS("A PrintConfig object was not supplied to apply_print_config()");
}
%};
void set_extruders(std::vector<unsigned int> extruder_ids);
void set_origin(Pointf* pointf)
%code{% THIS->set_origin(*pointf); %};
std::string preamble();
std::string change_layer(Layer* layer)
%code{% RETVAL = THIS->change_layer(*layer); %};
%name{extrude_loop} std::string extrude(ExtrusionLoop* loop, std::string description = "", double speed = -1)
%code{% RETVAL = THIS->extrude(*loop, description, speed); %};
%name{extrude_multipath} std::string extrude(ExtrusionMultiPath* multipath, std::string description = "", double speed = -1)
%code{% RETVAL = THIS->extrude(*multipath, description, speed); %};
%name{extrude_path} std::string extrude(ExtrusionPath* path, std::string description = "", double speed = -1)
%code{% RETVAL = THIS->extrude(*path, description, speed); %};
std::string extrude_support(ExtrusionEntityCollection *support_fills, unsigned int extruder_id);
std::string travel_to(Point* point, ExtrusionRole role, std::string comment)
%code{% RETVAL = THIS->travel_to(*point, role, comment); %};
bool needs_retraction(Polyline* travel, ExtrusionRole role = erNone)
%code{% RETVAL = THIS->needs_retraction(*travel, role); %};
std::string retract(bool toolchange = false);
std::string unretract();
std::string set_extruder(unsigned int extruder_id);
Clone<Pointf> point_to_gcode(Point* point)
%code{% RETVAL = THIS->point_to_gcode(*point); %};
%{
std::string
GCode::extrude(entity, description, speed)
SV* entity
std::string description;
double speed;
CODE:
ExtrusionEntity* e = (ExtrusionEntity *)SvIV((SV*)SvRV( entity ));
RETVAL = THIS->extrude(*e, description, speed);
OUTPUT:
RETVAL
%}
Ref<StaticPrintConfig> config()
%code{% RETVAL = const_cast<StaticPrintConfig*>(dynamic_cast<const StaticPrintConfig*>(&THIS->config())); %};
};

View File

@ -1,32 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/GCode/PressureEqualizer.hpp"
%}
%name{Slic3r::GCode::PressureEqualizer} class GCodePressureEqualizer {
GCodePressureEqualizer(StaticPrintConfig* config)
%code%{ RETVAL = new GCodePressureEqualizer(dynamic_cast<GCodeConfig*>(config)); %};
~GCodePressureEqualizer();
void reset();
// Process a next batch of G-code lines. Flush the internal buffers if asked for.
// const char* process(const char *szGCode, bool flush);
// std::string process(const char *szGCode, bool flush)
// %code{% const char *out = THIS->process(szGCode, flush); RETVAL = (out == NULL) ? "" : std::string(out); %};
%{
SV*
GCodePressureEqualizer::process(const char *szGCode, bool flush)
CODE:
const char *out = THIS->process(szGCode, flush);
RETVAL = newSVpv(out, THIS->get_output_buffer_length());
OUTPUT:
RETVAL
%}
};

View File

@ -54,9 +54,8 @@ GCodeWriter::extruders()
AV* av = newAV();
av_fill(av, THIS->extruders.size()-1);
int i = 0;
for (std::map<unsigned int,Extruder>::iterator it = THIS->extruders.begin(); it != THIS->extruders.end(); ++it) {
av_store(av, i++, perl_to_SV_ref(it->second));
}
for (const Extruder &extruder : THIS->extruders)
av_store(av, i++, perl_to_SV_ref(const_cast<Extruder&>(extruder)));
RETVAL = newRV_noinc((SV*)av);
OUTPUT:
RETVAL

View File

@ -195,30 +195,6 @@ _constant()
void set_step_started(PrintStep step)
%code%{ THIS->state.set_started(step); %};
std::vector<int> object_extruders()
%code%{
std::set<size_t> extruders = THIS->object_extruders();
RETVAL.reserve(extruders.size());
for (std::set<size_t>::const_iterator e = extruders.begin(); e != extruders.end(); ++e) {
RETVAL.push_back(*e);
}
%};
std::vector<int> support_material_extruders()
%code%{
std::set<size_t> extruders = THIS->support_material_extruders();
RETVAL.reserve(extruders.size());
for (std::set<size_t>::const_iterator e = extruders.begin(); e != extruders.end(); ++e) {
RETVAL.push_back(*e);
}
%};
std::vector<int> extruders()
%code%{
std::set<size_t> extruders = THIS->extruders();
RETVAL.reserve(extruders.size());
for (std::set<size_t>::const_iterator e = extruders.begin(); e != extruders.end(); ++e) {
RETVAL.push_back(*e);
}
%};
void clear_filament_stats()
%code%{
THIS->filament_stats.clear();

View File

@ -185,26 +185,10 @@ PlaceholderParser* O_OBJECT_SLIC3R
Ref<PlaceholderParser> O_OBJECT_SLIC3R_T
Clone<PlaceholderParser> O_OBJECT_SLIC3R_T
AvoidCrossingPerimeters* O_OBJECT_SLIC3R
Ref<AvoidCrossingPerimeters> O_OBJECT_SLIC3R_T
Clone<AvoidCrossingPerimeters> O_OBJECT_SLIC3R_T
Wipe* O_OBJECT_SLIC3R
Ref<Wipe> O_OBJECT_SLIC3R_T
Clone<Wipe> O_OBJECT_SLIC3R_T
OozePrevention* O_OBJECT_SLIC3R
Ref<OozePrevention> O_OBJECT_SLIC3R_T
Clone<OozePrevention> O_OBJECT_SLIC3R_T
CoolingBuffer* O_OBJECT_SLIC3R
Ref<CoolingBuffer> O_OBJECT_SLIC3R_T
Clone<CoolingBuffer> O_OBJECT_SLIC3R_T
SpiralVase* O_OBJECT_SLIC3R
Ref<SpiralVase> O_OBJECT_SLIC3R_T
Clone<SpiralVase> O_OBJECT_SLIC3R_T
GCode* O_OBJECT_SLIC3R
Ref<GCode> O_OBJECT_SLIC3R_T
Clone<GCode> O_OBJECT_SLIC3R_T
@ -221,10 +205,6 @@ GCodeWriter* O_OBJECT_SLIC3R
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
Clone<GCodeWriter> O_OBJECT_SLIC3R_T
GCodePressureEqualizer* O_OBJECT_SLIC3R
Ref<GCodePressureEqualizer> O_OBJECT_SLIC3R_T
Clone<GCodePressureEqualizer> O_OBJECT_SLIC3R_T
BridgeDetector* O_OBJECT_SLIC3R
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
Clone<BridgeDetector> O_OBJECT_SLIC3R_T

View File

@ -105,9 +105,6 @@
%typemap{GCodeSender*};
%typemap{Ref<GCodeSender>}{simple};
%typemap{Clone<GCodeSender>}{simple};
%typemap{GCodePressureEqualizer*};
%typemap{Ref<GCodePressureEqualizer>}{simple};
%typemap{Clone<GCodePressureEqualizer>}{simple};
%typemap{BridgeDetector*};
%typemap{Ref<BridgeDetector>}{simple};
%typemap{Clone<BridgeDetector>}{simple};
@ -152,18 +149,6 @@
%typemap{Ref<PlaceholderParser>}{simple};
%typemap{Clone<PlaceholderParser>}{simple};
%typemap{AvoidCrossingPerimeters*};
%typemap{Ref<AvoidCrossingPerimeters>}{simple};
%typemap{Clone<AvoidCrossingPerimeters>}{simple};
%typemap{Wipe*};
%typemap{Ref<Wipe>}{simple};
%typemap{Clone<Wipe>}{simple};
%typemap{OozePrevention*};
%typemap{Ref<OozePrevention>}{simple};
%typemap{Clone<OozePrevention>}{simple};
%typemap{CoolingBuffer*};
%typemap{Ref<CoolingBuffer>}{simple};
%typemap{Clone<CoolingBuffer>}{simple};