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::Polygon;
|
||||||
use Slic3r::Polyline;
|
use Slic3r::Polyline;
|
||||||
use Slic3r::Print;
|
use Slic3r::Print;
|
||||||
use Slic3r::Print::GCode;
|
|
||||||
use Slic3r::Print::Object;
|
use Slic3r::Print::Object;
|
||||||
use Slic3r::Print::Simple;
|
use Slic3r::Print::Simple;
|
||||||
use Slic3r::Surface;
|
use Slic3r::Surface;
|
||||||
@ -174,11 +173,8 @@ sub thread_cleanup {
|
|||||||
# Therefore the Filler instances shall be released at the end of the thread.
|
# Therefore the Filler instances shall be released at the end of the thread.
|
||||||
# *Slic3r::Filler::DESTROY = sub {};
|
# *Slic3r::Filler::DESTROY = sub {};
|
||||||
*Slic3r::GCode::DESTROY = sub {};
|
*Slic3r::GCode::DESTROY = sub {};
|
||||||
*Slic3r::GCode::AvoidCrossingPerimeters::DESTROY = sub {};
|
|
||||||
*Slic3r::GCode::OozePrevention::DESTROY = sub {};
|
|
||||||
*Slic3r::GCode::PlaceholderParser::DESTROY = sub {};
|
*Slic3r::GCode::PlaceholderParser::DESTROY = sub {};
|
||||||
*Slic3r::GCode::Sender::DESTROY = sub {};
|
*Slic3r::GCode::Sender::DESTROY = sub {};
|
||||||
*Slic3r::GCode::Wipe::DESTROY = sub {};
|
|
||||||
*Slic3r::GCode::Writer::DESTROY = sub {};
|
*Slic3r::GCode::Writer::DESTROY = sub {};
|
||||||
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
|
*Slic3r::Geometry::BoundingBox::DESTROY = sub {};
|
||||||
*Slic3r::Geometry::BoundingBoxf::DESTROY = sub {};
|
*Slic3r::Geometry::BoundingBoxf::DESTROY = sub {};
|
||||||
|
@ -76,34 +76,17 @@ sub export_gcode {
|
|||||||
|
|
||||||
{
|
{
|
||||||
# open output gcode file if we weren't supplied a file-handle
|
# open output gcode file if we weren't supplied a file-handle
|
||||||
my ($fh, $tempfile);
|
my $tempfile = "$output_file.tmp";
|
||||||
if ($params{output_fh}) {
|
my $gcode = Slic3r::GCode->new();
|
||||||
$fh = $params{output_fh};
|
my $result = $gcode->do_export($self, $tempfile);
|
||||||
} else {
|
die $result . "\n" if ($result ne '');
|
||||||
$tempfile = "$output_file.tmp";
|
my $i;
|
||||||
Slic3r::open(\$fh, ">", $tempfile)
|
for ($i = 0; $i < 5; $i += 1) {
|
||||||
or die "Failed to open $tempfile for writing\n";
|
last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file));
|
||||||
|
# Wait for 1/4 seconds and try to rename once again.
|
||||||
# enable UTF-8 output since user might have entered Unicode characters in fields like notes
|
select(undef, undef, undef, 0.25);
|
||||||
binmode $fh, ':utf8';
|
|
||||||
}
|
|
||||||
|
|
||||||
Slic3r::Print::GCode->new(
|
|
||||||
print => $self,
|
|
||||||
fh => $fh,
|
|
||||||
)->export;
|
|
||||||
|
|
||||||
# close our gcode file
|
|
||||||
close $fh;
|
|
||||||
if ($tempfile) {
|
|
||||||
my $i;
|
|
||||||
for ($i = 0; $i < 5; $i += 1) {
|
|
||||||
last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file));
|
|
||||||
# Wait for 1/4 seconds and try to rename once again.
|
|
||||||
select(undef, undef, undef, 0.25);
|
|
||||||
}
|
|
||||||
Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5);
|
|
||||||
}
|
}
|
||||||
|
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
|
# run post-processing scripts
|
||||||
|
@ -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;
|
package Slic3r::Test;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
use Cwd 'abs_path';
|
||||||
|
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our @ISA = qw(Exporter);
|
our @ISA = qw(Exporter);
|
||||||
@ -202,10 +203,19 @@ sub gcode {
|
|||||||
|
|
||||||
$print = $print->print if $print->isa('Slic3r::Test::Print');
|
$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->process;
|
||||||
$print->export_gcode(output_fh => $fh, quiet => 1);
|
$print->export_gcode(output_file => $gcode_temp_path, quiet => 1);
|
||||||
$fh->close;
|
# 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;
|
return $gcode;
|
||||||
}
|
}
|
||||||
|
@ -304,8 +304,6 @@ $j
|
|||||||
--use-relative-e-distances Enable this to get relative E values (default: no)
|
--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-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
||||||
--use-volumetric-e Express E in cubic millimeters and prepend M200 (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)
|
--gcode-comments Make G-code verbose by adding comments (default: no)
|
||||||
|
|
||||||
Filament options:
|
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);
|
my $buffer = buffer($config);
|
||||||
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time + 1);
|
$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';
|
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 F3000;_EXTRUDE_SET_SPEED\n" .
|
||||||
"G1 X100 E1\n" .
|
"G1 X100 E1\n" .
|
||||||
"G1 E4 F400",
|
"G1 E4 F400",
|
||||||
0, 0, 0.4
|
0, 0, 0
|
||||||
) . $buffer->flush;
|
) . $buffer->flush;
|
||||||
unlike $gcode, qr/F3000/, 'speed is altered when elapsed time is lower than slowdown threshold';
|
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';
|
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);
|
my $buffer = buffer($config);
|
||||||
$buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time + 1);
|
$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';
|
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) {
|
for my $obj_id (0 .. 1) {
|
||||||
# use an elapsed time which is < the slowdown threshold but greater than it when summed twice
|
# 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);
|
$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;
|
$gcode .= $buffer->flush;
|
||||||
like $gcode, qr/F3000/, 'slowdown is computed on all objects printing at same Z';
|
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) {
|
for my $obj_id (0 .. 1) {
|
||||||
# use an elapsed time which is < the threshold but greater than it when summed twice
|
# 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);
|
$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;
|
$gcode .= $buffer->flush;
|
||||||
@ -91,7 +91,7 @@ $config->set('disable_fan_first_layers', 0);
|
|||||||
for my $obj_id (0 .. 1) {
|
for my $obj_id (0 .. 1) {
|
||||||
# use an elapsed time which is < the threshold even when summed twice
|
# 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);
|
$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;
|
$gcode .= $buffer->flush;
|
||||||
|
@ -514,7 +514,6 @@ xsp/Flow.xsp
|
|||||||
xsp/GCode.xsp
|
xsp/GCode.xsp
|
||||||
xsp/GCodeSender.xsp
|
xsp/GCodeSender.xsp
|
||||||
xsp/GCodeWriter.xsp
|
xsp/GCodeWriter.xsp
|
||||||
xsp/GCodePressureEqualizer.xsp
|
|
||||||
xsp/Geometry.xsp
|
xsp/Geometry.xsp
|
||||||
xsp/GUI.xsp
|
xsp/GUI.xsp
|
||||||
xsp/GUI_3DScene.xsp
|
xsp/GUI_3DScene.xsp
|
||||||
|
@ -278,10 +278,7 @@ for my $class (qw(
|
|||||||
Slic3r::Filler
|
Slic3r::Filler
|
||||||
Slic3r::Flow
|
Slic3r::Flow
|
||||||
Slic3r::GCode
|
Slic3r::GCode
|
||||||
Slic3r::GCode::AvoidCrossingPerimeters
|
|
||||||
Slic3r::GCode::OozePrevention
|
|
||||||
Slic3r::GCode::PlaceholderParser
|
Slic3r::GCode::PlaceholderParser
|
||||||
Slic3r::GCode::Wipe
|
|
||||||
Slic3r::GCode::Writer
|
Slic3r::GCode::Writer
|
||||||
Slic3r::Geometry::BoundingBox
|
Slic3r::Geometry::BoundingBox
|
||||||
Slic3r::Geometry::BoundingBoxf
|
Slic3r::Geometry::BoundingBoxf
|
||||||
|
@ -37,6 +37,9 @@ public:
|
|||||||
Polylines unsupported_edges(double angle = -1) const;
|
Polylines unsupported_edges(double angle = -1) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Suppress warning "assignment operator could not be generated"
|
||||||
|
BridgeDetector& operator=(const BridgeDetector &);
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
|
|
||||||
struct BridgeDirection {
|
struct BridgeDirection {
|
||||||
|
@ -34,7 +34,7 @@ class ConfigOption {
|
|||||||
virtual int getInt() const { return 0; };
|
virtual int getInt() const { return 0; };
|
||||||
virtual double getFloat() const { return 0; };
|
virtual double getFloat() const { return 0; };
|
||||||
virtual bool getBool() const { return false; };
|
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);
|
||||||
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:
|
public:
|
||||||
ConfigOptionInt() : ConfigOptionSingle<int>(0) {};
|
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; };
|
int getInt() const { return this->value; };
|
||||||
void setInt(int val) { this->value = val; };
|
void setInt(int val) { this->value = val; };
|
||||||
|
@ -326,7 +326,7 @@ namespace boost { namespace polygon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the winding direction of the 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;
|
return unknown_winding;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -391,8 +391,8 @@ namespace boost { namespace polygon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//don't worry about these, just return false from them
|
//don't worry about these, just return false from them
|
||||||
static inline bool clean(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; }
|
static inline bool sorted(const Slic3r::ExPolygons& /* polygon_set */) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -4,12 +4,12 @@ namespace Slic3r {
|
|||||||
|
|
||||||
Extruder::Extruder(unsigned int id, GCodeConfig *config)
|
Extruder::Extruder(unsigned int id, GCodeConfig *config)
|
||||||
: id(id),
|
: id(id),
|
||||||
config(config)
|
m_config(config)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
// cache values that are going to be called often
|
// 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();
|
this->e_per_mm3 = this->extrusion_multiplier();
|
||||||
} else {
|
} else {
|
||||||
this->e_per_mm3 = this->extrusion_multiplier()
|
this->e_per_mm3 = this->extrusion_multiplier()
|
||||||
@ -31,7 +31,7 @@ double
|
|||||||
Extruder::extrude(double dE)
|
Extruder::extrude(double dE)
|
||||||
{
|
{
|
||||||
// in case of relative E distances we always reset to 0 before any output
|
// 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 = 0;
|
||||||
|
|
||||||
this->E += dE;
|
this->E += dE;
|
||||||
@ -50,7 +50,7 @@ double
|
|||||||
Extruder::retract(double length, double restart_extra)
|
Extruder::retract(double length, double restart_extra)
|
||||||
{
|
{
|
||||||
// in case of relative E distances we always reset to 0 before any output
|
// 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 = 0;
|
||||||
|
|
||||||
double to_retract = length - this->retracted;
|
double to_retract = length - this->retracted;
|
||||||
@ -84,7 +84,7 @@ Extruder::e_per_mm(double mm3_per_mm) const
|
|||||||
double
|
double
|
||||||
Extruder::extruded_volume() const
|
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
|
// Any current amount of retraction should not affect used filament, since
|
||||||
// it represents empty volume in the nozzle. We add it back to E.
|
// it represents empty volume in the nozzle. We add it back to E.
|
||||||
return this->absolute_E + this->retracted;
|
return this->absolute_E + this->retracted;
|
||||||
@ -96,7 +96,7 @@ Extruder::extruded_volume() const
|
|||||||
double
|
double
|
||||||
Extruder::used_filament() const
|
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);
|
return this->extruded_volume() / (this->filament_diameter() * this->filament_diameter() * PI/4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,61 +108,61 @@ Extruder::used_filament() const
|
|||||||
double
|
double
|
||||||
Extruder::filament_diameter() const
|
Extruder::filament_diameter() const
|
||||||
{
|
{
|
||||||
return this->config->filament_diameter.get_at(this->id);
|
return m_config->filament_diameter.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
Extruder::filament_density() const
|
Extruder::filament_density() const
|
||||||
{
|
{
|
||||||
return this->config->filament_density.get_at(this->id);
|
return m_config->filament_density.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
Extruder::filament_cost() const
|
Extruder::filament_cost() const
|
||||||
{
|
{
|
||||||
return this->config->filament_cost.get_at(this->id);
|
return m_config->filament_cost.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
Extruder::extrusion_multiplier() const
|
Extruder::extrusion_multiplier() const
|
||||||
{
|
{
|
||||||
return this->config->extrusion_multiplier.get_at(this->id);
|
return m_config->extrusion_multiplier.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
Extruder::retract_length() const
|
Extruder::retract_length() const
|
||||||
{
|
{
|
||||||
return this->config->retract_length.get_at(this->id);
|
return m_config->retract_length.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
Extruder::retract_lift() const
|
Extruder::retract_lift() const
|
||||||
{
|
{
|
||||||
return this->config->retract_lift.get_at(this->id);
|
return m_config->retract_lift.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Extruder::retract_speed() const
|
Extruder::retract_speed() const
|
||||||
{
|
{
|
||||||
return this->config->retract_speed.get_at(this->id);
|
return m_config->retract_speed.get_at(this->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
Extruder::retract_restart_extra() const
|
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
|
double
|
||||||
Extruder::retract_length_toolchange() const
|
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
|
double
|
||||||
Extruder::retract_restart_extra_toolchange() const
|
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
|
class Extruder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unsigned int id;
|
unsigned int id;
|
||||||
double E;
|
double E;
|
||||||
double absolute_E;
|
double absolute_E;
|
||||||
@ -20,7 +20,8 @@ class Extruder
|
|||||||
|
|
||||||
Extruder(unsigned int id, GCodeConfig *config);
|
Extruder(unsigned int id, GCodeConfig *config);
|
||||||
virtual ~Extruder() {}
|
virtual ~Extruder() {}
|
||||||
void reset();
|
|
||||||
|
void reset();
|
||||||
double extrude(double dE);
|
double extrude(double dE);
|
||||||
double retract(double length, double restart_extra);
|
double retract(double length, double restart_extra);
|
||||||
double unretract();
|
double unretract();
|
||||||
@ -34,15 +35,26 @@ class Extruder
|
|||||||
double extrusion_multiplier() const;
|
double extrusion_multiplier() const;
|
||||||
double retract_length() const;
|
double retract_length() const;
|
||||||
double retract_lift() const;
|
double retract_lift() const;
|
||||||
int retract_speed() const;
|
int retract_speed() const;
|
||||||
double retract_restart_extra() const;
|
double retract_restart_extra() const;
|
||||||
double retract_length_toolchange() const;
|
double retract_length_toolchange() const;
|
||||||
double retract_restart_extra_toolchange() const;
|
double retract_restart_extra_toolchange() const;
|
||||||
|
|
||||||
private:
|
static Extruder key(unsigned int id) { return Extruder(id); }
|
||||||
GCodeConfig *config;
|
|
||||||
|
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
|
#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.
|
// 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.
|
// 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));
|
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;
|
min = dist;
|
||||||
path_idx = path - this->paths.begin();
|
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;
|
p_non_overhang = p_tmp;
|
||||||
min_non_overhang = dist;
|
min_non_overhang = dist;
|
||||||
path_idx_non_overhang = path - this->paths.begin();
|
path_idx_non_overhang = path - this->paths.begin();
|
||||||
@ -291,7 +291,7 @@ ExtrusionLoop::has_overhang_point(const Point &point) const
|
|||||||
if (pos != -1) {
|
if (pos != -1) {
|
||||||
// point belongs to this path
|
// point belongs to this path
|
||||||
// we consider it overhang only if it's not an endpoint
|
// 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;
|
return false;
|
||||||
|
@ -29,6 +29,33 @@ enum ExtrusionRole {
|
|||||||
erMixed,
|
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 */
|
/* Special flags describing loop */
|
||||||
enum ExtrusionLoopRole {
|
enum ExtrusionLoopRole {
|
||||||
elrDefault,
|
elrDefault,
|
||||||
@ -101,26 +128,6 @@ public:
|
|||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
virtual double length() const;
|
virtual double length() const;
|
||||||
virtual ExtrusionRole role() const { return m_role; }
|
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.
|
// 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.
|
// 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;
|
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(); }
|
Point last_point() const { return this->paths.back().polyline.points.back(); }
|
||||||
virtual double length() const;
|
virtual double length() const;
|
||||||
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
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.
|
// 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.
|
// 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;
|
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;
|
bool has_overhang_point(const Point &point) const;
|
||||||
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
||||||
ExtrusionLoopRole loop_role() const { return m_loop_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.
|
// 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.
|
// 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;
|
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);
|
this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(Point start_near, bool no_reverse, ExtrusionRole role) const
|
||||||
ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) 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) {
|
if (this->no_sort) {
|
||||||
*retval = *this;
|
*retval = *this;
|
||||||
|
@ -59,6 +59,7 @@ public:
|
|||||||
void remove(size_t i);
|
void remove(size_t i);
|
||||||
ExtrusionEntityCollection chained_path(bool no_reverse = false, ExtrusionRole role = erMixed) const;
|
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;
|
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 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();
|
void reverse();
|
||||||
Point first_point() const { return this->entities.front()->first_point(); }
|
Point first_point() const { return this->entities.front()->first_point(); }
|
||||||
@ -78,6 +79,8 @@ public:
|
|||||||
void flatten(ExtrusionEntityCollection* retval) const;
|
void flatten(ExtrusionEntityCollection* retval) const;
|
||||||
ExtrusionEntityCollection flatten() const;
|
ExtrusionEntityCollection flatten() const;
|
||||||
double min_mm3_per_mm() const;
|
double min_mm3_per_mm() const;
|
||||||
|
|
||||||
|
// Following methods shall never be called on an ExtrusionEntityCollection.
|
||||||
Polyline as_polyline() const {
|
Polyline as_polyline() const {
|
||||||
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
|
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
|
||||||
return Polyline();
|
return Polyline();
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "Flow.hpp"
|
#include "Flow.hpp"
|
||||||
|
#include "Print.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
@ -113,4 +114,39 @@ float Flow::_width_from_spacing(float spacing, float nozzle_diameter, float heig
|
|||||||
#endif
|
#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 {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class PrintObject;
|
||||||
|
|
||||||
// Extra spacing of bridge threads, in mm.
|
// Extra spacing of bridge threads, in mm.
|
||||||
#define BRIDGE_EXTRA_SPACING 0.05
|
#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);
|
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
|
#endif
|
||||||
|
@ -147,6 +147,9 @@ struct AMFParserContext
|
|||||||
Instance *m_instance;
|
Instance *m_instance;
|
||||||
// Generic string buffer for vertices, face indices, metadata etc.
|
// Generic string buffer for vertices, face indices, metadata etc.
|
||||||
std::string m_value[3];
|
std::string m_value[3];
|
||||||
|
|
||||||
|
private:
|
||||||
|
AMFParserContext& operator=(AMFParserContext&);
|
||||||
};
|
};
|
||||||
|
|
||||||
void AMFParserContext::startElement(const char *name, const char **atts)
|
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()) {
|
switch (m_path.back()) {
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,16 +10,20 @@
|
|||||||
#include "PlaceholderParser.hpp"
|
#include "PlaceholderParser.hpp"
|
||||||
#include "Print.hpp"
|
#include "Print.hpp"
|
||||||
#include "PrintConfig.hpp"
|
#include "PrintConfig.hpp"
|
||||||
|
#include "GCode/CoolingBuffer.hpp"
|
||||||
|
#include "GCode/PressureEqualizer.hpp"
|
||||||
|
#include "GCode/SpiralVase.hpp"
|
||||||
|
#include "EdgeGrid.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
// Forward declarations.
|
// Forward declarations.
|
||||||
class GCode;
|
class GCode;
|
||||||
namespace EdgeGrid { class Grid; }
|
|
||||||
|
|
||||||
class AvoidCrossingPerimeters {
|
class AvoidCrossingPerimeters {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// this flag triggers the use of the external configuration space
|
// this flag triggers the use of the external configuration space
|
||||||
bool use_external_mp;
|
bool use_external_mp;
|
||||||
@ -35,7 +39,7 @@ class AvoidCrossingPerimeters {
|
|||||||
void init_layer_mp(const ExPolygons &islands);
|
void init_layer_mp(const ExPolygons &islands);
|
||||||
Polyline travel_to(GCode &gcodegen, Point point);
|
Polyline travel_to(GCode &gcodegen, Point point);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MotionPlanner* _external_mp;
|
MotionPlanner* _external_mp;
|
||||||
MotionPlanner* _layer_mp;
|
MotionPlanner* _layer_mp;
|
||||||
};
|
};
|
||||||
@ -65,76 +69,141 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class GCode {
|
class GCode {
|
||||||
public:
|
public:
|
||||||
|
GCode() :
|
||||||
|
m_enable_loop_clipping(true),
|
||||||
|
m_enable_cooling_markers(false),
|
||||||
|
m_enable_extrusion_role_markers(false),
|
||||||
|
m_enable_analyzer_markers(false),
|
||||||
|
m_layer_count(0),
|
||||||
|
m_layer_index(-1),
|
||||||
|
m_layer(nullptr),
|
||||||
|
m_first_layer(false),
|
||||||
|
m_elapsed_time(0.0),
|
||||||
|
m_volumetric_speed(0),
|
||||||
|
m_last_pos_defined(false),
|
||||||
|
m_last_extrusion_role(erNone),
|
||||||
|
m_brim_done(false),
|
||||||
|
m_second_layer_things_done(false),
|
||||||
|
m_last_obj_copy(Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
|
||||||
|
{}
|
||||||
|
~GCode() {}
|
||||||
|
|
||||||
|
bool do_export(FILE *file, Print &print);
|
||||||
|
|
||||||
|
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
|
||||||
|
const Pointf& origin() const { return m_origin; }
|
||||||
|
void set_origin(const Pointf &pointf);
|
||||||
|
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); }
|
||||||
|
const Point& last_pos() const { return m_last_pos; }
|
||||||
|
Pointf point_to_gcode(const Point &point) const;
|
||||||
|
const FullPrintConfig &config() const { return m_config; }
|
||||||
|
const Layer* layer() const { return m_layer; }
|
||||||
|
GCodeWriter& writer() { return m_writer; }
|
||||||
|
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
|
||||||
|
float get_reset_elapsed_time() { float t = m_elapsed_time; m_elapsed_time = 0.f; return t; }
|
||||||
|
|
||||||
|
// For Perl bindings, to be used exclusively by unit tests.
|
||||||
|
unsigned int layer_count() const { return m_layer_count; }
|
||||||
|
void set_layer_count(unsigned int value) { m_layer_count = value; }
|
||||||
|
float elapsed_time() const { return m_elapsed_time; }
|
||||||
|
void set_elapsed_time(float value) { m_elapsed_time = value; }
|
||||||
|
void apply_print_config(const PrintConfig &print_config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void process_layer(FILE *file, const Print &print, const Layer &layer, const Points &object_copies);
|
||||||
|
|
||||||
|
void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; }
|
||||||
|
bool last_pos_defined() const { return m_last_pos_defined; }
|
||||||
|
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
||||||
|
std::string preamble();
|
||||||
|
std::string change_layer(const Layer &layer);
|
||||||
|
std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1);
|
||||||
|
std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1);
|
||||||
|
std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1);
|
||||||
|
std::string extrude(ExtrusionPath path, std::string description = "", double speed = -1);
|
||||||
|
|
||||||
|
struct ByExtruder
|
||||||
|
{
|
||||||
|
struct ToExtrude {
|
||||||
|
ExtrusionEntityCollection perimeters;
|
||||||
|
ExtrusionEntityCollection infills;
|
||||||
|
};
|
||||||
|
std::vector<ToExtrude> by_region;
|
||||||
|
};
|
||||||
|
std::string extrude_perimeters(const Print &print, const std::vector<ByExtruder::ToExtrude> &by_region);
|
||||||
|
std::string extrude_infill(const Print &print, const std::vector<ByExtruder::ToExtrude> &by_region);
|
||||||
|
std::string extrude_support(const ExtrusionEntityCollection &support_fills, unsigned int extruder_id);
|
||||||
|
|
||||||
|
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
|
||||||
|
bool needs_retraction(const Polyline &travel, ExtrusionRole role = erNone);
|
||||||
|
std::string retract(bool toolchange = false);
|
||||||
|
std::string unretract();
|
||||||
|
std::string set_extruder(unsigned int extruder_id);
|
||||||
|
|
||||||
/* Origin of print coordinates expressed in unscaled G-code coordinates.
|
/* Origin of print coordinates expressed in unscaled G-code coordinates.
|
||||||
This affects the input arguments supplied to the extrude*() and travel_to()
|
This affects the input arguments supplied to the extrude*() and travel_to()
|
||||||
methods. */
|
methods. */
|
||||||
Pointf origin;
|
Pointf m_origin;
|
||||||
FullPrintConfig config;
|
FullPrintConfig m_config;
|
||||||
GCodeWriter writer;
|
GCodeWriter m_writer;
|
||||||
PlaceholderParser* placeholder_parser;
|
PlaceholderParser m_placeholder_parser;
|
||||||
OozePrevention ooze_prevention;
|
OozePrevention m_ooze_prevention;
|
||||||
Wipe wipe;
|
Wipe m_wipe;
|
||||||
AvoidCrossingPerimeters avoid_crossing_perimeters;
|
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
|
||||||
bool enable_loop_clipping;
|
bool m_enable_loop_clipping;
|
||||||
// If enabled, the G-code generator will put following comments at the ends
|
// 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
|
// 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.
|
// Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module.
|
||||||
bool enable_cooling_markers;
|
bool m_enable_cooling_markers;
|
||||||
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
// Markers for the Pressure Equalizer to recognize the extrusion type.
|
||||||
// The Pressure Equalizer removes the markers from the final G-code.
|
// The Pressure Equalizer removes the markers from the final G-code.
|
||||||
bool enable_extrusion_role_markers;
|
bool m_enable_extrusion_role_markers;
|
||||||
// Extended markers for the G-code Analyzer.
|
// Extended markers for the G-code Analyzer.
|
||||||
// The G-code Analyzer will remove these comments from the final G-code.
|
// The G-code Analyzer will remove these comments from the final G-code.
|
||||||
bool enable_analyzer_markers;
|
bool m_enable_analyzer_markers;
|
||||||
// How many times will change_layer() be called?
|
// How many times will change_layer() be called?
|
||||||
// change_layer() will update the progress bar.
|
// change_layer() will update the progress bar.
|
||||||
size_t layer_count;
|
unsigned int m_layer_count;
|
||||||
// Progress bar indicator. Increments from -1 up to layer_count.
|
// Progress bar indicator. Increments from -1 up to layer_count.
|
||||||
int layer_index;
|
int m_layer_index;
|
||||||
// Current layer processed. Insequential printing mode, only a single copy will be printed.
|
// Current layer processed. Insequential printing mode, only a single copy will be printed.
|
||||||
// In non-sequential mode, all its copies will be printed.
|
// In non-sequential mode, all its copies will be printed.
|
||||||
const Layer* layer;
|
const Layer* m_layer;
|
||||||
std::map<const PrintObject*,Point> _seam_position;
|
std::map<const PrintObject*,Point> m_seam_position;
|
||||||
// Distance Field structure to
|
// Distance Field structure to
|
||||||
EdgeGrid::Grid *_lower_layer_edge_grid;
|
std::unique_ptr<EdgeGrid::Grid> m_lower_layer_edge_grid;
|
||||||
bool first_layer; // this flag triggers first layer speeds
|
// this flag triggers first layer speeds
|
||||||
// Used by the CoolingBuffer.pm Perl module to calculate time spent per layer change.
|
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,
|
// 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.
|
// it does not account for wipe, retract / unretract moves.
|
||||||
// second it does not account for the velocity profiles of the printer.
|
// second it does not account for the velocity profiles of the printer.
|
||||||
float elapsed_time; // seconds
|
float m_elapsed_time; // seconds
|
||||||
double volumetric_speed;
|
double m_volumetric_speed;
|
||||||
// Support for the extrusion role markers. Which marker is active?
|
// Support for the extrusion role markers. Which marker is active?
|
||||||
ExtrusionRole _last_extrusion_role;
|
ExtrusionRole m_last_extrusion_role;
|
||||||
|
|
||||||
GCode();
|
Point m_last_pos;
|
||||||
~GCode();
|
bool m_last_pos_defined;
|
||||||
const Point& last_pos() const;
|
|
||||||
void set_last_pos(const Point &pos);
|
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
|
||||||
bool last_pos_defined() const;
|
std::unique_ptr<SpiralVase> m_spiral_vase;
|
||||||
void apply_print_config(const PrintConfig &print_config);
|
std::unique_ptr<PressureEqualizer> m_pressure_equalizer;
|
||||||
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
|
||||||
void set_origin(const Pointf &pointf);
|
// Heights at which the skirt has already been extruded.
|
||||||
std::string preamble();
|
std::set<coordf_t> m_skirt_done;
|
||||||
std::string change_layer(const Layer &layer);
|
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
|
||||||
std::string extrude(const ExtrusionEntity &entity, std::string description = "", double speed = -1);
|
bool m_brim_done;
|
||||||
std::string extrude(ExtrusionLoop loop, std::string description = "", double speed = -1);
|
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||||
std::string extrude(ExtrusionMultiPath multipath, std::string description = "", double speed = -1);
|
bool m_second_layer_things_done;
|
||||||
std::string extrude(ExtrusionPath path, std::string description = "", double speed = -1);
|
// Index of a last object copy extruded. -1 for not set yet.
|
||||||
std::string extrude_support(const ExtrusionEntityCollection *support_fills, unsigned int extruder_id);
|
Point m_last_obj_copy;
|
||||||
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;
|
|
||||||
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
|
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;
|
char *endptr = NULL;
|
||||||
long result = strtol(line, &endptr, 10);
|
long result = strtol(line, &endptr, 10);
|
||||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
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;
|
line = endptr;
|
||||||
return int(result);
|
return int(result);
|
||||||
};
|
};
|
||||||
@ -102,7 +102,7 @@ static inline float parse_float(const char *&line)
|
|||||||
char *endptr = NULL;
|
char *endptr = NULL;
|
||||||
float result = strtof(line, &endptr);
|
float result = strtof(line, &endptr);
|
||||||
if (endptr == NULL || !is_ws_or_eol(*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;
|
line = endptr;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -171,7 +171,7 @@ bool GCodeAnalyzer::process_line(const char *line, const size_t len)
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
if (i == -1)
|
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;
|
buf.pos_provided[i] = true;
|
||||||
new_pos[i] = parse_float(line);
|
new_pos[i] = parse_float(line);
|
||||||
if (i == 3 && m_config->use_relative_e_distances.value)
|
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;
|
set = true;
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
eatws(line);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include "../GCode.hpp"
|
||||||
#include "CoolingBuffer.hpp"
|
#include "CoolingBuffer.hpp"
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/replace.hpp>
|
#include <boost/algorithm/string/replace.hpp>
|
||||||
@ -5,28 +6,26 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
std::string
|
std::string CoolingBuffer::append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support)
|
||||||
CoolingBuffer::append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z)
|
|
||||||
{
|
{
|
||||||
std::string out;
|
std::string out;
|
||||||
if (this->_last_z.find(obj_id) != this->_last_z.end()) {
|
size_t signature = object_id * 2 + is_support ? 1 : 0;
|
||||||
// A layer was finished, Z of the object's layer changed. Process the layer.
|
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();
|
out = this->flush();
|
||||||
}
|
|
||||||
|
|
||||||
this->_layer_id = layer_id;
|
m_object_ids_visited.insert(signature);
|
||||||
this->_last_z[obj_id] = print_z;
|
m_layer_id = layer_id;
|
||||||
this->_gcode += gcode;
|
m_gcode += gcode;
|
||||||
// This is a very rough estimate of the print time,
|
// This is a very rough estimate of the print time,
|
||||||
// not taking into account the acceleration curves generated by the printer firmware.
|
// not taking into account the acceleration curves generated by the printer firmware.
|
||||||
this->_elapsed_time += this->_gcodegen->elapsed_time;
|
m_elapsed_time += m_gcodegen.get_reset_elapsed_time();
|
||||||
this->_gcodegen->elapsed_time = 0;
|
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
|
||||||
apply_speed_factor(std::string &line, float speed_factor, float min_print_speed)
|
|
||||||
{
|
{
|
||||||
// find pos of F
|
// find pos of F
|
||||||
size_t pos = line.find_first_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
|
std::string CoolingBuffer::flush()
|
||||||
CoolingBuffer::flush()
|
|
||||||
{
|
{
|
||||||
GCode &gg = *this->_gcodegen;
|
const FullPrintConfig &config = m_gcodegen.config();
|
||||||
|
|
||||||
std::string gcode = this->_gcode;
|
std::string gcode = m_gcode;
|
||||||
float elapsed = this->_elapsed_time;
|
float elapsed = m_elapsed_time;
|
||||||
this->_gcode = "";
|
m_gcode.clear();
|
||||||
this->_elapsed_time = 0;
|
m_elapsed_time = 0.;
|
||||||
this->_last_z.clear(); // reset the whole table otherwise we would compute overlapping times
|
|
||||||
|
|
||||||
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;
|
float speed_factor = 1.0;
|
||||||
|
|
||||||
if (gg.config.cooling) {
|
if (config.cooling) {
|
||||||
#ifdef SLIC3R_DEBUG
|
#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
|
#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
|
// 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).
|
// (stretch the layer print time to slowdown_below_layer_time).
|
||||||
fan_speed = gg.config.max_fan_speed;
|
fan_speed = config.max_fan_speed;
|
||||||
speed_factor = elapsed / (float)gg.config.slowdown_below_layer_time;
|
speed_factor = elapsed / (float)config.slowdown_below_layer_time;
|
||||||
} else if (elapsed < (float)gg.config.fan_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.
|
// Layer time quite short. Enable the fan proportionally according to the current layer time.
|
||||||
fan_speed = gg.config.max_fan_speed
|
fan_speed = config.max_fan_speed
|
||||||
- (gg.config.max_fan_speed - gg.config.min_fan_speed)
|
- (config.max_fan_speed - config.min_fan_speed)
|
||||||
* (elapsed - (float)gg.config.slowdown_below_layer_time)
|
* (elapsed - (float)config.slowdown_below_layer_time)
|
||||||
/ (gg.config.fan_below_layer_time - gg.config.slowdown_below_layer_time);
|
/ (config.fan_below_layer_time - config.slowdown_below_layer_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
@ -94,13 +91,14 @@ CoolingBuffer::flush()
|
|||||||
std::string new_gcode;
|
std::string new_gcode;
|
||||||
std::istringstream ss(gcode);
|
std::istringstream ss(gcode);
|
||||||
std::string line;
|
std::string line;
|
||||||
bool bridge_fan_start = false;
|
bool bridge_fan_start = false;
|
||||||
|
float min_print_speed = float(config.min_print_speed * 60.);
|
||||||
while (std::getline(ss, line)) {
|
while (std::getline(ss, line)) {
|
||||||
if (boost::starts_with(line, "G1")
|
if (boost::starts_with(line, "G1")
|
||||||
&& boost::contains(line, ";_EXTRUDE_SET_SPEED")
|
&& boost::contains(line, ";_EXTRUDE_SET_SPEED")
|
||||||
&& !boost::contains(line, ";_WIPE")
|
&& !boost::contains(line, ";_WIPE")
|
||||||
&& !bridge_fan_start) {
|
&& !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", "");
|
boost::replace_first(line, ";_EXTRUDE_SET_SPEED", "");
|
||||||
}
|
}
|
||||||
bridge_fan_start = boost::contains(line, ";_BRIDGE_FAN_START");
|
bridge_fan_start = boost::contains(line, ";_BRIDGE_FAN_START");
|
||||||
@ -109,22 +107,23 @@ CoolingBuffer::flush()
|
|||||||
gcode = new_gcode;
|
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;
|
fan_speed = 0;
|
||||||
|
|
||||||
gcode = gg.writer.set_fan(fan_speed) + gcode;
|
gcode = m_gcodegen.writer().set_fan(fan_speed) + gcode;
|
||||||
|
|
||||||
// bridge fan speed
|
// 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_START", "");
|
||||||
boost::replace_all(gcode, ";_BRIDGE_FAN_END", "");
|
boost::replace_all(gcode, ";_BRIDGE_FAN_END", "");
|
||||||
} else {
|
} else {
|
||||||
boost::replace_all(gcode, ";_BRIDGE_FAN_START", gg.writer.set_fan(gg.config.bridge_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", gg.writer.set_fan(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, ";_WIPE", "");
|
||||||
boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", "");
|
boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", "");
|
||||||
|
|
||||||
|
m_object_ids_visited.clear();
|
||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
#define slic3r_CoolingBuffer_hpp_
|
#define slic3r_CoolingBuffer_hpp_
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include "GCode.hpp"
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class GCode;
|
||||||
|
class Layer;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
A standalone G-code filter, to control cooling of the print.
|
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
|
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 {
|
class CoolingBuffer {
|
||||||
public:
|
public:
|
||||||
CoolingBuffer(GCode &gcodegen)
|
CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_elapsed_time(0.), m_layer_id(0) {}
|
||||||
: _gcodegen(&gcodegen), _elapsed_time(0.), _layer_id(0)
|
std::string append(const std::string &gcode, size_t object_id, size_t layer_id, bool is_support);
|
||||||
{
|
|
||||||
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);
|
|
||||||
std::string flush();
|
std::string flush();
|
||||||
GCode* gcodegen() { return this->_gcodegen; };
|
GCode* gcodegen() { return &m_gcodegen; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GCode* _gcodegen;
|
CoolingBuffer& operator=(const CoolingBuffer&);
|
||||||
std::string _gcode;
|
|
||||||
float _elapsed_time;
|
GCode& m_gcodegen;
|
||||||
size_t _layer_id;
|
std::string m_gcode;
|
||||||
std::map<std::string,float> _last_z;
|
float m_elapsed_time;
|
||||||
float _min_print_speed;
|
size_t m_layer_id;
|
||||||
|
std::set<size_t> m_object_ids_visited;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -9,17 +9,17 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
GCodePressureEqualizer::GCodePressureEqualizer(const Slic3r::GCodeConfig *config) :
|
PressureEqualizer::PressureEqualizer(const Slic3r::GCodeConfig *config) :
|
||||||
m_config(config)
|
m_config(config)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
GCodePressureEqualizer::~GCodePressureEqualizer()
|
PressureEqualizer::~PressureEqualizer()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodePressureEqualizer::reset()
|
void PressureEqualizer::reset()
|
||||||
{
|
{
|
||||||
circular_buffer_pos = 0;
|
circular_buffer_pos = 0;
|
||||||
circular_buffer_size = 100;
|
circular_buffer_size = 100;
|
||||||
@ -69,7 +69,7 @@ void GCodePressureEqualizer::reset()
|
|||||||
line_idx = 0;
|
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.
|
// Reset length of the output_buffer.
|
||||||
output_buffer_length = 0;
|
output_buffer_length = 0;
|
||||||
@ -147,7 +147,7 @@ static inline int parse_int(const char *&line)
|
|||||||
char *endptr = NULL;
|
char *endptr = NULL;
|
||||||
long result = strtol(line, &endptr, 10);
|
long result = strtol(line, &endptr, 10);
|
||||||
if (endptr == NULL || !is_ws_or_eol(*endptr))
|
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;
|
line = endptr;
|
||||||
return int(result);
|
return int(result);
|
||||||
};
|
};
|
||||||
@ -159,13 +159,13 @@ static inline float parse_float(const char *&line)
|
|||||||
char *endptr = NULL;
|
char *endptr = NULL;
|
||||||
float result = strtof(line, &endptr);
|
float result = strtof(line, &endptr);
|
||||||
if (endptr == NULL || !is_ws_or_eol(*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;
|
line = endptr;
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define EXTRUSION_ROLE_TAG ";_EXTRUSION_ROLE:"
|
#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) {
|
if (strncmp(line, EXTRUSION_ROLE_TAG, strlen(EXTRUSION_ROLE_TAG)) == 0) {
|
||||||
line += strlen(EXTRUSION_ROLE_TAG);
|
line += strlen(EXTRUSION_ROLE_TAG);
|
||||||
@ -228,7 +228,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC
|
|||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
if (i == -1)
|
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;
|
buf.pos_provided[i] = true;
|
||||||
new_pos[i] = parse_float(line);
|
new_pos[i] = parse_float(line);
|
||||||
if (i == 3 && m_config->use_relative_e_distances.value)
|
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;
|
set = true;
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
eatws(line);
|
||||||
}
|
}
|
||||||
@ -355,7 +355,7 @@ bool GCodePressureEqualizer::process_line(const char *line, const size_t len, GC
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodePressureEqualizer::output_gcode_line(GCodeLine &line)
|
void PressureEqualizer::output_gcode_line(GCodeLine &line)
|
||||||
{
|
{
|
||||||
if (! line.modified) {
|
if (! line.modified) {
|
||||||
push_to_output(line.raw.data(), line.raw_length, true);
|
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)
|
if (circular_buffer_items < 2)
|
||||||
return;
|
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];
|
char buf[2048];
|
||||||
int len = sprintf(buf,
|
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);
|
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.
|
// New length of the output buffer content.
|
||||||
size_t len_new = output_buffer_length + len + 1;
|
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;
|
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);
|
push_to_output("G1", 2, false);
|
||||||
for (char i = 0; i < 3; ++ i)
|
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
|
// 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.
|
// between these paths to limit fast changes in the volumetric extrusion speed.
|
||||||
class GCodePressureEqualizer
|
class PressureEqualizer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GCodePressureEqualizer(const Slic3r::GCodeConfig *config);
|
PressureEqualizer(const Slic3r::GCodeConfig *config);
|
||||||
~GCodePressureEqualizer();
|
~PressureEqualizer();
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include "SpiralVase.hpp"
|
#include "SpiralVase.hpp"
|
||||||
|
#include "GCode.hpp"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#define slic3r_SpiralVase_hpp_
|
#define slic3r_SpiralVase_hpp_
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
#include "GCode.hpp"
|
|
||||||
#include "GCodeReader.hpp"
|
#include "GCodeReader.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -23,8 +23,8 @@ GCodeWriter::apply_print_config(const PrintConfig &print_config)
|
|||||||
void
|
void
|
||||||
GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
|
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)
|
for (unsigned int extruder_id : extruder_ids)
|
||||||
this->extruders.insert( std::pair<unsigned int,Extruder>(*i, Extruder(*i, &this->config)) );
|
this->extruders.insert(Extruder(extruder_id, &this->config));
|
||||||
|
|
||||||
/* we enable support for multiple extruder if any extruder greater than 0 is used
|
/* 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
|
(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))
|
|| FLAVOR_IS(gcfSailfish))
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
if (this->_extruder != NULL) {
|
if (this->_extruder != nullptr) {
|
||||||
if (this->_extruder->E == 0 && !force) return "";
|
if (this->_extruder->E == 0. && ! force)
|
||||||
this->_extruder->E = 0;
|
return "";
|
||||||
|
this->_extruder->E = 0.;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->_extrusion_axis.empty() && !this->config.use_relative_e_distances) {
|
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();
|
return gcode.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
std::string GCodeWriter::toolchange(unsigned int extruder_id)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
// set the new extruder
|
// 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
|
// return the toolchange command
|
||||||
// if we are running a single-extruder setup, just set the extruder and return nothing
|
// if we are running a single-extruder setup, just set the extruder and return nothing
|
||||||
|
@ -12,17 +12,25 @@ namespace Slic3r {
|
|||||||
class GCodeWriter {
|
class GCodeWriter {
|
||||||
public:
|
public:
|
||||||
GCodeConfig config;
|
GCodeConfig config;
|
||||||
std::map<unsigned int,Extruder> extruders;
|
std::set<Extruder> extruders;
|
||||||
bool multiple_extruders;
|
bool multiple_extruders;
|
||||||
|
|
||||||
GCodeWriter()
|
GCodeWriter() :
|
||||||
: multiple_extruders(false), _extrusion_axis("E"), _extruder(NULL),
|
multiple_extruders(false), _extrusion_axis("E"), _extruder(nullptr),
|
||||||
_last_acceleration(0), _last_fan_speed(0), _lifted(0)
|
_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; }
|
std::string extrusion_axis() const { return this->_extrusion_axis; }
|
||||||
void apply_print_config(const PrintConfig &print_config);
|
void apply_print_config(const PrintConfig &print_config);
|
||||||
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
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 preamble();
|
||||||
std::string postamble() const;
|
std::string postamble() const;
|
||||||
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const;
|
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const;
|
||||||
@ -31,14 +39,17 @@ public:
|
|||||||
std::string set_acceleration(unsigned int acceleration);
|
std::string set_acceleration(unsigned int acceleration);
|
||||||
std::string reset_e(bool force = false);
|
std::string reset_e(bool force = false);
|
||||||
std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const;
|
std::string update_progress(unsigned int num, unsigned int tot, bool allow_100 = false) const;
|
||||||
bool need_toolchange(unsigned int extruder_id) const;
|
// return false if this extruder was already selected
|
||||||
std::string set_extruder(unsigned int extruder_id);
|
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 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 set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const;
|
||||||
std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string());
|
std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string());
|
||||||
std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string());
|
std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string());
|
||||||
std::string travel_to_z(double z, const std::string &comment = std::string());
|
std::string travel_to_z(double z, const std::string &comment = std::string());
|
||||||
bool will_move_z(double z) const;
|
bool will_move_z(double z) const;
|
||||||
std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string());
|
std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string());
|
||||||
std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string());
|
std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string());
|
||||||
std::string retract();
|
std::string retract();
|
||||||
@ -46,14 +57,15 @@ public:
|
|||||||
std::string unretract();
|
std::string unretract();
|
||||||
std::string lift();
|
std::string lift();
|
||||||
std::string unlift();
|
std::string unlift();
|
||||||
Pointf3 get_position() const { return this->_pos; }
|
Pointf3 get_position() const { return this->_pos; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string _extrusion_axis;
|
std::string _extrusion_axis;
|
||||||
Extruder* _extruder;
|
Extruder* _extruder;
|
||||||
unsigned int _last_acceleration;
|
unsigned int _last_acceleration;
|
||||||
unsigned int _last_fan_speed;
|
unsigned int _last_fan_speed;
|
||||||
double _lifted;
|
double _lifted;
|
||||||
Pointf3 _pos;
|
Pointf3 _pos;
|
||||||
|
|
||||||
std::string _travel_to_z(double z, const std::string &comment);
|
std::string _travel_to_z(double z, const std::string &comment);
|
||||||
std::string _retract(double length, double restart_extra, const std::string &comment);
|
std::string _retract(double length, double restart_extra, const std::string &comment);
|
||||||
|
@ -97,23 +97,34 @@ PlaceholderParser::apply_env_variables()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void PlaceholderParser::set(const std::string &key, const std::string &value)
|
||||||
PlaceholderParser::set(const std::string &key, const std::string &value)
|
|
||||||
{
|
{
|
||||||
this->_single[key] = value;
|
this->_single[key] = value;
|
||||||
this->_multiple.erase(key);
|
this->_multiple.erase(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void PlaceholderParser::set(const std::string &key, int value)
|
||||||
PlaceholderParser::set(const std::string &key, int value)
|
|
||||||
{
|
{
|
||||||
std::ostringstream ss;
|
std::ostringstream ss;
|
||||||
ss << value;
|
ss << value;
|
||||||
this->set(key, ss.str());
|
this->set(key, ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void PlaceholderParser::set(const std::string &key, unsigned int value)
|
||||||
PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
|
{
|
||||||
|
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()) {
|
if (values.empty()) {
|
||||||
this->_multiple.erase(key);
|
this->_multiple.erase(key);
|
||||||
@ -124,8 +135,7 @@ PlaceholderParser::set(const std::string &key, std::vector<std::string> values)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string PlaceholderParser::process(std::string str) const
|
||||||
PlaceholderParser::process(std::string str) const
|
|
||||||
{
|
{
|
||||||
// replace single options, like [foo]
|
// replace single options, like [foo]
|
||||||
for (t_strstr_map::const_iterator it = this->_single.begin(); it != this->_single.end(); ++it) {
|
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;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const
|
||||||
PlaceholderParser::find_and_replace(std::string &source, std::string const &find, std::string const &replace) const
|
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos; ) {
|
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
|
class PlaceholderParser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
t_strstr_map _single;
|
t_strstr_map _single;
|
||||||
t_strstrs_map _multiple;
|
t_strstrs_map _multiple;
|
||||||
|
|
||||||
@ -25,10 +25,12 @@ class PlaceholderParser
|
|||||||
void apply_env_variables();
|
void apply_env_variables();
|
||||||
void set(const std::string &key, const std::string &value);
|
void set(const std::string &key, const std::string &value);
|
||||||
void set(const std::string &key, int 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);
|
void set(const std::string &key, std::vector<std::string> values);
|
||||||
std::string process(std::string str) const;
|
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;
|
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;
|
coord_t y;
|
||||||
Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
|
Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
|
||||||
Point(int _x, int _y): 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);
|
Point(double x, double y);
|
||||||
static Point new_scale(coordf_t x, coordf_t y) {
|
static Point new_scale(coordf_t x, coordf_t y) {
|
||||||
return Point(scale_(x), scale_(y));
|
return Point(scale_(x), scale_(y));
|
||||||
|
@ -93,6 +93,23 @@ inline void polygons_rotate(Polygons &polys, double angle)
|
|||||||
p->rotate(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)
|
inline Lines to_lines(const Polygon &poly)
|
||||||
{
|
{
|
||||||
Lines lines;
|
Lines lines;
|
||||||
@ -179,7 +196,7 @@ namespace boost { namespace polygon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the winding direction of the 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;
|
return unknown_winding;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -220,8 +237,8 @@ namespace boost { namespace polygon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//don't worry about these, just return false from them
|
//don't worry about these, just return false from them
|
||||||
static inline bool clean(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; }
|
static inline bool sorted(const Slic3r::Polygons& /* polygon_set */) { return false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -30,7 +30,7 @@ Print::~Print()
|
|||||||
void
|
void
|
||||||
Print::clear_objects()
|
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->delete_object(i);
|
||||||
|
|
||||||
this->clear_regions();
|
this->clear_regions();
|
||||||
@ -255,70 +255,65 @@ Print::step_done(PrintObjectStep step) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns 0-based indices of used extruders
|
// returns 0-based indices of used extruders
|
||||||
std::set<size_t>
|
std::vector<unsigned int> Print::object_extruders() const
|
||||||
Print::object_extruders() const
|
|
||||||
{
|
{
|
||||||
std::set<size_t> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
|
|
||||||
FOREACH_REGION(this, region) {
|
FOREACH_REGION(this, region) {
|
||||||
// these checks reflect the same logic used in the GUI for enabling/disabling
|
// these checks reflect the same logic used in the GUI for enabling/disabling
|
||||||
// extruder selection fields
|
// extruder selection fields
|
||||||
if ((*region)->config.perimeters.value > 0 || this->config.brim_width.value > 0)
|
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)
|
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)
|
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;
|
return extruders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns 0-based indices of used extruders
|
// returns 0-based indices of used extruders
|
||||||
std::set<size_t>
|
std::vector<unsigned int> Print::support_material_extruders() const
|
||||||
Print::support_material_extruders() const
|
|
||||||
{
|
{
|
||||||
std::set<size_t> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
bool support_uses_current_extruder = false;
|
bool support_uses_current_extruder = false;
|
||||||
|
|
||||||
FOREACH_OBJECT(this, object) {
|
for (PrintObject *object : this->objects) {
|
||||||
if ((*object)->has_support_material()) {
|
if (object->has_support_material()) {
|
||||||
if ((*object)->config.support_material_extruder == 0)
|
if (object->config.support_material_extruder == 0)
|
||||||
support_uses_current_extruder = true;
|
support_uses_current_extruder = true;
|
||||||
else
|
else
|
||||||
extruders.insert((*object)->config.support_material_extruder - 1);
|
extruders.push_back(object->config.support_material_extruder - 1);
|
||||||
if ((*object)->config.support_material_interface_extruder == 0)
|
if (object->config.support_material_interface_extruder == 0)
|
||||||
support_uses_current_extruder = true;
|
support_uses_current_extruder = true;
|
||||||
else
|
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.
|
// 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();
|
append(extruders, this->object_extruders());
|
||||||
extruders.insert(object_extruders.begin(), object_extruders.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
std::sort(extruders.begin(), extruders.end());
|
||||||
|
extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end());
|
||||||
return extruders;
|
return extruders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns 0-based indices of used extruders
|
// returns 0-based indices of used extruders
|
||||||
std::set<size_t>
|
std::vector<unsigned int> Print::extruders() const
|
||||||
Print::extruders() const
|
|
||||||
{
|
{
|
||||||
std::set<size_t> extruders = this->object_extruders();
|
std::vector<unsigned int> extruders = this->object_extruders();
|
||||||
|
append(extruders, this->support_material_extruders());
|
||||||
std::set<size_t> s_extruders = this->support_material_extruders();
|
std::sort(extruders.begin(), extruders.end());
|
||||||
extruders.insert(s_extruders.begin(), s_extruders.end());
|
extruders.erase(std::unique(extruders.begin(), extruders.end()), extruders.end());
|
||||||
|
|
||||||
return extruders;
|
return extruders;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void Print::_simplify_slices(double distance)
|
||||||
Print::_simplify_slices(double distance)
|
|
||||||
{
|
{
|
||||||
FOREACH_OBJECT(this, object) {
|
FOREACH_OBJECT(this, object) {
|
||||||
FOREACH_LAYER(*object, layer) {
|
FOREACH_LAYER(*object, layer) {
|
||||||
@ -330,23 +325,17 @@ Print::_simplify_slices(double distance)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double Print::max_allowed_layer_height() const
|
||||||
Print::max_allowed_layer_height() const
|
|
||||||
{
|
{
|
||||||
std::vector<double> nozzle_diameter;
|
double nozzle_diameter_max = 0.;
|
||||||
|
for (unsigned int extruder_id : this->extruders())
|
||||||
std::set<size_t> extruders = this->extruders();
|
nozzle_diameter_max = std::max(nozzle_diameter_max, this->config.nozzle_diameter.get_at(extruder_id));
|
||||||
for (std::set<size_t>::const_iterator e = extruders.begin(); e != extruders.end(); ++e) {
|
return nozzle_diameter_max;
|
||||||
nozzle_diameter.push_back(this->config.nozzle_diameter.get_at(*e));
|
|
||||||
}
|
|
||||||
|
|
||||||
return *std::max_element(nozzle_diameter.begin(), nozzle_diameter.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Caller is responsible for supplying models whose objects don't collide
|
/* Caller is responsible for supplying models whose objects don't collide
|
||||||
and have explicit instance positions */
|
and have explicit instance positions */
|
||||||
void
|
void Print::add_model_object(ModelObject* model_object, int idx)
|
||||||
Print::add_model_object(ModelObject* model_object, int idx)
|
|
||||||
{
|
{
|
||||||
DynamicPrintConfig object_config = model_object->config; // clone
|
DynamicPrintConfig object_config = model_object->config; // clone
|
||||||
object_config.normalize();
|
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();
|
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
|
// 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;
|
Polygon p = convex_hull;
|
||||||
p.translate(*copy);
|
p.translate(copy);
|
||||||
if (! intersection(a, p).empty())
|
if (! intersection(a, p).empty())
|
||||||
return "Some objects are too close; your extruder will collide with them.";
|
return "Some objects are too close; your extruder will collide with them.";
|
||||||
polygons_append(a, p);
|
polygons_append(a, p);
|
||||||
@ -654,13 +643,13 @@ Print::validate() const
|
|||||||
|
|
||||||
{
|
{
|
||||||
// find the smallest nozzle diameter
|
// find the smallest nozzle diameter
|
||||||
std::set<size_t> extruders = this->extruders();
|
std::vector<unsigned int> extruders = this->extruders();
|
||||||
if (extruders.empty())
|
if (extruders.empty())
|
||||||
return "The supplied settings will cause an empty print.";
|
return "The supplied settings will cause an empty print.";
|
||||||
|
|
||||||
std::set<double> nozzle_diameters;
|
std::vector<double> nozzle_diameters;
|
||||||
for (std::set<size_t>::iterator it = extruders.begin(); it != extruders.end(); ++it)
|
for (unsigned int extruder_id : extruders)
|
||||||
nozzle_diameters.insert(this->config.nozzle_diameter.get_at(*it));
|
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());
|
double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
||||||
|
|
||||||
FOREACH_OBJECT(this, i_object) {
|
FOREACH_OBJECT(this, i_object) {
|
||||||
|
@ -117,7 +117,7 @@ public:
|
|||||||
ModelObject* model_object() { return this->_model_object; }
|
ModelObject* model_object() { return this->_model_object; }
|
||||||
const ModelObject* model_object() const { 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 add_copy(const Pointf &point);
|
||||||
bool delete_last_copy();
|
bool delete_last_copy();
|
||||||
bool delete_all_copies();
|
bool delete_all_copies();
|
||||||
@ -249,9 +249,9 @@ public:
|
|||||||
Flow brim_flow() const;
|
Flow brim_flow() const;
|
||||||
Flow skirt_flow() const;
|
Flow skirt_flow() const;
|
||||||
|
|
||||||
std::set<size_t> object_extruders() const;
|
std::vector<unsigned int> object_extruders() const;
|
||||||
std::set<size_t> support_material_extruders() const;
|
std::vector<unsigned int> support_material_extruders() const;
|
||||||
std::set<size_t> extruders() const;
|
std::vector<unsigned int> extruders() const;
|
||||||
void _simplify_slices(double distance);
|
void _simplify_slices(double distance);
|
||||||
double max_allowed_layer_height() const;
|
double max_allowed_layer_height() const;
|
||||||
bool has_support_material() const;
|
bool has_support_material() const;
|
||||||
|
@ -44,7 +44,7 @@ SlicingParameters SlicingParameters::create_from_config(
|
|||||||
const PrintConfig &print_config,
|
const PrintConfig &print_config,
|
||||||
const PrintObjectConfig &object_config,
|
const PrintObjectConfig &object_config,
|
||||||
coordf_t object_height,
|
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) ?
|
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
|
||||||
object_config.layer_height.value :
|
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.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));
|
params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, 0));
|
||||||
} else {
|
} else {
|
||||||
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ 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, *it_extruder));
|
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, *it_extruder));
|
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);
|
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?
|
//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.;
|
coordf_t average_object_extruder_dmr = 0.;
|
||||||
if (! object_extruders.empty()) {
|
if (! object_extruders.empty()) {
|
||||||
for (std::set<size_t>::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder)
|
for (unsigned int extruder_id : object_extruders)
|
||||||
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(*it_extruder);
|
average_object_extruder_dmr += print_config.nozzle_diameter.get_at(extruder_id);
|
||||||
average_object_extruder_dmr /= coordf_t(object_extruders.size());
|
average_object_extruder_dmr /= coordf_t(object_extruders.size());
|
||||||
}
|
}
|
||||||
params.first_object_layer_height = average_object_extruder_dmr;
|
params.first_object_layer_height = average_object_extruder_dmr;
|
||||||
|
@ -26,7 +26,7 @@ struct SlicingParameters
|
|||||||
const PrintConfig &print_config,
|
const PrintConfig &print_config,
|
||||||
const PrintObjectConfig &object_config,
|
const PrintObjectConfig &object_config,
|
||||||
coordf_t object_height,
|
coordf_t object_height,
|
||||||
const std::set<size_t> &object_extruders);
|
const std::vector<unsigned int> &object_extruders);
|
||||||
|
|
||||||
// Has any raft layers?
|
// Has any raft layers?
|
||||||
bool has_raft() const { return raft_layers() > 0; }
|
bool has_raft() const { return raft_layers() > 0; }
|
||||||
|
@ -145,32 +145,10 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
|
|||||||
m_print_config (&object->print()->config),
|
m_print_config (&object->print()->config),
|
||||||
m_object_config (&object->config),
|
m_object_config (&object->config),
|
||||||
m_slicing_params (slicing_params),
|
m_slicing_params (slicing_params),
|
||||||
|
m_first_layer_flow (support_material_1st_layer_flow(object, float(slicing_params.first_print_layer_height))),
|
||||||
m_first_layer_flow (Flow::new_from_config_width(
|
m_support_material_flow (support_material_flow(object, float(slicing_params.layer_height))),
|
||||||
frSupportMaterial,
|
m_support_material_interface_flow(support_material_interface_flow(object, float(slicing_params.layer_height))),
|
||||||
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
|
m_support_layer_height_min(0.01)
|
||||||
(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)
|
|
||||||
{
|
{
|
||||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||||
m_support_layer_height_min = 1000000.;
|
m_support_layer_height_min = 1000000.;
|
||||||
|
@ -9,7 +9,7 @@ namespace Slic3r {
|
|||||||
|
|
||||||
class SurfaceCollection
|
class SurfaceCollection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Surfaces surfaces;
|
Surfaces surfaces;
|
||||||
|
|
||||||
SurfaceCollection() {};
|
SurfaceCollection() {};
|
||||||
|
@ -14,15 +14,10 @@ REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
|
|||||||
REGISTER_CLASS(ExtrusionSimulator, "ExtrusionSimulator");
|
REGISTER_CLASS(ExtrusionSimulator, "ExtrusionSimulator");
|
||||||
REGISTER_CLASS(Filler, "Filler");
|
REGISTER_CLASS(Filler, "Filler");
|
||||||
REGISTER_CLASS(Flow, "Flow");
|
REGISTER_CLASS(Flow, "Flow");
|
||||||
REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters");
|
|
||||||
REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer");
|
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(GCode, "GCode");
|
||||||
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
REGISTER_CLASS(GCodeSender, "GCode::Sender");
|
||||||
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
||||||
REGISTER_CLASS(GCodePressureEqualizer, "GCode::PressureEqualizer");
|
|
||||||
REGISTER_CLASS(Layer, "Layer");
|
REGISTER_CLASS(Layer, "Layer");
|
||||||
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
REGISTER_CLASS(SupportLayer, "Layer::Support");
|
||||||
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
#ifndef _xsinit_h_
|
#ifndef _xsinit_h_
|
||||||
#define _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 some macros set by Perl which cause compilation errors on Win32
|
||||||
#undef read
|
#undef read
|
||||||
#undef seekdir
|
#undef seekdir
|
||||||
|
@ -29,9 +29,6 @@
|
|||||||
%code{% RETVAL = THIS->has_overhang_point(*point); %};
|
%code{% RETVAL = THIS->has_overhang_point(*point); %};
|
||||||
ExtrusionRole role() const;
|
ExtrusionRole role() const;
|
||||||
ExtrusionLoopRole loop_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_width();
|
||||||
Polygons polygons_covered_by_spacing();
|
Polygons polygons_covered_by_spacing();
|
||||||
%{
|
%{
|
||||||
|
@ -16,9 +16,6 @@
|
|||||||
void append(ExtrusionPath* path)
|
void append(ExtrusionPath* path)
|
||||||
%code{% THIS->paths.push_back(*path); %};
|
%code{% THIS->paths.push_back(*path); %};
|
||||||
double length();
|
double length();
|
||||||
bool is_perimeter();
|
|
||||||
bool is_infill();
|
|
||||||
bool is_solid_infill();
|
|
||||||
Polygons polygons_covered_by_width();
|
Polygons polygons_covered_by_width();
|
||||||
Polygons polygons_covered_by_spacing();
|
Polygons polygons_covered_by_spacing();
|
||||||
Clone<Polyline> polyline()
|
Clone<Polyline> polyline()
|
||||||
|
@ -23,10 +23,8 @@
|
|||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
double length();
|
double length();
|
||||||
ExtrusionRole role() const;
|
ExtrusionRole role() const;
|
||||||
bool is_perimeter();
|
bool is_bridge()
|
||||||
bool is_infill();
|
%code{% RETVAL = is_bridge(THIS->role()); %};
|
||||||
bool is_solid_infill();
|
|
||||||
bool is_bridge();
|
|
||||||
Polygons polygons_covered_by_width();
|
Polygons polygons_covered_by_width();
|
||||||
Polygons polygons_covered_by_spacing();
|
Polygons polygons_covered_by_spacing();
|
||||||
%{
|
%{
|
||||||
|
206
xs/xsp/GCode.xsp
206
xs/xsp/GCode.xsp
@ -4,175 +4,44 @@
|
|||||||
#include <xsinit.h>
|
#include <xsinit.h>
|
||||||
#include "libslic3r/GCode.hpp"
|
#include "libslic3r/GCode.hpp"
|
||||||
#include "libslic3r/GCode/CoolingBuffer.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 {
|
%name{Slic3r::GCode::CoolingBuffer} class CoolingBuffer {
|
||||||
CoolingBuffer(GCode* gcode)
|
CoolingBuffer(GCode* gcode)
|
||||||
%code{% RETVAL = new CoolingBuffer(*gcode); %};
|
%code{% RETVAL = new CoolingBuffer(*gcode); %};
|
||||||
~CoolingBuffer();
|
~CoolingBuffer();
|
||||||
Ref<GCode> gcodegen();
|
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();
|
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 {
|
%name{Slic3r::GCode} class GCode {
|
||||||
GCode();
|
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()
|
Ref<Pointf> origin()
|
||||||
%code{% RETVAL = &(THIS->origin); %};
|
%code{% RETVAL = &(THIS->origin()); %};
|
||||||
|
void set_origin(Pointf* pointf)
|
||||||
Ref<StaticPrintConfig> config()
|
%code{% THIS->set_origin(*pointf); %};
|
||||||
%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();
|
|
||||||
Ref<Point> last_pos()
|
Ref<Point> last_pos()
|
||||||
%code{% RETVAL = &(THIS->last_pos()); %};
|
%code{% RETVAL = &(THIS->last_pos()); %};
|
||||||
void set_last_pos(Point* pos)
|
|
||||||
%code{% THIS->set_last_pos(*pos); %};
|
|
||||||
|
|
||||||
double volumetric_speed()
|
unsigned int layer_count() const;
|
||||||
%code{% RETVAL = THIS->volumetric_speed; %};
|
void set_layer_count(unsigned int value);
|
||||||
void set_volumetric_speed(double value)
|
float elapsed_time() const;
|
||||||
%code{% THIS->volumetric_speed = value; %};
|
void set_elapsed_time(float value);
|
||||||
|
|
||||||
void apply_print_config(StaticPrintConfig* print_config)
|
void apply_print_config(StaticPrintConfig* print_config)
|
||||||
%code{%
|
%code{%
|
||||||
@ -182,40 +51,7 @@
|
|||||||
CONFESS("A PrintConfig object was not supplied to apply_print_config()");
|
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* av = newAV();
|
||||||
av_fill(av, THIS->extruders.size()-1);
|
av_fill(av, THIS->extruders.size()-1);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (std::map<unsigned int,Extruder>::iterator it = THIS->extruders.begin(); it != THIS->extruders.end(); ++it) {
|
for (const Extruder &extruder : THIS->extruders)
|
||||||
av_store(av, i++, perl_to_SV_ref(it->second));
|
av_store(av, i++, perl_to_SV_ref(const_cast<Extruder&>(extruder)));
|
||||||
}
|
|
||||||
RETVAL = newRV_noinc((SV*)av);
|
RETVAL = newRV_noinc((SV*)av);
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
@ -195,30 +195,6 @@ _constant()
|
|||||||
void set_step_started(PrintStep step)
|
void set_step_started(PrintStep step)
|
||||||
%code%{ THIS->state.set_started(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()
|
void clear_filament_stats()
|
||||||
%code%{
|
%code%{
|
||||||
THIS->filament_stats.clear();
|
THIS->filament_stats.clear();
|
||||||
|
@ -185,26 +185,10 @@ PlaceholderParser* O_OBJECT_SLIC3R
|
|||||||
Ref<PlaceholderParser> O_OBJECT_SLIC3R_T
|
Ref<PlaceholderParser> O_OBJECT_SLIC3R_T
|
||||||
Clone<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
|
CoolingBuffer* O_OBJECT_SLIC3R
|
||||||
Ref<CoolingBuffer> O_OBJECT_SLIC3R_T
|
Ref<CoolingBuffer> O_OBJECT_SLIC3R_T
|
||||||
Clone<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
|
GCode* O_OBJECT_SLIC3R
|
||||||
Ref<GCode> O_OBJECT_SLIC3R_T
|
Ref<GCode> O_OBJECT_SLIC3R_T
|
||||||
Clone<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
|
Ref<GCodeWriter> O_OBJECT_SLIC3R_T
|
||||||
Clone<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
|
BridgeDetector* O_OBJECT_SLIC3R
|
||||||
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
|
Ref<BridgeDetector> O_OBJECT_SLIC3R_T
|
||||||
Clone<BridgeDetector> O_OBJECT_SLIC3R_T
|
Clone<BridgeDetector> O_OBJECT_SLIC3R_T
|
||||||
|
@ -105,9 +105,6 @@
|
|||||||
%typemap{GCodeSender*};
|
%typemap{GCodeSender*};
|
||||||
%typemap{Ref<GCodeSender>}{simple};
|
%typemap{Ref<GCodeSender>}{simple};
|
||||||
%typemap{Clone<GCodeSender>}{simple};
|
%typemap{Clone<GCodeSender>}{simple};
|
||||||
%typemap{GCodePressureEqualizer*};
|
|
||||||
%typemap{Ref<GCodePressureEqualizer>}{simple};
|
|
||||||
%typemap{Clone<GCodePressureEqualizer>}{simple};
|
|
||||||
%typemap{BridgeDetector*};
|
%typemap{BridgeDetector*};
|
||||||
%typemap{Ref<BridgeDetector>}{simple};
|
%typemap{Ref<BridgeDetector>}{simple};
|
||||||
%typemap{Clone<BridgeDetector>}{simple};
|
%typemap{Clone<BridgeDetector>}{simple};
|
||||||
@ -152,18 +149,6 @@
|
|||||||
%typemap{Ref<PlaceholderParser>}{simple};
|
%typemap{Ref<PlaceholderParser>}{simple};
|
||||||
%typemap{Clone<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{CoolingBuffer*};
|
||||||
%typemap{Ref<CoolingBuffer>}{simple};
|
%typemap{Ref<CoolingBuffer>}{simple};
|
||||||
%typemap{Clone<CoolingBuffer>}{simple};
|
%typemap{Clone<CoolingBuffer>}{simple};
|
||||||
|
Loading…
Reference in New Issue
Block a user