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:
parent
72ae3585e4
commit
e90279c513
@ -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 {};
|
||||
|
@ -76,26 +76,10 @@ 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 $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));
|
||||
@ -104,7 +88,6 @@ sub export_gcode {
|
||||
}
|
||||
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
|
||||
if (@{$self->config->post_process}) {
|
||||
|
@ -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;
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
12
t/cooling.t
12
t/cooling.t
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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; };
|
||||
|
@ -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 <>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace Slic3r {
|
||||
|
||||
class Extruder
|
||||
{
|
||||
public:
|
||||
public:
|
||||
unsigned int id;
|
||||
double E;
|
||||
double absolute_E;
|
||||
@ -20,6 +20,7 @@ class Extruder
|
||||
|
||||
Extruder(unsigned int id, GCodeConfig *config);
|
||||
virtual ~Extruder() {}
|
||||
|
||||
void reset();
|
||||
double extrude(double dE);
|
||||
double retract(double length, double restart_extra);
|
||||
@ -39,10 +40,21 @@ class Extruder
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
@ -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() {}
|
||||
|
||||
/* 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;
|
||||
// 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;
|
||||
// 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;
|
||||
// Extended markers for the G-code Analyzer.
|
||||
// The G-code Analyzer will remove these comments from the final G-code.
|
||||
bool enable_analyzer_markers;
|
||||
// How many times will change_layer() be called?
|
||||
// change_layer() will update the progress bar.
|
||||
size_t layer_count;
|
||||
// Progress bar indicator. Increments from -1 up to layer_count.
|
||||
int 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;
|
||||
// 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.
|
||||
// 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;
|
||||
// Support for the extrusion role markers. Which marker is active?
|
||||
ExtrusionRole _last_extrusion_role;
|
||||
bool do_export(FILE *file, Print &print);
|
||||
|
||||
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);
|
||||
// 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);
|
||||
std::string extrude_support(const ExtrusionEntityCollection *support_fills, unsigned int extruder_id);
|
||||
|
||||
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);
|
||||
Pointf point_to_gcode(const Point &point);
|
||||
|
||||
private:
|
||||
Point _last_pos;
|
||||
bool _last_pos_defined;
|
||||
/* Origin of print coordinates expressed in unscaled G-code coordinates.
|
||||
This affects the input arguments supplied to the extrude*() and travel_to()
|
||||
methods. */
|
||||
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 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 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 m_enable_analyzer_markers;
|
||||
// How many times will change_layer() be called?
|
||||
// change_layer() will update the progress bar.
|
||||
unsigned int m_layer_count;
|
||||
// Progress bar indicator. Increments from -1 up to layer_count.
|
||||
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* m_layer;
|
||||
std::map<const PrintObject*,Point> m_seam_position;
|
||||
// Distance Field structure to
|
||||
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 m_elapsed_time; // seconds
|
||||
double m_volumetric_speed;
|
||||
// Support for the extrusion role markers. Which marker is active?
|
||||
ExtrusionRole m_last_extrusion_role;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
@ -95,12 +92,13 @@ CoolingBuffer::flush()
|
||||
std::istringstream ss(gcode);
|
||||
std::string line;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "SpiralVase.hpp"
|
||||
#include "GCode.hpp"
|
||||
#include <sstream>
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -2,7 +2,6 @@
|
||||
#define slic3r_SpiralVase_hpp_
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "GCode.hpp"
|
||||
#include "GCodeReader.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
GCodeWriter() :
|
||||
multiple_extruders(false), _extrusion_axis("E"), _extruder(nullptr),
|
||||
_last_acceleration(0), _last_fan_speed(0), _lifted(0)
|
||||
{};
|
||||
Extruder* extruder() const { return this->_extruder; }
|
||||
{}
|
||||
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);
|
||||
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,8 +39,11 @@ 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());
|
||||
@ -47,6 +58,7 @@ public:
|
||||
std::string lift();
|
||||
std::string unlift();
|
||||
Pointf3 get_position() const { return this->_pos; }
|
||||
|
||||
private:
|
||||
std::string _extrusion_axis;
|
||||
Extruder* _extruder;
|
||||
|
@ -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; ) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 <>
|
||||
|
@ -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 © : 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) {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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.;
|
||||
|
@ -9,7 +9,7 @@ namespace Slic3r {
|
||||
|
||||
class SurfaceCollection
|
||||
{
|
||||
public:
|
||||
public:
|
||||
Surfaces surfaces;
|
||||
|
||||
SurfaceCollection() {};
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
%{
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
%{
|
||||
|
206
xs/xsp/GCode.xsp
206
xs/xsp/GCode.xsp
@ -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())); %};
|
||||
};
|
||||
|
@ -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
|
||||
|
||||
%}
|
||||
|
||||
};
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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};
|
||||
|
Loading…
Reference in New Issue
Block a user