mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-16 11:17:51 -06:00
Merge branch 'master' into sender
Conflicts: Build.PL
This commit is contained in:
commit
3ae6f2630e
106 changed files with 2262 additions and 994 deletions
12
Build.PL
12
Build.PL
|
@ -19,7 +19,6 @@ my %prereqs = qw(
|
||||||
Moo 1.003001
|
Moo 1.003001
|
||||||
POSIX 0
|
POSIX 0
|
||||||
Scalar::Util 0
|
Scalar::Util 0
|
||||||
Test::Harness 0
|
|
||||||
Test::More 0
|
Test::More 0
|
||||||
Thread::Semaphore 0
|
Thread::Semaphore 0
|
||||||
IO::Scalar 0
|
IO::Scalar 0
|
||||||
|
@ -28,9 +27,8 @@ my %prereqs = qw(
|
||||||
);
|
);
|
||||||
my %recommends = qw(
|
my %recommends = qw(
|
||||||
Class::XSAccessor 0
|
Class::XSAccessor 0
|
||||||
LWP::UserAgent 0
|
|
||||||
Net::Bonjour 0
|
|
||||||
XML::SAX::ExpatXS 0
|
XML::SAX::ExpatXS 0
|
||||||
|
Test::Harness 0
|
||||||
);
|
);
|
||||||
|
|
||||||
my $sudo = grep { $_ eq '--sudo' } @ARGV;
|
my $sudo = grep { $_ eq '--sudo' } @ARGV;
|
||||||
|
@ -40,14 +38,20 @@ if ($gui) {
|
||||||
%prereqs = qw(
|
%prereqs = qw(
|
||||||
Class::Accessor 0
|
Class::Accessor 0
|
||||||
Wx 0.9918
|
Wx 0.9918
|
||||||
|
Socket 2.016
|
||||||
);
|
);
|
||||||
%recommends = qw(
|
%recommends = qw(
|
||||||
Growl::GNTP 0.15
|
Growl::GNTP 0.15
|
||||||
Wx::GLCanvas 0
|
Wx::GLCanvas 0
|
||||||
OpenGL 0
|
OpenGL 0
|
||||||
|
LWP::UserAgent 0
|
||||||
|
Net::Bonjour 0
|
||||||
);
|
);
|
||||||
if ($^O eq 'MSWin32') {
|
if ($^O eq 'MSWin32') {
|
||||||
$recommends{"Win32::TieRegistry"} = 0;
|
$recommends{"Win32::TieRegistry"} = 0;
|
||||||
|
|
||||||
|
# we need an up-to-date Win32::API because older aren't thread-safe (GH #2517)
|
||||||
|
$prereqs{'Win32::API'} = 0.79;
|
||||||
}
|
}
|
||||||
} elsif ($xs_only) {
|
} elsif ($xs_only) {
|
||||||
%prereqs = %recommends = ();
|
%prereqs = %recommends = ();
|
||||||
|
@ -115,7 +119,7 @@ EOF
|
||||||
foreach my $module (sort keys %modules) {
|
foreach my $module (sort keys %modules) {
|
||||||
my $version = $modules{$module};
|
my $version = $modules{$module};
|
||||||
my @cmd = ($cpanm, @cpanm_args);
|
my @cmd = ($cpanm, @cpanm_args);
|
||||||
push @cmd, '-f', if $module eq 'OpenGL'; # temporary workaround for upstream bug in test
|
push @cmd, '--notest', if $module eq 'OpenGL'; # temporary workaround for upstream bug in test
|
||||||
push @cmd, "$module~$version";
|
push @cmd, "$module~$version";
|
||||||
if ($module eq 'XML::SAX::ExpatXS' && $^O eq 'MSWin32') {
|
if ($module eq 'XML::SAX::ExpatXS' && $^O eq 'MSWin32') {
|
||||||
my $mingw = 'C:\dev\CitrusPerl\mingw64';
|
my $mingw = 'C:\dev\CitrusPerl\mingw64';
|
||||||
|
|
15
README.md
15
README.md
|
@ -61,8 +61,8 @@ If you want to compile the source yourself just do the following (checkout
|
||||||
```
|
```
|
||||||
$ git clone https://github.com/alexrj/Slic3r.git
|
$ git clone https://github.com/alexrj/Slic3r.git
|
||||||
$ cd Slic3r
|
$ cd Slic3r
|
||||||
$ sudo perl Build.PL
|
$ perl Build.PL --sudo
|
||||||
$ sudo perl Build.PL --gui
|
$ perl Build.PL --sudo --gui
|
||||||
$ ./slic3r.pl
|
$ ./slic3r.pl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -219,7 +219,8 @@ The author of the Silk icon set is Mark James.
|
||||||
--end-gcode Load final G-code from the supplied file. This will overwrite
|
--end-gcode Load final G-code from the supplied file. This will overwrite
|
||||||
the default commands (turn off temperature [M104 S0],
|
the default commands (turn off temperature [M104 S0],
|
||||||
home X axis [G28 X], disable motors [M84]).
|
home X axis [G28 X], disable motors [M84]).
|
||||||
--layer-gcode Load layer-change G-code from the supplied file (default: nothing).
|
--before-layer-gcode Load before-layer-change G-code from the supplied file (default: nothing).
|
||||||
|
--layer-gcode Load after-layer-change G-code from the supplied file (default: nothing).
|
||||||
--toolchange-gcode Load tool-change G-code from the supplied file (default: nothing).
|
--toolchange-gcode Load tool-change G-code from the supplied file (default: nothing).
|
||||||
--seam-position Position of loop starting points (random/nearest/aligned, default: aligned).
|
--seam-position Position of loop starting points (random/nearest/aligned, default: aligned).
|
||||||
--external-perimeters-first Reverse perimeter order. (default: no)
|
--external-perimeters-first Reverse perimeter order. (default: no)
|
||||||
|
@ -253,6 +254,9 @@ The author of the Silk icon set is Mark James.
|
||||||
Spacing between pattern lines (mm, default: 2.5)
|
Spacing between pattern lines (mm, default: 2.5)
|
||||||
--support-material-angle
|
--support-material-angle
|
||||||
Support material angle in degrees (range: 0-90, default: 0)
|
Support material angle in degrees (range: 0-90, default: 0)
|
||||||
|
--support-material-contact-distance
|
||||||
|
Vertical distance between object and support material
|
||||||
|
(0+, default: 0.2)
|
||||||
--support-material-interface-layers
|
--support-material-interface-layers
|
||||||
Number of perpendicular layers between support material and object (0+, default: 3)
|
Number of perpendicular layers between support material and object (0+, default: 3)
|
||||||
--support-material-interface-spacing
|
--support-material-interface-spacing
|
||||||
|
@ -279,8 +283,8 @@ The author of the Silk icon set is Mark James.
|
||||||
|
|
||||||
Retraction options for multi-extruder setups:
|
Retraction options for multi-extruder setups:
|
||||||
--retract-length-toolchange
|
--retract-length-toolchange
|
||||||
Length of retraction in mm when disabling tool (default: 1)
|
Length of retraction in mm when disabling tool (default: 10)
|
||||||
--retract-restart-extra-toolchnage
|
--retract-restart-extra-toolchange
|
||||||
Additional amount of filament in mm to push after
|
Additional amount of filament in mm to push after
|
||||||
switching tool (default: 0)
|
switching tool (default: 0)
|
||||||
|
|
||||||
|
@ -346,6 +350,7 @@ The author of the Silk icon set is Mark James.
|
||||||
Set a different extrusion width for top infill
|
Set a different extrusion width for top infill
|
||||||
--support-material-extrusion-width
|
--support-material-extrusion-width
|
||||||
Set a different extrusion width for support material
|
Set a different extrusion width for support material
|
||||||
|
--infill-overlap Overlap between infill and perimeters (default: 15%)
|
||||||
--bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1)
|
--bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: 1)
|
||||||
|
|
||||||
Multiple extruder options:
|
Multiple extruder options:
|
||||||
|
|
|
@ -79,7 +79,7 @@ use constant RESOLUTION => 0.0125;
|
||||||
use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR;
|
use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR;
|
||||||
use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI;
|
use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI;
|
||||||
use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15;
|
use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15;
|
||||||
use constant INFILL_OVERLAP_OVER_SPACING => 0.45;
|
use constant INFILL_OVERLAP_OVER_SPACING => 0.3;
|
||||||
use constant EXTERNAL_INFILL_MARGIN => 3;
|
use constant EXTERNAL_INFILL_MARGIN => 3;
|
||||||
use constant INSET_OVERLAP_TOLERANCE => 0.4;
|
use constant INSET_OVERLAP_TOLERANCE => 0.4;
|
||||||
|
|
||||||
|
@ -211,6 +211,7 @@ sub thread_cleanup {
|
||||||
*Slic3r::Polyline::DESTROY = sub {};
|
*Slic3r::Polyline::DESTROY = sub {};
|
||||||
*Slic3r::Polyline::Collection::DESTROY = sub {};
|
*Slic3r::Polyline::Collection::DESTROY = sub {};
|
||||||
*Slic3r::Print::DESTROY = sub {};
|
*Slic3r::Print::DESTROY = sub {};
|
||||||
|
*Slic3r::Print::Object::DESTROY = sub {};
|
||||||
*Slic3r::Print::Region::DESTROY = sub {};
|
*Slic3r::Print::Region::DESTROY = sub {};
|
||||||
*Slic3r::Surface::DESTROY = sub {};
|
*Slic3r::Surface::DESTROY = sub {};
|
||||||
*Slic3r::Surface::Collection::DESTROY = sub {};
|
*Slic3r::Surface::Collection::DESTROY = sub {};
|
||||||
|
|
|
@ -218,6 +218,8 @@ sub validate {
|
||||||
# --first-layer-height
|
# --first-layer-height
|
||||||
die "Invalid value for --first-layer-height\n"
|
die "Invalid value for --first-layer-height\n"
|
||||||
if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
|
if $self->first_layer_height !~ /^(?:\d*(?:\.\d+)?)%?$/;
|
||||||
|
die "Invalid value for --first-layer-height\n"
|
||||||
|
if $self->get_value('first_layer_height') <= 0;
|
||||||
|
|
||||||
# --filament-diameter
|
# --filament-diameter
|
||||||
die "Invalid value for --filament-diameter\n"
|
die "Invalid value for --filament-diameter\n"
|
||||||
|
|
|
@ -142,7 +142,7 @@ sub make_fill {
|
||||||
# we are going to grow such regions by overlapping them with the void (if any)
|
# we are going to grow such regions by overlapping them with the void (if any)
|
||||||
# TODO: detect and investigate whether there could be narrow regions without
|
# TODO: detect and investigate whether there could be narrow regions without
|
||||||
# any void neighbors
|
# any void neighbors
|
||||||
my $distance_between_surfaces = $infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING;
|
my $distance_between_surfaces = $infill_flow->scaled_spacing;
|
||||||
{
|
{
|
||||||
my $collapsed = diff(
|
my $collapsed = diff(
|
||||||
[ map @{$_->expolygon}, @surfaces ],
|
[ map @{$_->expolygon}, @surfaces ],
|
||||||
|
|
|
@ -26,9 +26,7 @@ sub fill_surface {
|
||||||
$self->spacing(unscale $distance);
|
$self->spacing(unscale $distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
# compensate the overlap which is good for rectilinear but harmful for concentric
|
my @loops = my @last = map $_->clone, @$expolygon;
|
||||||
# where the perimeter/infill spacing should be equal to any other loop spacing
|
|
||||||
my @loops = my @last = @{offset(\@$expolygon, -&Slic3r::INFILL_OVERLAP_OVER_SPACING * $min_spacing / 2)};
|
|
||||||
while (@last) {
|
while (@last) {
|
||||||
push @loops, @last = @{offset2(\@last, -($distance + 0.5*$min_spacing), +0.5*$min_spacing)};
|
push @loops, @last = @{offset2(\@last, -($distance + 0.5*$min_spacing), +0.5*$min_spacing)};
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ sub fill_surface {
|
||||||
}
|
}
|
||||||
|
|
||||||
my @paths;
|
my @paths;
|
||||||
if ($params{complete}) {
|
if ($params{complete} || 1) {
|
||||||
# we were requested to complete each loop;
|
# we were requested to complete each loop;
|
||||||
# in this case we don't try to make more continuous paths
|
# in this case we don't try to make more continuous paths
|
||||||
@paths = map $_->split_at_first_point,
|
@paths = map $_->split_at_first_point,
|
||||||
|
|
|
@ -54,10 +54,21 @@ sub fill_surface {
|
||||||
# the minimum offset for preventing edge lines from being clipped is scaled_epsilon;
|
# the minimum offset for preventing edge lines from being clipped is scaled_epsilon;
|
||||||
# however we use a larger offset to support expolygons with slightly skewed sides and
|
# however we use a larger offset to support expolygons with slightly skewed sides and
|
||||||
# not perfectly straight
|
# not perfectly straight
|
||||||
my @polylines = @{intersection_pl(\@vertical_lines, $expolygon->offset(scale 0.02))};
|
my @polylines = @{intersection_pl(\@vertical_lines, $expolygon->offset(+scale 0.02))};
|
||||||
|
|
||||||
|
my $extra = $self->_min_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING;
|
||||||
|
foreach my $polyline (@polylines) {
|
||||||
|
my ($first_point, $last_point) = @$polyline[0,-1];
|
||||||
|
if ($first_point->y > $last_point->y) { #>
|
||||||
|
($first_point, $last_point) = ($last_point, $first_point);
|
||||||
|
}
|
||||||
|
$first_point->set_y($first_point->y - $extra); #--
|
||||||
|
$last_point->set_y($last_point->y + $extra); #++
|
||||||
|
}
|
||||||
|
|
||||||
# connect lines
|
# connect lines
|
||||||
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
|
unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections
|
||||||
|
# offset the expolygon by max(min_spacing/2, extra)
|
||||||
my ($expolygon_off) = @{$expolygon->offset_ex($self->_min_spacing/2)};
|
my ($expolygon_off) = @{$expolygon->offset_ex($self->_min_spacing/2)};
|
||||||
my $collection = Slic3r::Polyline::Collection->new(@polylines);
|
my $collection = Slic3r::Polyline::Collection->new(@polylines);
|
||||||
@polylines = ();
|
@polylines = ();
|
||||||
|
|
|
@ -14,6 +14,9 @@ sub read_file {
|
||||||
$mesh->ReadSTLFile($path);
|
$mesh->ReadSTLFile($path);
|
||||||
$mesh->repair;
|
$mesh->repair;
|
||||||
|
|
||||||
|
die "This STL file couldn't be read because it's empty.\n"
|
||||||
|
if $mesh->facets_count == 0;
|
||||||
|
|
||||||
my $model = Slic3r::Model->new;
|
my $model = Slic3r::Model->new;
|
||||||
|
|
||||||
my $basename = basename($file);
|
my $basename = basename($file);
|
||||||
|
|
|
@ -141,7 +141,7 @@ sub extrude_loop {
|
||||||
my $obj_ptr = 0;
|
my $obj_ptr = 0;
|
||||||
if (defined $self->layer) {
|
if (defined $self->layer) {
|
||||||
$obj_ptr = $self->layer->object->ptr;
|
$obj_ptr = $self->layer->object->ptr;
|
||||||
if (defined $self->_seam_position->{$self->layer->object}) {
|
if (defined $self->_seam_position->{$obj_ptr}) {
|
||||||
$last_pos = $self->_seam_position->{$obj_ptr};
|
$last_pos = $self->_seam_position->{$obj_ptr};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -500,11 +500,14 @@ sub pre_toolchange {
|
||||||
|
|
||||||
# move to the nearest standby point
|
# move to the nearest standby point
|
||||||
if (@{$self->standby_points}) {
|
if (@{$self->standby_points}) {
|
||||||
my $last_pos = $gcodegen->last_pos->clone;
|
# get current position in print coordinates
|
||||||
$last_pos->translate(scale +$gcodegen->origin->x, scale +$gcodegen->origin->y); #))
|
my $pos = Slic3r::Point->new_scale(@{$gcodegen->writer->get_position}[0,1]);
|
||||||
my $standby_point = $last_pos->nearest_point($self->standby_points);
|
|
||||||
$standby_point->translate(scale -$gcodegen->origin->x, scale -$gcodegen->origin->y); #))
|
my $standby_point = Slic3r::Pointf->new_unscale(@{$pos->nearest_point($self->standby_points)});
|
||||||
$gcode .= $gcodegen->travel_to($standby_point, undef, 'move to standby position');
|
# We don't call $gcodegen->travel_to() because we don't need retraction (it was already
|
||||||
|
# triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates
|
||||||
|
# of the destination point must not be transformed by origin nor current extruder offset.
|
||||||
|
$gcode .= $gcodegen->writer->travel_to_xy($standby_point, 'move to standby position');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($gcodegen->config->standby_temperature_delta != 0) {
|
if ($gcodegen->config->standby_temperature_delta != 0) {
|
||||||
|
|
|
@ -17,9 +17,9 @@ use Slic3r::GUI::Plater;
|
||||||
use Slic3r::GUI::Plater::2D;
|
use Slic3r::GUI::Plater::2D;
|
||||||
use Slic3r::GUI::Plater::2DToolpaths;
|
use Slic3r::GUI::Plater::2DToolpaths;
|
||||||
use Slic3r::GUI::Plater::3D;
|
use Slic3r::GUI::Plater::3D;
|
||||||
|
use Slic3r::GUI::Plater::3DPreview;
|
||||||
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
use Slic3r::GUI::Plater::ObjectPartsPanel;
|
||||||
use Slic3r::GUI::Plater::ObjectCutDialog;
|
use Slic3r::GUI::Plater::ObjectCutDialog;
|
||||||
use Slic3r::GUI::Plater::ObjectPreviewDialog;
|
|
||||||
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
use Slic3r::GUI::Plater::ObjectSettingsDialog;
|
||||||
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
use Slic3r::GUI::Plater::OverrideSettingsPanel;
|
||||||
use Slic3r::GUI::Preferences;
|
use Slic3r::GUI::Preferences;
|
||||||
|
@ -29,7 +29,7 @@ use Slic3r::GUI::OptionsGroup::Field;
|
||||||
use Slic3r::GUI::SimpleTab;
|
use Slic3r::GUI::SimpleTab;
|
||||||
use Slic3r::GUI::Tab;
|
use Slic3r::GUI::Tab;
|
||||||
|
|
||||||
our $have_OpenGL = eval "use Slic3r::GUI::PreviewCanvas; 1";
|
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
||||||
our $have_LWP = eval "use LWP::UserAgent; 1";
|
our $have_LWP = eval "use LWP::UserAgent; 1";
|
||||||
|
|
||||||
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
|
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
|
||||||
|
@ -86,9 +86,13 @@ sub OnInit {
|
||||||
# just checking for existence of $datadir is not enough: it may be an empty directory
|
# just checking for existence of $datadir is not enough: it may be an empty directory
|
||||||
# supplied as argument to --datadir; in that case we should still run the wizard
|
# supplied as argument to --datadir; in that case we should still run the wizard
|
||||||
my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1;
|
my $run_wizard = (-d $enc_datadir && -e "$enc_datadir/slic3r.ini") ? 0 : 1;
|
||||||
for ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") {
|
foreach my $dir ($enc_datadir, "$enc_datadir/print", "$enc_datadir/filament", "$enc_datadir/printer") {
|
||||||
mkdir or $self->fatal_error("Slic3r was unable to create its data directory at $_ (errno: $!).")
|
next if -d $dir;
|
||||||
unless -d $_;
|
if (!mkdir $dir) {
|
||||||
|
my $error = "Slic3r was unable to create its data directory at $dir ($!).";
|
||||||
|
warn "$error\n";
|
||||||
|
fatal_error(undef, $error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# load settings
|
# load settings
|
||||||
|
@ -165,22 +169,19 @@ sub catch_error {
|
||||||
|
|
||||||
# static method accepting a wxWindow object as first parameter
|
# static method accepting a wxWindow object as first parameter
|
||||||
sub show_error {
|
sub show_error {
|
||||||
my $self = shift;
|
my ($parent, $message) = @_;
|
||||||
my ($message) = @_;
|
Wx::MessageDialog->new($parent, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal;
|
||||||
Wx::MessageDialog->new($self, $message, 'Error', wxOK | wxICON_ERROR)->ShowModal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# static method accepting a wxWindow object as first parameter
|
# static method accepting a wxWindow object as first parameter
|
||||||
sub show_info {
|
sub show_info {
|
||||||
my $self = shift;
|
my ($parent, $message, $title) = @_;
|
||||||
my ($message, $title) = @_;
|
Wx::MessageDialog->new($parent, $message, $title || 'Notice', wxOK | wxICON_INFORMATION)->ShowModal;
|
||||||
Wx::MessageDialog->new($self, $message, $title || 'Notice', wxOK | wxICON_INFORMATION)->ShowModal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# static method accepting a wxWindow object as first parameter
|
# static method accepting a wxWindow object as first parameter
|
||||||
sub fatal_error {
|
sub fatal_error {
|
||||||
my $self = shift;
|
show_error(@_);
|
||||||
$self->show_error(@_);
|
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,6 +221,7 @@ sub presets {
|
||||||
opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
|
opendir my $dh, Slic3r::encode_path("$Slic3r::GUI::datadir/$section")
|
||||||
or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
|
or die "Failed to read directory $Slic3r::GUI::datadir/$section (errno: $!)\n";
|
||||||
foreach my $file (grep /\.ini$/i, readdir $dh) {
|
foreach my $file (grep /\.ini$/i, readdir $dh) {
|
||||||
|
$file = Slic3r::decode_path($file);
|
||||||
my $name = basename($file);
|
my $name = basename($file);
|
||||||
$name =~ s/\.ini$//;
|
$name =~ s/\.ini$//;
|
||||||
$presets{$name} = "$Slic3r::GUI::datadir/$section/$file";
|
$presets{$name} = "$Slic3r::GUI::datadir/$section/$file";
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package Slic3r::GUI::PreviewCanvas;
|
package Slic3r::GUI::3DScene::Base;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ use Slic3r::Geometry::Clipper qw(offset_ex intersection_pl);
|
||||||
use Wx::GLCanvas qw(:all);
|
use Wx::GLCanvas qw(:all);
|
||||||
|
|
||||||
__PACKAGE__->mk_accessors( qw(_quat _dirty init
|
__PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
|
enable_cutting
|
||||||
enable_picking
|
enable_picking
|
||||||
enable_moving
|
enable_moving
|
||||||
on_hover
|
on_hover
|
||||||
|
@ -21,7 +22,6 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
on_right_click
|
on_right_click
|
||||||
on_move
|
on_move
|
||||||
volumes
|
volumes
|
||||||
print
|
|
||||||
_sphi _stheta
|
_sphi _stheta
|
||||||
cutting_plane_z
|
cutting_plane_z
|
||||||
cut_lines_vertices
|
cut_lines_vertices
|
||||||
|
@ -43,9 +43,9 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
use constant TRACKBALLSIZE => 0.8;
|
use constant TRACKBALLSIZE => 0.8;
|
||||||
use constant TURNTABLE_MODE => 1;
|
use constant TURNTABLE_MODE => 1;
|
||||||
use constant GROUND_Z => -0.02;
|
use constant GROUND_Z => -0.02;
|
||||||
|
use constant DEFAULT_COLOR => [1,1,0];
|
||||||
use constant SELECTED_COLOR => [0,1,0,1];
|
use constant SELECTED_COLOR => [0,1,0,1];
|
||||||
use constant HOVER_COLOR => [0.4,0.9,0,1];
|
use constant HOVER_COLOR => [0.4,0.9,0,1];
|
||||||
use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ];
|
|
||||||
|
|
||||||
# make OpenGL::Array thread-safe
|
# make OpenGL::Array thread-safe
|
||||||
{
|
{
|
||||||
|
@ -135,6 +135,17 @@ sub mouse_event {
|
||||||
if ($self->enable_picking) {
|
if ($self->enable_picking) {
|
||||||
$self->deselect_volumes;
|
$self->deselect_volumes;
|
||||||
$self->select_volume($volume_idx);
|
$self->select_volume($volume_idx);
|
||||||
|
|
||||||
|
if ($volume_idx != -1) {
|
||||||
|
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
|
||||||
|
my @volumes;
|
||||||
|
if ($group_id != -1) {
|
||||||
|
$self->select_volume($_)
|
||||||
|
for grep $self->volumes->[$_]->select_group_id == $group_id,
|
||||||
|
0..$#{$self->volumes};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$self->Refresh;
|
$self->Refresh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +174,13 @@ sub mouse_event {
|
||||||
# get volume being dragged
|
# get volume being dragged
|
||||||
my $volume = $self->volumes->[$self->_drag_volume_idx];
|
my $volume = $self->volumes->[$self->_drag_volume_idx];
|
||||||
|
|
||||||
# get all volumes belonging to the same group but only having the same instance_idx
|
# get all volumes belonging to the same group, if any
|
||||||
my @volumes = grep $_->group_id == $volume->group_id && $_->instance_idx == $volume->instance_idx, @{$self->volumes};
|
my @volumes;
|
||||||
|
if ($volume->drag_group_id == -1) {
|
||||||
|
@volumes = ($volume);
|
||||||
|
} else {
|
||||||
|
@volumes = grep $_->drag_group_id == $volume->drag_group_id, @{$self->volumes};
|
||||||
|
}
|
||||||
|
|
||||||
# apply new temporary volume origin and ignore Z
|
# apply new temporary volume origin and ignore Z
|
||||||
$_->origin->translate($vector->x, $vector->y, 0) for @volumes; #,,
|
$_->origin->translate($vector->x, $vector->y, 0) for @volumes; #,,
|
||||||
|
@ -209,8 +225,17 @@ sub mouse_event {
|
||||||
$self->_drag_start_xy($pos);
|
$self->_drag_start_xy($pos);
|
||||||
}
|
}
|
||||||
} elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
|
} elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
|
||||||
if ($self->on_move && defined $self->_drag_volume_idx) {
|
if ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) {
|
||||||
$self->on_move->($self->_drag_volume_idx) if $self->_dragged;
|
# get all volumes belonging to the same group, if any
|
||||||
|
my @volume_idxs;
|
||||||
|
my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id;
|
||||||
|
if ($group_id == -1) {
|
||||||
|
@volume_idxs = ($self->_drag_volume_idx);
|
||||||
|
} else {
|
||||||
|
@volume_idxs = grep $self->volumes->[$_]->drag_group_id == $group_id,
|
||||||
|
0..$#{$self->volumes};
|
||||||
|
}
|
||||||
|
$self->on_move->(@volume_idxs);
|
||||||
}
|
}
|
||||||
$self->_drag_volume_idx(undef);
|
$self->_drag_volume_idx(undef);
|
||||||
$self->_drag_start_pos(undef);
|
$self->_drag_start_pos(undef);
|
||||||
|
@ -256,7 +281,7 @@ sub zoom_to_volume {
|
||||||
my ($self, $volume_idx) = @_;
|
my ($self, $volume_idx) = @_;
|
||||||
|
|
||||||
my $volume = $self->volumes->[$volume_idx];
|
my $volume = $self->volumes->[$volume_idx];
|
||||||
my $bb = $volume->bounding_box;
|
my $bb = $volume->transformed_bounding_box;
|
||||||
$self->zoom_to_bounding_box($bb);
|
$self->zoom_to_bounding_box($bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +294,7 @@ sub volumes_bounding_box {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
my $bb = Slic3r::Geometry::BoundingBoxf3->new;
|
my $bb = Slic3r::Geometry::BoundingBoxf3->new;
|
||||||
$bb->merge($_->bounding_box) for @{$self->volumes};
|
$bb->merge($_->transformed_bounding_box) for @{$self->volumes};
|
||||||
return $bb;
|
return $bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,59 +373,6 @@ sub set_bed_shape {
|
||||||
$self->origin(Slic3r::Pointf->new(0,0));
|
$self->origin(Slic3r::Pointf->new(0,0));
|
||||||
}
|
}
|
||||||
|
|
||||||
sub load_object {
|
|
||||||
my ($self, $object, $all_instances) = @_;
|
|
||||||
|
|
||||||
my $z_min = $object->raw_bounding_box->z_min;
|
|
||||||
|
|
||||||
# color mesh(es) by material
|
|
||||||
my @materials = ();
|
|
||||||
|
|
||||||
# sort volumes: non-modifiers first
|
|
||||||
my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$object->volumes};
|
|
||||||
my @volumes_idx = ();
|
|
||||||
my $group_id = $#{$self->volumes} + 1;
|
|
||||||
foreach my $volume (@volumes) {
|
|
||||||
my @instance_idxs = $all_instances ? (0..$#{$object->instances}) : (0);
|
|
||||||
foreach my $instance_idx (@instance_idxs) {
|
|
||||||
my $instance = $object->instances->[$instance_idx];
|
|
||||||
my $mesh = $volume->mesh->clone;
|
|
||||||
$instance->transform_mesh($mesh);
|
|
||||||
|
|
||||||
my $material_id = $volume->material_id // '_';
|
|
||||||
my $color_idx = first { $materials[$_] eq $material_id } 0..$#materials;
|
|
||||||
if (!defined $color_idx) {
|
|
||||||
push @materials, $material_id;
|
|
||||||
$color_idx = $#materials;
|
|
||||||
}
|
|
||||||
|
|
||||||
my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ];
|
|
||||||
push @$color, $volume->modifier ? 0.5 : 1;
|
|
||||||
push @{$self->volumes}, my $v = Slic3r::GUI::PreviewCanvas::Volume->new(
|
|
||||||
group_id => $group_id,
|
|
||||||
instance_idx => $instance_idx,
|
|
||||||
mesh => $mesh,
|
|
||||||
color => $color,
|
|
||||||
origin => Slic3r::Pointf3->new(0,0,-$z_min),
|
|
||||||
);
|
|
||||||
push @volumes_idx, $#{$self->volumes};
|
|
||||||
|
|
||||||
{
|
|
||||||
my $vertices = $mesh->vertices;
|
|
||||||
my @verts = map @{ $vertices->[$_] }, map @$_, @{$mesh->facets};
|
|
||||||
$v->verts(OpenGL::Array->new_list(GL_FLOAT, @verts));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
my @norms = map { @$_, @$_, @$_ } @{$mesh->normals};
|
|
||||||
$v->norms(OpenGL::Array->new_list(GL_FLOAT, @norms));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return @volumes_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub deselect_volumes {
|
sub deselect_volumes {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
$_->selected(0) for @{$self->volumes};
|
$_->selected(0) for @{$self->volumes};
|
||||||
|
@ -422,6 +394,7 @@ sub SetCuttingPlane {
|
||||||
my @verts = ();
|
my @verts = ();
|
||||||
foreach my $volume (@{$self->volumes}) {
|
foreach my $volume (@{$self->volumes}) {
|
||||||
foreach my $volume (@{$self->volumes}) {
|
foreach my $volume (@{$self->volumes}) {
|
||||||
|
next if !$volume->mesh;
|
||||||
my $expolygons = $volume->mesh->slice([ $z - $volume->origin->z ])->[0];
|
my $expolygons = $volume->mesh->slice([ $z - $volume->origin->z ])->[0];
|
||||||
$expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
|
$expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
|
||||||
|
|
||||||
|
@ -698,7 +671,13 @@ sub Render {
|
||||||
$_->hover(0) for @{$self->volumes};
|
$_->hover(0) for @{$self->volumes};
|
||||||
if ($volume_idx <= $#{$self->volumes}) {
|
if ($volume_idx <= $#{$self->volumes}) {
|
||||||
$self->_hover_volume_idx($volume_idx);
|
$self->_hover_volume_idx($volume_idx);
|
||||||
|
|
||||||
$self->volumes->[$volume_idx]->hover(1);
|
$self->volumes->[$volume_idx]->hover(1);
|
||||||
|
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
|
||||||
|
if ($group_id != -1) {
|
||||||
|
$_->hover(1) for grep { $_->select_group_id == $group_id } @{$self->volumes};
|
||||||
|
}
|
||||||
|
|
||||||
$self->on_hover->($volume_idx) if $self->on_hover;
|
$self->on_hover->($volume_idx) if $self->on_hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -833,73 +812,6 @@ sub draw_volumes {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
if (defined($self->print) && !$fakecolor) {
|
|
||||||
my $tess = gluNewTess();
|
|
||||||
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
|
||||||
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
|
||||||
gluTessCallback($tess, GLU_TESS_VERTEX, 'DEFAULT');
|
|
||||||
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
|
||||||
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
|
||||||
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
|
|
||||||
|
|
||||||
foreach my $object (@{$self->print->objects}) {
|
|
||||||
foreach my $layer (@{$object->layers}) {
|
|
||||||
my $gap = 0;
|
|
||||||
my $top_z = $layer->print_z;
|
|
||||||
my $bottom_z = $layer->print_z - $layer->height + $gap;
|
|
||||||
|
|
||||||
foreach my $copy (@{ $object->_shifted_copies }) {
|
|
||||||
glPushMatrix();
|
|
||||||
glTranslatef(map unscale($_), @$copy, 0);
|
|
||||||
|
|
||||||
foreach my $slice (@{$layer->slices}) {
|
|
||||||
glColor3f(@{COLORS->[0]});
|
|
||||||
gluTessBeginPolygon($tess);
|
|
||||||
glNormal3f(0,0,1);
|
|
||||||
foreach my $polygon (@$slice) {
|
|
||||||
gluTessBeginContour($tess);
|
|
||||||
gluTessVertex_p($tess, (map unscale($_), @$_), $layer->print_z) for @$polygon;
|
|
||||||
gluTessEndContour($tess);
|
|
||||||
}
|
|
||||||
gluTessEndPolygon($tess);
|
|
||||||
|
|
||||||
foreach my $polygon (@$slice) {
|
|
||||||
foreach my $line (@{$polygon->lines}) {
|
|
||||||
if (0) {
|
|
||||||
glLineWidth(1);
|
|
||||||
glColor3f(0,0,0);
|
|
||||||
glBegin(GL_LINES);
|
|
||||||
glVertex3f((map unscale($_), @{$line->a}), $bottom_z);
|
|
||||||
glVertex3f((map unscale($_), @{$line->b}), $bottom_z);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
glLineWidth(0);
|
|
||||||
glColor3f(@{COLORS->[0]});
|
|
||||||
glBegin(GL_QUADS);
|
|
||||||
# We'll use this for the middle normal when using 4 quads:
|
|
||||||
#my $xy_normal = $line->normal;
|
|
||||||
#$_xynormal->scale(1/$line->length);
|
|
||||||
glNormal3f(0,0,-1);
|
|
||||||
glVertex3f((map unscale($_), @{$line->a}), $bottom_z);
|
|
||||||
glVertex3f((map unscale($_), @{$line->b}), $bottom_z);
|
|
||||||
glNormal3f(0,0,1);
|
|
||||||
glVertex3f((map unscale($_), @{$line->b}), $top_z);
|
|
||||||
glVertex3f((map unscale($_), @{$line->a}), $top_z);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
glPopMatrix(); # copy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gluDeleteTess($tess);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
glEnableClientState(GL_NORMAL_ARRAY);
|
glEnableClientState(GL_NORMAL_ARRAY);
|
||||||
|
|
||||||
|
@ -908,10 +820,6 @@ sub draw_volumes {
|
||||||
glPushMatrix();
|
glPushMatrix();
|
||||||
glTranslatef(@{$volume->origin});
|
glTranslatef(@{$volume->origin});
|
||||||
|
|
||||||
glVertexPointer_p(3, $volume->verts);
|
|
||||||
|
|
||||||
glCullFace(GL_BACK);
|
|
||||||
glNormalPointer_p($volume->norms);
|
|
||||||
if ($fakecolor) {
|
if ($fakecolor) {
|
||||||
my $r = ($volume_idx & 0x000000FF) >> 0;
|
my $r = ($volume_idx & 0x000000FF) >> 0;
|
||||||
my $g = ($volume_idx & 0x0000FF00) >> 8;
|
my $g = ($volume_idx & 0x0000FF00) >> 8;
|
||||||
|
@ -924,7 +832,49 @@ sub draw_volumes {
|
||||||
} else {
|
} else {
|
||||||
glColor4f(@{ $volume->color });
|
glColor4f(@{ $volume->color });
|
||||||
}
|
}
|
||||||
glDrawArrays(GL_TRIANGLES, 0, $volume->verts->elements / 3);
|
|
||||||
|
my @sorted_z = ();
|
||||||
|
my ($min_z, $max_z);
|
||||||
|
if ($volume->range && $volume->offsets) {
|
||||||
|
@sorted_z = sort { $a <=> $b } keys %{$volume->offsets};
|
||||||
|
|
||||||
|
($min_z, $max_z) = @{$volume->range};
|
||||||
|
$min_z = first { $_ >= $min_z } @sorted_z;
|
||||||
|
$max_z = first { $_ > $max_z } @sorted_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
glCullFace(GL_BACK);
|
||||||
|
if ($volume->qverts) {
|
||||||
|
my ($min_offset, $max_offset);
|
||||||
|
if (defined $min_z) {
|
||||||
|
$min_offset = $volume->offsets->{$min_z}->[0];
|
||||||
|
}
|
||||||
|
if (defined $max_z) {
|
||||||
|
$max_offset = $volume->offsets->{$max_z}->[0];
|
||||||
|
}
|
||||||
|
$min_offset //= 0;
|
||||||
|
$max_offset //= $volume->qverts->size;
|
||||||
|
|
||||||
|
glVertexPointer_c(3, GL_FLOAT, 0, $volume->qverts->verts_ptr);
|
||||||
|
glNormalPointer_c(GL_FLOAT, 0, $volume->qverts->norms_ptr);
|
||||||
|
glDrawArrays(GL_QUADS, $min_offset / 3, ($max_offset-$min_offset) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($volume->tverts) {
|
||||||
|
my ($min_offset, $max_offset);
|
||||||
|
if (defined $min_z) {
|
||||||
|
$min_offset = $volume->offsets->{$min_z}->[1];
|
||||||
|
}
|
||||||
|
if (defined $max_z) {
|
||||||
|
$max_offset = $volume->offsets->{$max_z}->[1];
|
||||||
|
}
|
||||||
|
$min_offset //= 0;
|
||||||
|
$max_offset //= $volume->tverts->size;
|
||||||
|
|
||||||
|
glVertexPointer_c(3, GL_FLOAT, 0, $volume->tverts->verts_ptr);
|
||||||
|
glNormalPointer_c(GL_FLOAT, 0, $volume->tverts->norms_ptr);
|
||||||
|
glDrawArrays(GL_TRIANGLES, $min_offset / 3, ($max_offset-$min_offset) / 3);
|
||||||
|
}
|
||||||
|
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
@ -940,25 +890,352 @@ sub draw_volumes {
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
package Slic3r::GUI::PreviewCanvas::Volume;
|
package Slic3r::GUI::3DScene::Volume;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
has 'mesh' => (is => 'ro', required => 1);
|
has 'bounding_box' => (is => 'ro', required => 1);
|
||||||
has 'color' => (is => 'ro', required => 1);
|
|
||||||
has 'group_id' => (is => 'ro', required => 1);
|
|
||||||
has 'instance_idx' => (is => 'ro', default => sub { 0 });
|
|
||||||
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
|
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
|
||||||
has 'verts' => (is => 'rw');
|
has 'color' => (is => 'ro', required => 1);
|
||||||
has 'norms' => (is => 'rw');
|
has 'select_group_id' => (is => 'rw', default => sub { -1 });
|
||||||
|
has 'drag_group_id' => (is => 'rw', default => sub { -1 });
|
||||||
has 'selected' => (is => 'rw', default => sub { 0 });
|
has 'selected' => (is => 'rw', default => sub { 0 });
|
||||||
has 'hover' => (is => 'rw', default => sub { 0 });
|
has 'hover' => (is => 'rw', default => sub { 0 });
|
||||||
|
has 'range' => (is => 'rw');
|
||||||
|
|
||||||
sub bounding_box {
|
# geometric data
|
||||||
|
has 'qverts' => (is => 'rw'); # GLVertexArray object
|
||||||
|
has 'tverts' => (is => 'rw'); # GLVertexArray object
|
||||||
|
has 'mesh' => (is => 'rw'); # only required for cut contours
|
||||||
|
has 'offsets' => (is => 'rw'); # [ z => [ qverts_idx, tverts_idx ] ]
|
||||||
|
|
||||||
|
sub transformed_bounding_box {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
my $bb = $self->mesh->bounding_box;
|
my $bb = $self->bounding_box;
|
||||||
$bb->translate(@{$self->origin});
|
$bb->translate(@{$self->origin});
|
||||||
return $bb;
|
return $bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package Slic3r::GUI::3DScene;
|
||||||
|
use base qw(Slic3r::GUI::3DScene::Base);
|
||||||
|
|
||||||
|
use OpenGL qw(:glconstants :gluconstants :glufunctions);
|
||||||
|
use List::Util qw(first);
|
||||||
|
use Slic3r::Geometry qw(scale unscale epsilon);
|
||||||
|
use Slic3r::Print::State ':steps';
|
||||||
|
|
||||||
|
use constant COLORS => [ [1,1,0,1], [1,0.5,0.5,1], [0.5,1,0.5,1], [0.5,0.5,1,1] ];
|
||||||
|
|
||||||
|
__PACKAGE__->mk_accessors(qw(
|
||||||
|
color_by
|
||||||
|
select_by
|
||||||
|
drag_by
|
||||||
|
volumes_by_object
|
||||||
|
_objects_by_volumes
|
||||||
|
));
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $class = shift;
|
||||||
|
|
||||||
|
my $self = $class->SUPER::new(@_);
|
||||||
|
$self->color_by('volume'); # object | volume
|
||||||
|
$self->select_by('object'); # object | volume | instance
|
||||||
|
$self->drag_by('instance'); # object | instance
|
||||||
|
$self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ]
|
||||||
|
$self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ]
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load_object {
|
||||||
|
my ($self, $model, $obj_idx, $instance_idxs) = @_;
|
||||||
|
|
||||||
|
my $model_object;
|
||||||
|
if ($model->isa('Slic3r::Model::Object')) {
|
||||||
|
$model_object = $model;
|
||||||
|
$model = $model_object->model;
|
||||||
|
$obj_idx = 0;
|
||||||
|
} else {
|
||||||
|
$model_object = $model->get_object($obj_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
$instance_idxs ||= [0..$#{$model_object->instances}];
|
||||||
|
|
||||||
|
my @volumes_idx = ();
|
||||||
|
foreach my $volume_idx (0..$#{$model_object->volumes}) {
|
||||||
|
my $volume = $model_object->volumes->[$volume_idx];
|
||||||
|
foreach my $instance_idx (@$instance_idxs) {
|
||||||
|
my $instance = $model_object->instances->[$instance_idx];
|
||||||
|
my $mesh = $volume->mesh->clone;
|
||||||
|
$instance->transform_mesh($mesh);
|
||||||
|
|
||||||
|
my $color_idx;
|
||||||
|
if ($self->color_by eq 'volume') {
|
||||||
|
$color_idx = $volume_idx;
|
||||||
|
} elsif ($self->color_by eq 'object') {
|
||||||
|
$color_idx = $obj_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ];
|
||||||
|
$color->[3] = $volume->modifier ? 0.5 : 1;
|
||||||
|
push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new(
|
||||||
|
bounding_box => $mesh->bounding_box,
|
||||||
|
color => $color,
|
||||||
|
);
|
||||||
|
$v->mesh($mesh) if $self->enable_cutting;
|
||||||
|
if ($self->select_by eq 'object') {
|
||||||
|
$v->select_group_id($obj_idx*1000000);
|
||||||
|
} elsif ($self->select_by eq 'volume') {
|
||||||
|
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000);
|
||||||
|
} elsif ($self->select_by eq 'instance') {
|
||||||
|
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
|
||||||
|
}
|
||||||
|
if ($self->drag_by eq 'object') {
|
||||||
|
$v->drag_group_id($obj_idx*1000);
|
||||||
|
} elsif ($self->drag_by eq 'instance') {
|
||||||
|
$v->drag_group_id($obj_idx*1000 + $instance_idx);
|
||||||
|
}
|
||||||
|
push @volumes_idx, my $scene_volume_idx = $#{$self->volumes};
|
||||||
|
$self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ];
|
||||||
|
|
||||||
|
my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
$verts->load_mesh($mesh);
|
||||||
|
$v->tverts($verts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$self->volumes_by_object->{$obj_idx} = [@volumes_idx];
|
||||||
|
return @volumes_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load_print_object_slices {
|
||||||
|
my ($self, $object) = @_;
|
||||||
|
|
||||||
|
my @verts = ();
|
||||||
|
my @norms = ();
|
||||||
|
my @quad_verts = ();
|
||||||
|
my @quad_norms = ();
|
||||||
|
foreach my $layer (@{$object->layers}) {
|
||||||
|
my $gap = 0;
|
||||||
|
my $top_z = $layer->print_z;
|
||||||
|
my $bottom_z = $layer->print_z - $layer->height + $gap;
|
||||||
|
|
||||||
|
foreach my $copy (@{ $object->_shifted_copies }) {
|
||||||
|
{
|
||||||
|
my @expolygons = map $_->clone, @{$layer->slices};
|
||||||
|
$_->translate(@$copy) for @expolygons;
|
||||||
|
$self->_expolygons_to_verts(\@expolygons, $layer->print_z, \@verts, \@norms);
|
||||||
|
}
|
||||||
|
foreach my $slice (@{$layer->slices}) {
|
||||||
|
foreach my $polygon (@$slice) {
|
||||||
|
foreach my $line (@{$polygon->lines}) {
|
||||||
|
$line->translate(@$copy);
|
||||||
|
|
||||||
|
push @quad_norms, (0,0,-1), (0,0,-1);
|
||||||
|
push @quad_verts, (map unscale($_), @{$line->a}), $bottom_z;
|
||||||
|
push @quad_verts, (map unscale($_), @{$line->b}), $bottom_z;
|
||||||
|
push @quad_norms, (0,0,1), (0,0,1);
|
||||||
|
push @quad_verts, (map unscale($_), @{$line->b}), $top_z;
|
||||||
|
push @quad_verts, (map unscale($_), @{$line->a}), $top_z;
|
||||||
|
|
||||||
|
# We'll use this for the middle normal when using 4 quads:
|
||||||
|
#my $xy_normal = $line->normal;
|
||||||
|
#$_xynormal->scale(1/$line->length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $obb = $object->bounding_box;
|
||||||
|
my $bb = Slic3r::Geometry::BoundingBoxf3->new;
|
||||||
|
$bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->min_point}, 0));
|
||||||
|
$bb->merge_point(Slic3r::Pointf3->new_unscale(@{$obb->max_point}, $object->size->z));
|
||||||
|
|
||||||
|
push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new(
|
||||||
|
bounding_box => $bb,
|
||||||
|
color => COLORS->[0],
|
||||||
|
verts => OpenGL::Array->new_list(GL_FLOAT, @verts),
|
||||||
|
norms => OpenGL::Array->new_list(GL_FLOAT, @norms),
|
||||||
|
quad_verts => OpenGL::Array->new_list(GL_FLOAT, @quad_verts),
|
||||||
|
quad_norms => OpenGL::Array->new_list(GL_FLOAT, @quad_norms),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load_print_object_toolpaths {
|
||||||
|
my ($self, $object) = @_;
|
||||||
|
|
||||||
|
my $perim_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
my $perim_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
my $infill_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
my $infill_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
my $support_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
my $support_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||||
|
|
||||||
|
my %perim_offsets = (); # print_z => [ qverts, tverts ]
|
||||||
|
my %infill_offsets = ();
|
||||||
|
my %support_offsets = ();
|
||||||
|
|
||||||
|
# order layers by print_z
|
||||||
|
my @layers = sort { $a->print_z <=> $b->print_z }
|
||||||
|
@{$object->layers}, @{$object->support_layers};
|
||||||
|
|
||||||
|
foreach my $layer (@layers) {
|
||||||
|
my $top_z = $layer->print_z;
|
||||||
|
|
||||||
|
if (!exists $perim_offsets{$top_z}) {
|
||||||
|
$perim_offsets{$top_z} = [
|
||||||
|
$perim_qverts->size, $perim_tverts->size,
|
||||||
|
];
|
||||||
|
$infill_offsets{$top_z} = [
|
||||||
|
$infill_qverts->size, $infill_tverts->size,
|
||||||
|
];
|
||||||
|
$support_offsets{$top_z} = [
|
||||||
|
$support_qverts->size, $support_tverts->size,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $copy (@{ $object->_shifted_copies }) {
|
||||||
|
foreach my $layerm (@{$layer->regions}) {
|
||||||
|
if ($object->step_done(STEP_PERIMETERS)) {
|
||||||
|
$self->_extrusionentity_to_verts($layerm->perimeters, $top_z, $copy,
|
||||||
|
$perim_qverts, $perim_tverts);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($object->step_done(STEP_INFILL)) {
|
||||||
|
$self->_extrusionentity_to_verts($layerm->fills, $top_z, $copy,
|
||||||
|
$infill_qverts, $infill_tverts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($layer->isa('Slic3r::Layer::Support') && $object->step_done(STEP_SUPPORTMATERIAL)) {
|
||||||
|
$self->_extrusionentity_to_verts($layer->support_fills, $top_z, $copy,
|
||||||
|
$support_qverts, $support_tverts);
|
||||||
|
|
||||||
|
$self->_extrusionentity_to_verts($layer->support_interface_fills, $top_z, $copy,
|
||||||
|
$support_qverts, $support_tverts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $obb = $object->bounding_box;
|
||||||
|
my $bb = Slic3r::Geometry::BoundingBoxf3->new;
|
||||||
|
foreach my $copy (@{ $object->_shifted_copies }) {
|
||||||
|
my $cbb = $obb->clone;
|
||||||
|
$cbb->translate(@$copy);
|
||||||
|
$bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->min_point}, 0));
|
||||||
|
$bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->max_point}, $object->size->z));
|
||||||
|
}
|
||||||
|
|
||||||
|
push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new(
|
||||||
|
bounding_box => $bb,
|
||||||
|
color => COLORS->[0],
|
||||||
|
qverts => $perim_qverts,
|
||||||
|
tverts => $perim_tverts,
|
||||||
|
offsets => { %perim_offsets },
|
||||||
|
);
|
||||||
|
|
||||||
|
push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new(
|
||||||
|
bounding_box => $bb,
|
||||||
|
color => COLORS->[1],
|
||||||
|
qverts => $infill_qverts,
|
||||||
|
tverts => $infill_tverts,
|
||||||
|
offsets => { %infill_offsets },
|
||||||
|
);
|
||||||
|
|
||||||
|
push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new(
|
||||||
|
bounding_box => $bb,
|
||||||
|
color => COLORS->[2],
|
||||||
|
qverts => $support_qverts,
|
||||||
|
tverts => $support_tverts,
|
||||||
|
offsets => { %support_offsets },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_toolpaths_range {
|
||||||
|
my ($self, $min_z, $max_z) = @_;
|
||||||
|
|
||||||
|
foreach my $volume (@{$self->volumes}) {
|
||||||
|
$volume->range([ $min_z, $max_z ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _expolygons_to_verts {
|
||||||
|
my ($self, $expolygons, $z, $verts, $norms) = @_;
|
||||||
|
|
||||||
|
my $tess = gluNewTess();
|
||||||
|
gluTessCallback($tess, GLU_TESS_BEGIN, 'DEFAULT');
|
||||||
|
gluTessCallback($tess, GLU_TESS_END, 'DEFAULT');
|
||||||
|
gluTessCallback($tess, GLU_TESS_VERTEX, sub {
|
||||||
|
my ($x, $y, $z) = @_;
|
||||||
|
push @$verts, $x, $y, $z;
|
||||||
|
push @$norms, (0,0,1), (0,0,1), (0,0,1);
|
||||||
|
});
|
||||||
|
gluTessCallback($tess, GLU_TESS_COMBINE, 'DEFAULT');
|
||||||
|
gluTessCallback($tess, GLU_TESS_ERROR, 'DEFAULT');
|
||||||
|
gluTessCallback($tess, GLU_TESS_EDGE_FLAG, 'DEFAULT');
|
||||||
|
|
||||||
|
foreach my $expolygon (@$expolygons) {
|
||||||
|
gluTessBeginPolygon($tess);
|
||||||
|
foreach my $polygon (@$expolygon) {
|
||||||
|
gluTessBeginContour($tess);
|
||||||
|
gluTessVertex_p($tess, (map unscale($_), @$_), $z) for @$polygon;
|
||||||
|
gluTessEndContour($tess);
|
||||||
|
}
|
||||||
|
gluTessEndPolygon($tess);
|
||||||
|
}
|
||||||
|
|
||||||
|
gluDeleteTess($tess);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _extrusionentity_to_verts {
|
||||||
|
my ($self, $entity, $top_z, $copy, $qverts, $tverts) = @_;
|
||||||
|
|
||||||
|
my ($lines, $widths, $heights, $closed);
|
||||||
|
if ($entity->isa('Slic3r::ExtrusionPath::Collection')) {
|
||||||
|
$self->_extrusionentity_to_verts($_, $top_z, $copy, $qverts, $tverts)
|
||||||
|
for @$entity;
|
||||||
|
return;
|
||||||
|
} elsif ($entity->isa('Slic3r::ExtrusionPath')) {
|
||||||
|
my $polyline = $entity->polyline->clone;
|
||||||
|
$polyline->remove_duplicate_points;
|
||||||
|
$polyline->translate(@$copy);
|
||||||
|
$lines = $polyline->lines;
|
||||||
|
$widths = [ map $entity->width, 0..$#$lines ];
|
||||||
|
$heights = [ map $entity->height, 0..$#$lines ];
|
||||||
|
$closed = 0;
|
||||||
|
} else {
|
||||||
|
$lines = [];
|
||||||
|
$widths = [];
|
||||||
|
$heights = [];
|
||||||
|
$closed = 1;
|
||||||
|
foreach my $path (@$entity) {
|
||||||
|
my $polyline = $path->polyline->clone;
|
||||||
|
$polyline->remove_duplicate_points;
|
||||||
|
$polyline->translate(@$copy);
|
||||||
|
my $path_lines = $polyline->lines;
|
||||||
|
push @$lines, @$path_lines;
|
||||||
|
push @$widths, map $path->width, 0..$#$path_lines;
|
||||||
|
push @$heights, map $path->height, 0..$#$path_lines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights,
|
||||||
|
$closed, $top_z, $copy, $qverts, $tverts);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub object_idx {
|
||||||
|
my ($self, $volume_idx) = @_;
|
||||||
|
return $self->_objects_by_volumes->{$volume_idx}[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub volume_idx {
|
||||||
|
my ($self, $volume_idx) = @_;
|
||||||
|
return $self->_objects_by_volumes->{$volume_idx}[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
sub instance_idx {
|
||||||
|
my ($self, $volume_idx) = @_;
|
||||||
|
return $self->_objects_by_volumes->{$volume_idx}[2];
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
|
@ -47,5 +47,9 @@ sub GetValue {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return $self->{devices}[ $self->{choice}->GetSelection ]->address;
|
return $self->{devices}[ $self->{choice}->GetSelection ]->address;
|
||||||
}
|
}
|
||||||
|
sub GetPort {
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->{devices}[ $self->{choice}->GetSelection ]->port;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -47,8 +47,14 @@ sub new {
|
||||||
$self->Fit;
|
$self->Fit;
|
||||||
$self->SetMinSize([760, 490]);
|
$self->SetMinSize([760, 490]);
|
||||||
if (defined $Slic3r::GUI::Settings->{_}{main_frame_size}) {
|
if (defined $Slic3r::GUI::Settings->{_}{main_frame_size}) {
|
||||||
$self->SetSize([ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ]);
|
my $size = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_size}, 2 ];
|
||||||
$self->Move([ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ]);
|
$self->SetSize($size);
|
||||||
|
|
||||||
|
my $display = Wx::Display->new->GetClientArea();
|
||||||
|
my $pos = [ split ',', $Slic3r::GUI::Settings->{_}{main_frame_pos}, 2 ];
|
||||||
|
if (($pos->[X] + $size->[X]/2) < $display->GetRight && ($pos->[Y] + $size->[Y]/2) < $display->GetBottom) {
|
||||||
|
$self->Move($pos);
|
||||||
|
}
|
||||||
$self->Maximize(1) if $Slic3r::GUI::Settings->{_}{main_frame_maximized};
|
$self->Maximize(1) if $Slic3r::GUI::Settings->{_}{main_frame_maximized};
|
||||||
} else {
|
} else {
|
||||||
$self->SetSize($self->GetMinSize);
|
$self->SetSize($self->GetMinSize);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl
|
||||||
:panel :sizer :toolbar :window wxTheApp :notebook);
|
:panel :sizer :toolbar :window wxTheApp :notebook);
|
||||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||||
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||||
EVT_CHOICE EVT_TIMER);
|
EVT_CHOICE EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||||
use base 'Wx::Panel';
|
use base 'Wx::Panel';
|
||||||
|
|
||||||
use constant TB_ADD => &Wx::NewId;
|
use constant TB_ADD => &Wx::NewId;
|
||||||
|
@ -28,7 +28,6 @@ use constant TB_SCALE => &Wx::NewId;
|
||||||
use constant TB_SPLIT => &Wx::NewId;
|
use constant TB_SPLIT => &Wx::NewId;
|
||||||
use constant TB_CUT => &Wx::NewId;
|
use constant TB_CUT => &Wx::NewId;
|
||||||
use constant TB_SETTINGS => &Wx::NewId;
|
use constant TB_SETTINGS => &Wx::NewId;
|
||||||
use constant CONFIG_TIMER_ID => &Wx::NewId;
|
|
||||||
|
|
||||||
# package variables to avoid passing lexicals to threads
|
# package variables to avoid passing lexicals to threads
|
||||||
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
|
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
|
||||||
|
@ -53,8 +52,6 @@ sub new {
|
||||||
$self->{model} = Slic3r::Model->new;
|
$self->{model} = Slic3r::Model->new;
|
||||||
$self->{print} = Slic3r::Print->new;
|
$self->{print} = Slic3r::Print->new;
|
||||||
$self->{objects} = [];
|
$self->{objects} = [];
|
||||||
$self->{apply_config_timer} = Wx::Timer->new($self, CONFIG_TIMER_ID)
|
|
||||||
if $Slic3r::have_threads;
|
|
||||||
|
|
||||||
$self->{print}->set_status_cb(sub {
|
$self->{print}->set_status_cb(sub {
|
||||||
my ($percent, $message) = @_;
|
my ($percent, $message) = @_;
|
||||||
|
@ -87,32 +84,47 @@ sub new {
|
||||||
$canvas->PopupMenu($menu, $click_pos);
|
$canvas->PopupMenu($menu, $click_pos);
|
||||||
$menu->Destroy;
|
$menu->Destroy;
|
||||||
};
|
};
|
||||||
my $on_instance_moved = sub {
|
my $on_instances_moved = sub {
|
||||||
my ($obj_idx, $instance_idx) = @_;
|
|
||||||
$self->update;
|
$self->update;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Initialize 3D plater
|
||||||
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
|
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
|
||||||
|
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
|
||||||
|
$self->{canvas3D}->set_on_select_object($on_select_object);
|
||||||
|
$self->{canvas3D}->set_on_double_click($on_double_click);
|
||||||
|
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
|
||||||
|
$self->{canvas3D}->set_on_instances_moved($on_instances_moved);
|
||||||
|
}
|
||||||
|
|
||||||
# Initialize 2D preview canvas
|
# Initialize 2D preview canvas
|
||||||
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
|
$self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config});
|
||||||
$self->{preview_notebook}->AddPage($self->{canvas}, '2D');
|
$self->{preview_notebook}->AddPage($self->{canvas}, '2D');
|
||||||
$self->{canvas}->on_select_object($on_select_object);
|
$self->{canvas}->on_select_object($on_select_object);
|
||||||
$self->{canvas}->on_double_click($on_double_click);
|
$self->{canvas}->on_double_click($on_double_click);
|
||||||
$self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
|
$self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); });
|
||||||
$self->{canvas}->on_instance_moved($on_instance_moved);
|
$self->{canvas}->on_instances_moved($on_instances_moved);
|
||||||
|
|
||||||
# Initialize 3D preview and toolpaths preview
|
# Initialize 3D toolpaths preview
|
||||||
if ($Slic3r::GUI::have_OpenGL) {
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
|
$self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print});
|
||||||
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
|
$self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview');
|
||||||
$self->{canvas3D}->set_on_select_object($on_select_object);
|
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
|
||||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
|
||||||
$self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
|
|
||||||
$self->{canvas3D}->set_on_instance_moved($on_instance_moved);
|
|
||||||
|
|
||||||
$self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print});
|
|
||||||
$self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Initialize toolpaths preview
|
||||||
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
|
$self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print});
|
||||||
|
$self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Layers');
|
||||||
|
}
|
||||||
|
|
||||||
|
EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub {
|
||||||
|
if ($self->{preview_notebook}->GetSelection == $self->{preview3D_page_idx}) {
|
||||||
|
$self->{preview3D}->load_print;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
# toolbar for object manipulation
|
# toolbar for object manipulation
|
||||||
if (!&Wx::wxMSW) {
|
if (!&Wx::wxMSW) {
|
||||||
Wx::ToolTip::Enable(1);
|
Wx::ToolTip::Enable(1);
|
||||||
|
@ -250,7 +262,8 @@ sub new {
|
||||||
}
|
}
|
||||||
|
|
||||||
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
||||||
for grep defined($_), $self, $self->{canvas}, $self->{canvas3D}, $self->{list};
|
for grep defined($_),
|
||||||
|
$self, $self->{canvas}, $self->{canvas3D}, $self->{preview3D}, $self->{list};
|
||||||
|
|
||||||
EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub {
|
EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub {
|
||||||
my ($self, $event) = @_;
|
my ($self, $event) = @_;
|
||||||
|
@ -282,16 +295,23 @@ sub new {
|
||||||
Slic3r::thread_cleanup();
|
Slic3r::thread_cleanup();
|
||||||
});
|
});
|
||||||
|
|
||||||
EVT_TIMER($self, CONFIG_TIMER_ID, sub {
|
if ($Slic3r::have_threads) {
|
||||||
|
my $timer_id = Wx::NewId();
|
||||||
|
$self->{apply_config_timer} = Wx::Timer->new($self, $timer_id);
|
||||||
|
EVT_TIMER($self, $timer_id, sub {
|
||||||
my ($self, $event) = @_;
|
my ($self, $event) = @_;
|
||||||
$self->async_apply_config;
|
$self->async_apply_config;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$self->{canvas}->update_bed_size;
|
$self->{canvas}->update_bed_size;
|
||||||
if ($self->{canvas3D}) {
|
if ($self->{canvas3D}) {
|
||||||
$self->{canvas3D}->update_bed_size;
|
$self->{canvas3D}->update_bed_size;
|
||||||
$self->{canvas3D}->zoom_to_bed;
|
$self->{canvas3D}->zoom_to_bed;
|
||||||
}
|
}
|
||||||
|
if ($self->{preview3D}) {
|
||||||
|
$self->{preview3D}->set_bed_shape($self->{config}->bed_shape);
|
||||||
|
}
|
||||||
$self->update;
|
$self->update;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -549,6 +569,7 @@ sub remove {
|
||||||
|
|
||||||
# Prevent toolpaths preview from rendering while we modify the Print object
|
# Prevent toolpaths preview from rendering while we modify the Print object
|
||||||
$self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D};
|
$self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D};
|
||||||
|
$self->{preview3D}->enabled(0) if $self->{preview3D};
|
||||||
|
|
||||||
# if no object index is supplied, remove the selected one
|
# if no object index is supplied, remove the selected one
|
||||||
if (!defined $obj_idx) {
|
if (!defined $obj_idx) {
|
||||||
|
@ -573,6 +594,7 @@ sub reset {
|
||||||
|
|
||||||
# Prevent toolpaths preview from rendering while we modify the Print object
|
# Prevent toolpaths preview from rendering while we modify the Print object
|
||||||
$self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D};
|
$self->{toolpaths2D}->enabled(0) if $self->{toolpaths2D};
|
||||||
|
$self->{preview3D}->enabled(0) if $self->{preview3D};
|
||||||
|
|
||||||
@{$self->{objects}} = ();
|
@{$self->{objects}} = ();
|
||||||
$self->{model}->clear_objects;
|
$self->{model}->clear_objects;
|
||||||
|
@ -585,17 +607,20 @@ sub reset {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub increase {
|
sub increase {
|
||||||
my $self = shift;
|
my ($self, $copies) = @_;
|
||||||
|
|
||||||
|
$copies //= 1;
|
||||||
my ($obj_idx, $object) = $self->selected_object;
|
my ($obj_idx, $object) = $self->selected_object;
|
||||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||||
my $last_instance = $model_object->instances->[-1];
|
my $instance = $model_object->instances->[-1];
|
||||||
my $i = $model_object->add_instance(
|
for my $i (1..$copies) {
|
||||||
offset => Slic3r::Pointf->new(map 10+$_, @{$last_instance->offset}),
|
$instance = $model_object->add_instance(
|
||||||
scaling_factor => $last_instance->scaling_factor,
|
offset => Slic3r::Pointf->new(map 10+$_, @{$instance->offset}),
|
||||||
rotation => $last_instance->rotation,
|
scaling_factor => $instance->scaling_factor,
|
||||||
|
rotation => $instance->rotation,
|
||||||
);
|
);
|
||||||
$self->{print}->objects->[$obj_idx]->add_copy($i->offset);
|
$self->{print}->objects->[$obj_idx]->add_copy($instance->offset);
|
||||||
|
}
|
||||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||||
|
|
||||||
# only autoarrange if user has autocentering enabled
|
# only autoarrange if user has autocentering enabled
|
||||||
|
@ -609,15 +634,18 @@ sub increase {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub decrease {
|
sub decrease {
|
||||||
my $self = shift;
|
my ($self, $copies) = @_;
|
||||||
|
|
||||||
|
$copies //= 1;
|
||||||
$self->stop_background_process;
|
$self->stop_background_process;
|
||||||
|
|
||||||
my ($obj_idx, $object) = $self->selected_object;
|
my ($obj_idx, $object) = $self->selected_object;
|
||||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||||
if ($model_object->instances_count >= 2) {
|
if ($model_object->instances_count > $copies) {
|
||||||
|
for my $i (1..$copies) {
|
||||||
$model_object->delete_last_instance;
|
$model_object->delete_last_instance;
|
||||||
$self->{print}->objects->[$obj_idx]->delete_last_copy;
|
$self->{print}->objects->[$obj_idx]->delete_last_copy;
|
||||||
|
}
|
||||||
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
$self->{list}->SetItem($obj_idx, 1, $model_object->instances_count);
|
||||||
} else {
|
} else {
|
||||||
$self->remove;
|
$self->remove;
|
||||||
|
@ -631,6 +659,28 @@ sub decrease {
|
||||||
$self->schedule_background_process;
|
$self->schedule_background_process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_number_of_copies {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->pause_background_process;
|
||||||
|
|
||||||
|
# get current number of copies
|
||||||
|
my ($obj_idx, $object) = $self->selected_object;
|
||||||
|
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||||
|
|
||||||
|
# prompt user
|
||||||
|
my $copies = Wx::GetNumberFromUser("", "Enter the number of copies of the selected object:", "Copies", $model_object->instances_count, 0, 1000, $self);
|
||||||
|
my $diff = $copies - $model_object->instances_count;
|
||||||
|
if ($diff == 0) {
|
||||||
|
# no variation
|
||||||
|
$self->resume_background_process;
|
||||||
|
} elsif ($diff > 0) {
|
||||||
|
$self->increase($diff);
|
||||||
|
} elsif ($diff < 0) {
|
||||||
|
$self->decrease(-$diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub rotate {
|
sub rotate {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my ($angle, $axis) = @_;
|
my ($angle, $axis) = @_;
|
||||||
|
@ -667,6 +717,9 @@ sub rotate {
|
||||||
$_->set_rotation(0) for @{ $model_object->instances };
|
$_->set_rotation(0) for @{ $model_object->instances };
|
||||||
}
|
}
|
||||||
$model_object->rotate(deg2rad($angle), $axis);
|
$model_object->rotate(deg2rad($angle), $axis);
|
||||||
|
|
||||||
|
# realign object to Z = 0
|
||||||
|
$model_object->center_around_origin;
|
||||||
$self->make_thumbnail($obj_idx);
|
$self->make_thumbnail($obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,6 +749,9 @@ sub flip {
|
||||||
|
|
||||||
$model_object->flip($axis);
|
$model_object->flip($axis);
|
||||||
$model_object->update_bounding_box;
|
$model_object->update_bounding_box;
|
||||||
|
|
||||||
|
# realign object to Z = 0
|
||||||
|
$model_object->center_around_origin;
|
||||||
$self->make_thumbnail($obj_idx);
|
$self->make_thumbnail($obj_idx);
|
||||||
|
|
||||||
# update print and start background processing
|
# update print and start background processing
|
||||||
|
@ -733,6 +789,7 @@ sub changescale {
|
||||||
my $versor = [1,1,1];
|
my $versor = [1,1,1];
|
||||||
$versor->[$axis] = $scale/100;
|
$versor->[$axis] = $scale/100;
|
||||||
$model_object->scale_xyz(Slic3r::Pointf3->new(@$versor));
|
$model_object->scale_xyz(Slic3r::Pointf3->new(@$versor));
|
||||||
|
# object was already aligned to Z = 0, so no need to realign it
|
||||||
$self->make_thumbnail($obj_idx);
|
$self->make_thumbnail($obj_idx);
|
||||||
} else {
|
} else {
|
||||||
# max scale factor should be above 2540 to allow importing files exported in inches
|
# max scale factor should be above 2540 to allow importing files exported in inches
|
||||||
|
@ -825,13 +882,16 @@ sub schedule_background_process {
|
||||||
|
|
||||||
if (defined $self->{apply_config_timer}) {
|
if (defined $self->{apply_config_timer}) {
|
||||||
$self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot
|
$self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot
|
||||||
$self->{toolpaths2D}->reload_print;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub async_apply_config {
|
sub async_apply_config {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
# reset preview canvases
|
||||||
|
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||||
|
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||||
|
|
||||||
# pause process thread before applying new config
|
# pause process thread before applying new config
|
||||||
# since we don't want to touch data that is being used by the threads
|
# since we don't want to touch data that is being used by the threads
|
||||||
$self->pause_background_process;
|
$self->pause_background_process;
|
||||||
|
@ -904,7 +964,8 @@ sub stop_background_process {
|
||||||
$self->statusbar->SetCancelCallback(undef);
|
$self->statusbar->SetCancelCallback(undef);
|
||||||
$self->statusbar->StopBusy;
|
$self->statusbar->StopBusy;
|
||||||
$self->statusbar->SetStatusText("");
|
$self->statusbar->SetStatusText("");
|
||||||
$self->{toolpaths2D}->reload_print;
|
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||||
|
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||||
|
|
||||||
if ($self->{process_thread}) {
|
if ($self->{process_thread}) {
|
||||||
Slic3r::debugf "Killing background process.\n";
|
Slic3r::debugf "Killing background process.\n";
|
||||||
|
@ -1028,7 +1089,8 @@ sub on_process_completed {
|
||||||
$self->{process_thread} = undef;
|
$self->{process_thread} = undef;
|
||||||
|
|
||||||
return if $error;
|
return if $error;
|
||||||
$self->{toolpaths2D}->reload_print;
|
$self->{toolpaths2D}->reload_print if $self->{toolpaths2D};
|
||||||
|
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||||
|
|
||||||
# if we have an export filename, start a new thread for exporting G-code
|
# if we have an export filename, start a new thread for exporting G-code
|
||||||
if ($self->{export_gcode_output_file}) {
|
if ($self->{export_gcode_output_file}) {
|
||||||
|
@ -1299,6 +1361,8 @@ sub on_config_change {
|
||||||
if ($opt_key eq 'bed_shape') {
|
if ($opt_key eq 'bed_shape') {
|
||||||
$self->{canvas}->update_bed_size;
|
$self->{canvas}->update_bed_size;
|
||||||
$self->{canvas3D}->update_bed_size if $self->{canvas3D};
|
$self->{canvas3D}->update_bed_size if $self->{canvas3D};
|
||||||
|
$self->{preview3D}->set_bed_shape($self->{config}->bed_shape)
|
||||||
|
if $self->{preview3D};
|
||||||
$self->update;
|
$self->update;
|
||||||
} elsif ($opt_key eq 'serial_port') {
|
} elsif ($opt_key eq 'serial_port') {
|
||||||
if ($config->get('serial_port')) {
|
if ($config->get('serial_port')) {
|
||||||
|
@ -1346,7 +1410,7 @@ sub list_item_activated {
|
||||||
my ($self, $event, $obj_idx) = @_;
|
my ($self, $event, $obj_idx) = @_;
|
||||||
|
|
||||||
$obj_idx //= $event->GetIndex;
|
$obj_idx //= $event->GetIndex;
|
||||||
$self->object_cut_dialog($obj_idx);
|
$self->object_settings_dialog($obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub object_cut_dialog {
|
sub object_cut_dialog {
|
||||||
|
@ -1518,6 +1582,7 @@ sub refresh_canvases {
|
||||||
|
|
||||||
$self->{canvas}->Refresh;
|
$self->{canvas}->Refresh;
|
||||||
$self->{canvas3D}->update if $self->{canvas3D};
|
$self->{canvas3D}->update if $self->{canvas3D};
|
||||||
|
$self->{preview3D}->reload_print if $self->{preview3D};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub validate_config {
|
sub validate_config {
|
||||||
|
@ -1549,6 +1614,9 @@ sub object_menu {
|
||||||
$frame->_append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub {
|
$frame->_append_menu_item($menu, "Decrease copies\tCtrl+-", 'Remove one copy of the selected object', sub {
|
||||||
$self->decrease;
|
$self->decrease;
|
||||||
});
|
});
|
||||||
|
$frame->_append_menu_item($menu, "Set number of copies…", 'Change the number of copies of the selected object', sub {
|
||||||
|
$self->set_number_of_copies;
|
||||||
|
});
|
||||||
$menu->AppendSeparator();
|
$menu->AppendSeparator();
|
||||||
$frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub {
|
$frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub {
|
||||||
$self->rotate(-45);
|
$self->rotate(-45);
|
||||||
|
|
|
@ -27,7 +27,7 @@ sub new {
|
||||||
$self->{on_select_object} = sub {};
|
$self->{on_select_object} = sub {};
|
||||||
$self->{on_double_click} = sub {};
|
$self->{on_double_click} = sub {};
|
||||||
$self->{on_right_click} = sub {};
|
$self->{on_right_click} = sub {};
|
||||||
$self->{on_instance_moved} = sub {};
|
$self->{on_instances_moved} = sub {};
|
||||||
|
|
||||||
$self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID);
|
$self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID);
|
||||||
$self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID);
|
$self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID);
|
||||||
|
@ -63,9 +63,9 @@ sub on_right_click {
|
||||||
$self->{on_right_click} = $cb;
|
$self->{on_right_click} = $cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_instance_moved {
|
sub on_instances_moved {
|
||||||
my ($self, $cb) = @_;
|
my ($self, $cb) = @_;
|
||||||
$self->{on_instance_moved} = $cb;
|
$self->{on_instances_moved} = $cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub repaint {
|
sub repaint {
|
||||||
|
@ -211,7 +211,7 @@ sub mouse_event {
|
||||||
}
|
}
|
||||||
$self->Refresh;
|
$self->Refresh;
|
||||||
} elsif ($event->LeftUp) {
|
} elsif ($event->LeftUp) {
|
||||||
$self->{on_instance_moved}->(@{ $self->{drag_object} })
|
$self->{on_instances_moved}->()
|
||||||
if $self->{drag_object};
|
if $self->{drag_object};
|
||||||
$self->{drag_start_pos} = undef;
|
$self->{drag_start_pos} = undef;
|
||||||
$self->{drag_object} = undef;
|
$self->{drag_object} = undef;
|
||||||
|
|
|
@ -8,7 +8,7 @@ use Slic3r::Geometry qw();
|
||||||
use Slic3r::Geometry::Clipper qw();
|
use Slic3r::Geometry::Clipper qw();
|
||||||
use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
|
use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL);
|
||||||
use Wx::Event qw();
|
use Wx::Event qw();
|
||||||
use base 'Slic3r::GUI::PreviewCanvas';
|
use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
|
@ -17,45 +17,42 @@ sub new {
|
||||||
my $self = $class->SUPER::new($parent);
|
my $self = $class->SUPER::new($parent);
|
||||||
$self->enable_picking(1);
|
$self->enable_picking(1);
|
||||||
$self->enable_moving(1);
|
$self->enable_moving(1);
|
||||||
|
$self->select_by('object');
|
||||||
|
$self->drag_by('instance');
|
||||||
|
|
||||||
$self->{objects} = $objects;
|
$self->{objects} = $objects;
|
||||||
$self->{model} = $model;
|
$self->{model} = $model;
|
||||||
$self->{config} = $config;
|
$self->{config} = $config;
|
||||||
$self->{on_select_object} = sub {};
|
$self->{on_select_object} = sub {};
|
||||||
$self->{on_instance_moved} = sub {};
|
$self->{on_instances_moved} = sub {};
|
||||||
|
|
||||||
$self->on_select(sub {
|
$self->on_select(sub {
|
||||||
my ($volume_idx) = @_;
|
my ($volume_idx) = @_;
|
||||||
|
|
||||||
my $obj_idx = undef;
|
my $obj_idx = undef;
|
||||||
if ($volume_idx != -1) {
|
if ($volume_idx != -1) {
|
||||||
$obj_idx = $self->{_volumes_inv}{$volume_idx};
|
$obj_idx = $self->object_idx($volume_idx);
|
||||||
$self->volumes->[$_]->selected(1) for @{$self->{_volumes}{$obj_idx}};
|
|
||||||
$self->Refresh;
|
|
||||||
}
|
}
|
||||||
$self->{on_select_object}->($obj_idx)
|
$self->{on_select_object}->($obj_idx)
|
||||||
if $self->{on_select_object};
|
if $self->{on_select_object};
|
||||||
});
|
});
|
||||||
$self->on_hover(sub {
|
|
||||||
my ($volume_idx) = @_;
|
|
||||||
|
|
||||||
my $obj_idx = $self->{_volumes_inv}{$volume_idx};
|
|
||||||
$self->volumes->[$_]->hover(1) for @{$self->{_volumes}{$obj_idx}};
|
|
||||||
});
|
|
||||||
$self->on_move(sub {
|
$self->on_move(sub {
|
||||||
my ($volume_idx) = @_;
|
my @volume_idxs = @_;
|
||||||
|
|
||||||
|
foreach my $volume_idx (@volume_idxs) {
|
||||||
my $volume = $self->volumes->[$volume_idx];
|
my $volume = $self->volumes->[$volume_idx];
|
||||||
my $obj_idx = $self->{_volumes_inv}{$volume_idx};
|
my $obj_idx = $self->object_idx($volume_idx);
|
||||||
|
my $instance_idx = $self->instance_idx($volume_idx);
|
||||||
my $model_object = $self->{model}->get_object($obj_idx);
|
my $model_object = $self->{model}->get_object($obj_idx);
|
||||||
$model_object
|
$model_object
|
||||||
->instances->[$volume->instance_idx]
|
->instances->[$instance_idx]
|
||||||
->offset
|
->offset
|
||||||
->translate($volume->origin->x, $volume->origin->y); #))
|
->translate($volume->origin->x, $volume->origin->y); #))
|
||||||
$model_object->invalidate_bounding_box;
|
$model_object->invalidate_bounding_box;
|
||||||
|
}
|
||||||
|
|
||||||
$self->{on_instance_moved}->($obj_idx, $volume->instance_idx)
|
$self->{on_instances_moved}->()
|
||||||
if $self->{on_instance_moved};
|
if $self->{on_instances_moved};
|
||||||
});
|
});
|
||||||
|
|
||||||
return $self;
|
return $self;
|
||||||
|
@ -76,27 +73,19 @@ sub set_on_right_click {
|
||||||
$self->on_right_click($cb);
|
$self->on_right_click($cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub set_on_instance_moved {
|
sub set_on_instances_moved {
|
||||||
my ($self, $cb) = @_;
|
my ($self, $cb) = @_;
|
||||||
$self->{on_instance_moved} = $cb;
|
$self->{on_instances_moved} = $cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub update {
|
sub update {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
$self->{_volumes} = {}; # obj_idx => [ volume_idx, volume_idx ]
|
|
||||||
$self->{_volumes_inv} = {}; # volume_idx => obj_idx
|
|
||||||
$self->reset_objects;
|
$self->reset_objects;
|
||||||
|
|
||||||
$self->update_bed_size;
|
$self->update_bed_size;
|
||||||
|
|
||||||
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
||||||
my $model_object = $self->{model}->get_object($obj_idx);
|
my @volume_idxs = $self->load_object($self->{model}, $obj_idx);
|
||||||
my @volume_idxs = $self->load_object($model_object, 1);
|
|
||||||
|
|
||||||
# store mapping between canvas volumes and model objects
|
|
||||||
$self->{_volumes}{$obj_idx} = [ @volume_idxs ];
|
|
||||||
$self->{_volumes_inv}{$_} = $obj_idx for @volume_idxs;
|
|
||||||
|
|
||||||
if ($self->{objects}[$obj_idx]->selected) {
|
if ($self->{objects}[$obj_idx]->selected) {
|
||||||
$self->select_volume($_) for @volume_idxs;
|
$self->select_volume($_) for @volume_idxs;
|
||||||
|
|
144
lib/Slic3r/GUI/Plater/3DPreview.pm
Normal file
144
lib/Slic3r/GUI/Plater/3DPreview.pm
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package Slic3r::GUI::Plater::3DPreview;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use Slic3r::Print::State ':steps';
|
||||||
|
use Wx qw(:misc :sizer :slider :statictext wxWHITE);
|
||||||
|
use Wx::Event qw(EVT_SLIDER EVT_KEY_DOWN);
|
||||||
|
use base qw(Wx::Panel Class::Accessor);
|
||||||
|
|
||||||
|
__PACKAGE__->mk_accessors(qw(print enabled _loaded canvas slider));
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my $class = shift;
|
||||||
|
my ($parent, $print) = @_;
|
||||||
|
|
||||||
|
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition);
|
||||||
|
|
||||||
|
# init GUI elements
|
||||||
|
my $canvas = Slic3r::GUI::3DScene->new($self);
|
||||||
|
$self->canvas($canvas);
|
||||||
|
my $slider = Wx::Slider->new(
|
||||||
|
$self, -1,
|
||||||
|
0, # default
|
||||||
|
0, # min
|
||||||
|
# we set max to a bogus non-zero value because the MSW implementation of wxSlider
|
||||||
|
# will skip drawing the slider if max <= min:
|
||||||
|
1, # max
|
||||||
|
wxDefaultPosition,
|
||||||
|
wxDefaultSize,
|
||||||
|
wxVERTICAL | wxSL_INVERSE,
|
||||||
|
);
|
||||||
|
$self->slider($slider);
|
||||||
|
|
||||||
|
my $z_label = $self->{z_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition,
|
||||||
|
[40,-1], wxALIGN_CENTRE_HORIZONTAL);
|
||||||
|
$z_label->SetFont($Slic3r::GUI::small_font);
|
||||||
|
|
||||||
|
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
|
$vsizer->Add($slider, 1, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
|
||||||
|
$vsizer->Add($z_label, 0, wxALL | wxEXPAND | wxALIGN_CENTER, 3);
|
||||||
|
|
||||||
|
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
|
$sizer->Add($canvas, 1, wxALL | wxEXPAND, 0);
|
||||||
|
$sizer->Add($vsizer, 0, wxTOP | wxBOTTOM | wxEXPAND, 5);
|
||||||
|
|
||||||
|
EVT_SLIDER($self, $slider, sub {
|
||||||
|
$self->set_z($self->{layers_z}[$slider->GetValue])
|
||||||
|
if $self->enabled;
|
||||||
|
});
|
||||||
|
EVT_KEY_DOWN($canvas, sub {
|
||||||
|
my ($s, $event) = @_;
|
||||||
|
|
||||||
|
my $key = $event->GetKeyCode;
|
||||||
|
if ($key == 85 || $key == 315) {
|
||||||
|
$slider->SetValue($slider->GetValue + 1);
|
||||||
|
$self->set_z($self->{layers_z}[$slider->GetValue]);
|
||||||
|
} elsif ($key == 68 || $key == 317) {
|
||||||
|
$slider->SetValue($slider->GetValue - 1);
|
||||||
|
$self->set_z($self->{layers_z}[$slider->GetValue]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$self->SetSizer($sizer);
|
||||||
|
$self->SetMinSize($self->GetSize);
|
||||||
|
$sizer->SetSizeHints($self);
|
||||||
|
|
||||||
|
# init canvas
|
||||||
|
$self->print($print);
|
||||||
|
$self->reload_print;
|
||||||
|
|
||||||
|
return $self;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub reload_print {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
$self->canvas->reset_objects;
|
||||||
|
$self->_loaded(0);
|
||||||
|
$self->load_print;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub load_print {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return if $self->_loaded;
|
||||||
|
|
||||||
|
# we require that there's at least one object and the posSlice step
|
||||||
|
# is performed on all of them (this ensures that _shifted_copies was
|
||||||
|
# populated and we know the number of layers)
|
||||||
|
if (!$self->print->object_step_done(STEP_SLICE)) {
|
||||||
|
$self->enabled(0);
|
||||||
|
$self->slider->Hide;
|
||||||
|
$self->canvas->Refresh; # clears canvas
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my %z = (); # z => 1
|
||||||
|
foreach my $object (@{$self->{print}->objects}) {
|
||||||
|
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
|
||||||
|
$z{$layer->print_z} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$self->enabled(1);
|
||||||
|
$self->{layers_z} = [ sort { $a <=> $b } keys %z ];
|
||||||
|
$self->slider->SetRange(0, scalar(@{$self->{layers_z}})-1);
|
||||||
|
if ((my $z_idx = $self->slider->GetValue) <= $#{$self->{layers_z}} && $self->slider->GetValue != 0) {
|
||||||
|
$self->set_z($self->{layers_z}[$z_idx]);
|
||||||
|
} else {
|
||||||
|
$self->slider->SetValue(scalar(@{$self->{layers_z}})-1);
|
||||||
|
$self->set_z($self->{layers_z}[-1]) if @{$self->{layers_z}};
|
||||||
|
}
|
||||||
|
$self->slider->Show;
|
||||||
|
$self->Layout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($self->IsShown) {
|
||||||
|
foreach my $object (@{$self->print->objects}) {
|
||||||
|
$self->canvas->load_print_object_toolpaths($object);
|
||||||
|
|
||||||
|
#my @volume_ids = $self->canvas->load_object($object->model_object);
|
||||||
|
#$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids;
|
||||||
|
}
|
||||||
|
$self->canvas->zoom_to_volumes;
|
||||||
|
$self->_loaded(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_z {
|
||||||
|
my ($self, $z) = @_;
|
||||||
|
|
||||||
|
return if !$self->enabled;
|
||||||
|
$self->{z_label}->SetLabel(sprintf '%.2f', $z);
|
||||||
|
$self->canvas->set_toolpaths_range(0, $z);
|
||||||
|
$self->canvas->Refresh if $self->IsShown;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub set_bed_shape {
|
||||||
|
my ($self, $bed_shape) = @_;
|
||||||
|
$self->canvas->set_bed_shape($bed_shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
|
@ -86,8 +86,9 @@ sub new {
|
||||||
# right pane with preview canvas
|
# right pane with preview canvas
|
||||||
my $canvas;
|
my $canvas;
|
||||||
if ($Slic3r::GUI::have_OpenGL) {
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
$canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self);
|
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||||
$canvas->load_object($self->{model_object});
|
$canvas->enable_cutting(1);
|
||||||
|
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||||
$canvas->set_auto_bed_shape;
|
$canvas->set_auto_bed_shape;
|
||||||
$canvas->SetSize([500,500]);
|
$canvas->SetSize([500,500]);
|
||||||
$canvas->SetMinSize($canvas->GetSize);
|
$canvas->SetMinSize($canvas->GetSize);
|
||||||
|
@ -153,6 +154,7 @@ sub perform_cut {
|
||||||
$self->{new_model} = $new_model;
|
$self->{new_model} = $new_model;
|
||||||
$self->{new_model_objects} = [];
|
$self->{new_model_objects} = [];
|
||||||
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
|
if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) {
|
||||||
|
$upper_object->center_around_origin; # align to Z = 0
|
||||||
push @{$self->{new_model_objects}}, $upper_object;
|
push @{$self->{new_model_objects}}, $upper_object;
|
||||||
}
|
}
|
||||||
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) {
|
||||||
|
|
|
@ -68,8 +68,18 @@ sub new {
|
||||||
# right pane with preview canvas
|
# right pane with preview canvas
|
||||||
my $canvas;
|
my $canvas;
|
||||||
if ($Slic3r::GUI::have_OpenGL) {
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
$canvas = $self->{canvas} = Slic3r::GUI::PreviewCanvas->new($self);
|
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||||
$canvas->load_object($self->{model_object});
|
$canvas->enable_picking(1);
|
||||||
|
$canvas->select_by('volume');
|
||||||
|
|
||||||
|
$canvas->on_select(sub {
|
||||||
|
my ($volume_idx) = @_;
|
||||||
|
|
||||||
|
# convert scene volume to model object volume
|
||||||
|
$self->reload_tree($canvas->volume_idx($volume_idx));
|
||||||
|
});
|
||||||
|
|
||||||
|
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||||
$canvas->set_auto_bed_shape;
|
$canvas->set_auto_bed_shape;
|
||||||
$canvas->SetSize([500,500]);
|
$canvas->SetSize([500,500]);
|
||||||
$canvas->zoom_to_volumes;
|
$canvas->zoom_to_volumes;
|
||||||
|
@ -101,20 +111,24 @@ sub new {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reload_tree {
|
sub reload_tree {
|
||||||
my ($self) = @_;
|
my ($self, $selected_volume_idx) = @_;
|
||||||
|
|
||||||
|
$selected_volume_idx //= -1;
|
||||||
my $object = $self->{model_object};
|
my $object = $self->{model_object};
|
||||||
my $tree = $self->{tree};
|
my $tree = $self->{tree};
|
||||||
my $rootId = $tree->GetRootItem;
|
my $rootId = $tree->GetRootItem;
|
||||||
|
|
||||||
$tree->DeleteChildren($rootId);
|
$tree->DeleteChildren($rootId);
|
||||||
|
|
||||||
my $itemId;
|
my $selectedId = $rootId;
|
||||||
foreach my $volume_id (0..$#{$object->volumes}) {
|
foreach my $volume_id (0..$#{$object->volumes}) {
|
||||||
my $volume = $object->volumes->[$volume_id];
|
my $volume = $object->volumes->[$volume_id];
|
||||||
|
|
||||||
my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
|
my $icon = $volume->modifier ? ICON_MODIFIERMESH : ICON_SOLIDMESH;
|
||||||
$itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon);
|
my $itemId = $tree->AppendItem($rootId, $volume->name || $volume_id, $icon);
|
||||||
|
if ($volume_id == $selected_volume_idx) {
|
||||||
|
$selectedId = $itemId;
|
||||||
|
}
|
||||||
$tree->SetPlData($itemId, {
|
$tree->SetPlData($itemId, {
|
||||||
type => 'volume',
|
type => 'volume',
|
||||||
volume_id => $volume_id,
|
volume_id => $volume_id,
|
||||||
|
@ -122,10 +136,9 @@ sub reload_tree {
|
||||||
}
|
}
|
||||||
$tree->ExpandAll;
|
$tree->ExpandAll;
|
||||||
|
|
||||||
# select last appended part
|
|
||||||
# This will trigger the selection_changed() event
|
# This will trigger the selection_changed() event
|
||||||
Slic3r::GUI->CallAfter(sub {
|
Slic3r::GUI->CallAfter(sub {
|
||||||
$self->{tree}->SelectItem($itemId);
|
$self->{tree}->SelectItem($selectedId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +157,7 @@ sub selection_changed {
|
||||||
|
|
||||||
# deselect all meshes
|
# deselect all meshes
|
||||||
if ($self->{canvas}) {
|
if ($self->{canvas}) {
|
||||||
$_->{selected} = 0 for @{$self->{canvas}->volumes};
|
$_->selected(0) for @{$self->{canvas}->volumes};
|
||||||
}
|
}
|
||||||
|
|
||||||
# disable things as if nothing is selected
|
# disable things as if nothing is selected
|
||||||
|
@ -169,10 +182,7 @@ sub selection_changed {
|
||||||
# get default values
|
# get default values
|
||||||
@opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys};
|
@opt_keys = @{Slic3r::Config::PrintRegion->new->get_keys};
|
||||||
} elsif ($itemData->{type} eq 'object') {
|
} elsif ($itemData->{type} eq 'object') {
|
||||||
# select all object volumes in 3D preview
|
# select nothing in 3D preview
|
||||||
if ($self->{canvas}) {
|
|
||||||
$_->{selected} = 1 for @{$self->{canvas}->volumes};
|
|
||||||
}
|
|
||||||
|
|
||||||
# attach object config to settings panel
|
# attach object config to settings panel
|
||||||
$self->{staticbox}->SetLabel('Object Settings');
|
$self->{staticbox}->SetLabel('Object Settings');
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
package Slic3r::GUI::Plater::ObjectPreviewDialog;
|
|
||||||
use strict;
|
|
||||||
use warnings;
|
|
||||||
use utf8;
|
|
||||||
|
|
||||||
use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL);
|
|
||||||
use Wx::Event qw(EVT_CLOSE);
|
|
||||||
use base 'Wx::Dialog';
|
|
||||||
|
|
||||||
sub new {
|
|
||||||
my $class = shift;
|
|
||||||
my ($parent, %params) = @_;
|
|
||||||
my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
|
|
||||||
$self->{model_object} = $params{model_object};
|
|
||||||
|
|
||||||
my $canvas = Slic3r::GUI::PreviewCanvas->new($self);
|
|
||||||
$canvas->load_object($self->{model_object});
|
|
||||||
$canvas->set_bounding_box($self->{model_object}->bounding_box);
|
|
||||||
$canvas->set_auto_bed_shape;
|
|
||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
|
||||||
$sizer->Add($canvas, 1, wxEXPAND, 0);
|
|
||||||
$self->SetSizer($sizer);
|
|
||||||
$self->SetMinSize($self->GetSize);
|
|
||||||
|
|
||||||
# needed to actually free memory
|
|
||||||
EVT_CLOSE($self, sub {
|
|
||||||
$self->EndModal(wxID_OK);
|
|
||||||
$self->Destroy;
|
|
||||||
});
|
|
||||||
|
|
||||||
return $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
1;
|
|
|
@ -22,6 +22,7 @@ sub new {
|
||||||
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
|
$self->{vsizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$self->SetSizer($self->{vsizer});
|
$self->SetSizer($self->{vsizer});
|
||||||
$self->build;
|
$self->build;
|
||||||
|
$self->_update;
|
||||||
|
|
||||||
{
|
{
|
||||||
my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize);
|
my $label = Wx::StaticText->new($self, -1, "Want more options? Switch to the Expert Mode.", wxDefaultPosition, wxDefaultSize);
|
||||||
|
@ -71,12 +72,14 @@ sub load_config {
|
||||||
$self->{config}->set($opt_key, $config->get($opt_key));
|
$self->{config}->set($opt_key, $config->get($opt_key));
|
||||||
}
|
}
|
||||||
$_->reload_config for @{$self->{optgroups}};
|
$_->reload_config for @{$self->{optgroups}};
|
||||||
|
$self->_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub load_presets {}
|
sub load_presets {}
|
||||||
|
|
||||||
sub is_dirty { 0 }
|
sub is_dirty { 0 }
|
||||||
sub config { $_[0]->{config}->clone }
|
sub config { $_[0]->{config}->clone }
|
||||||
|
sub _update {}
|
||||||
|
|
||||||
sub on_value_change {
|
sub on_value_change {
|
||||||
my ($self, $cb) = @_;
|
my ($self, $cb) = @_;
|
||||||
|
@ -88,7 +91,19 @@ sub on_presets_changed {}
|
||||||
# propagate event to the parent
|
# propagate event to the parent
|
||||||
sub _on_value_change {
|
sub _on_value_change {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->{on_value_change}->(@_) if $self->{on_value_change};
|
$self->{on_value_change}->(@_) if $self->{on_value_change};
|
||||||
|
$self->_update;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_field {
|
||||||
|
my ($self, $opt_key, $opt_index) = @_;
|
||||||
|
|
||||||
|
foreach my $optgroup (@{ $self->{optgroups} }) {
|
||||||
|
my $field = $optgroup->get_fieldc($opt_key, $opt_index);
|
||||||
|
return $field if defined $field;
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab::Print;
|
package Slic3r::GUI::SimpleTab::Print;
|
||||||
|
@ -104,10 +119,12 @@ sub build {
|
||||||
|
|
||||||
$self->init_config_options(qw(
|
$self->init_config_options(qw(
|
||||||
layer_height perimeters top_solid_layers bottom_solid_layers
|
layer_height perimeters top_solid_layers bottom_solid_layers
|
||||||
fill_density fill_pattern support_material support_material_spacing raft_layers
|
fill_density fill_pattern external_fill_pattern
|
||||||
|
support_material support_material_spacing raft_layers
|
||||||
|
support_material_contact_distance dont_support_bridges
|
||||||
perimeter_speed infill_speed travel_speed
|
perimeter_speed infill_speed travel_speed
|
||||||
brim_width
|
brim_width
|
||||||
complete_objects extruder_clearance_radius extruder_clearance_height
|
xy_size_compensation
|
||||||
));
|
));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -127,12 +144,15 @@ sub build {
|
||||||
my $optgroup = $self->new_optgroup('Infill');
|
my $optgroup = $self->new_optgroup('Infill');
|
||||||
$optgroup->append_single_option_line('fill_density');
|
$optgroup->append_single_option_line('fill_density');
|
||||||
$optgroup->append_single_option_line('fill_pattern');
|
$optgroup->append_single_option_line('fill_pattern');
|
||||||
|
$optgroup->append_single_option_line('external_fill_pattern');
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my $optgroup = $self->new_optgroup('Support material');
|
my $optgroup = $self->new_optgroup('Support material');
|
||||||
$optgroup->append_single_option_line('support_material');
|
$optgroup->append_single_option_line('support_material');
|
||||||
$optgroup->append_single_option_line('support_material_spacing');
|
$optgroup->append_single_option_line('support_material_spacing');
|
||||||
|
$optgroup->append_single_option_line('support_material_contact_distance');
|
||||||
|
$optgroup->append_single_option_line('dont_support_bridges');
|
||||||
$optgroup->append_single_option_line('raft_layers');
|
$optgroup->append_single_option_line('raft_layers');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,18 +169,35 @@ sub build {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my $optgroup = $self->new_optgroup('Sequential printing');
|
my $optgroup = $self->new_optgroup('Other');
|
||||||
$optgroup->append_single_option_line('complete_objects');
|
$optgroup->append_single_option_line('xy_size_compensation');
|
||||||
|
|
||||||
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
|
||||||
label => 'Extruder clearance (mm)',
|
|
||||||
);
|
|
||||||
$line->append_option($optgroup->get_option('extruder_clearance_radius'));
|
|
||||||
$line->append_option($optgroup->get_option('extruder_clearance_height'));
|
|
||||||
$optgroup->append_line($line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _update {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my $config = $self->{config};
|
||||||
|
|
||||||
|
my $have_perimeters = $config->perimeters > 0;
|
||||||
|
$self->get_field($_)->toggle($have_perimeters)
|
||||||
|
for qw(perimeter_speed);
|
||||||
|
|
||||||
|
my $have_infill = $config->fill_density > 0;
|
||||||
|
my $have_solid_infill = $config->top_solid_layers > 0 || $config->bottom_solid_layers > 0;
|
||||||
|
$self->get_field($_)->toggle($have_infill)
|
||||||
|
for qw(fill_pattern);
|
||||||
|
$self->get_field($_)->toggle($have_solid_infill)
|
||||||
|
for qw(external_fill_pattern);
|
||||||
|
$self->get_field($_)->toggle($have_infill || $have_solid_infill)
|
||||||
|
for qw(infill_speed);
|
||||||
|
|
||||||
|
my $have_support_material = $config->support_material || $config->raft_layers > 0;
|
||||||
|
$self->get_field($_)->toggle($have_support_material)
|
||||||
|
for qw(support_material_spacing dont_support_bridges
|
||||||
|
support_material_contact_distance);
|
||||||
|
}
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab::Filament;
|
package Slic3r::GUI::SimpleTab::Filament;
|
||||||
use base 'Slic3r::GUI::SimpleTab';
|
use base 'Slic3r::GUI::SimpleTab';
|
||||||
|
|
||||||
|
@ -206,6 +243,8 @@ sub build {
|
||||||
|
|
||||||
package Slic3r::GUI::SimpleTab::Printer;
|
package Slic3r::GUI::SimpleTab::Printer;
|
||||||
use base 'Slic3r::GUI::SimpleTab';
|
use base 'Slic3r::GUI::SimpleTab';
|
||||||
|
use Wx qw(:sizer :button :bitmap :misc :id);
|
||||||
|
use Wx::Event qw(EVT_BUTTON);
|
||||||
|
|
||||||
sub name { 'printer' }
|
sub name { 'printer' }
|
||||||
sub title { 'Printer Settings' }
|
sub title { 'Printer Settings' }
|
||||||
|
@ -214,17 +253,46 @@ sub build {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
$self->init_config_options(qw(
|
$self->init_config_options(qw(
|
||||||
|
bed_shape
|
||||||
z_offset
|
z_offset
|
||||||
gcode_flavor
|
gcode_flavor
|
||||||
nozzle_diameter
|
nozzle_diameter
|
||||||
retract_length retract_lift
|
retract_length retract_lift wipe
|
||||||
start_gcode
|
start_gcode
|
||||||
end_gcode
|
end_gcode
|
||||||
));
|
));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
my $bed_shape_widget = sub {
|
||||||
|
my ($parent) = @_;
|
||||||
|
|
||||||
|
my $btn = Wx::Button->new($parent, -1, "Set…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT);
|
||||||
|
$btn->SetFont($Slic3r::GUI::small_font);
|
||||||
|
if ($Slic3r::GUI::have_button_icons) {
|
||||||
|
$btn->SetBitmap(Wx::Bitmap->new("$Slic3r::var/cog.png", wxBITMAP_TYPE_PNG));
|
||||||
|
}
|
||||||
|
|
||||||
|
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||||
|
$sizer->Add($btn);
|
||||||
|
|
||||||
|
EVT_BUTTON($self, $btn, sub {
|
||||||
|
my $dlg = Slic3r::GUI::BedShapeDialog->new($self, $self->{config}->bed_shape);
|
||||||
|
if ($dlg->ShowModal == wxID_OK) {
|
||||||
|
my $value = $dlg->GetValue;
|
||||||
|
$self->{config}->set('bed_shape', $value);
|
||||||
|
$self->_on_value_change('bed_shape', $value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $sizer;
|
||||||
|
};
|
||||||
|
|
||||||
my $optgroup = $self->new_optgroup('Size and coordinates');
|
my $optgroup = $self->new_optgroup('Size and coordinates');
|
||||||
# TODO: add bed_shape
|
my $line = Slic3r::GUI::OptionsGroup::Line->new(
|
||||||
|
label => 'Bed shape',
|
||||||
|
widget => $bed_shape_widget,
|
||||||
|
);
|
||||||
|
$optgroup->append_line($line);
|
||||||
$optgroup->append_single_option_line('z_offset');
|
$optgroup->append_single_option_line('z_offset');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,6 +310,7 @@ sub build {
|
||||||
my $optgroup = $self->new_optgroup('Retraction');
|
my $optgroup = $self->new_optgroup('Retraction');
|
||||||
$optgroup->append_single_option_line('retract_length', 0);
|
$optgroup->append_single_option_line('retract_length', 0);
|
||||||
$optgroup->append_single_option_line('retract_lift', 0);
|
$optgroup->append_single_option_line('retract_lift', 0);
|
||||||
|
$optgroup->append_single_option_line('wipe', 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -265,4 +334,14 @@ sub build {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _update {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
my $config = $self->{config};
|
||||||
|
|
||||||
|
my $have_retraction = $config->retract_length->[0] > 0;
|
||||||
|
$self->get_field($_, 0)->toggle($have_retraction)
|
||||||
|
for qw(retract_lift wipe);
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -292,16 +292,24 @@ sub reload_config {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub update_tree {
|
sub update_tree {
|
||||||
my $self = shift;
|
my ($self) = @_;
|
||||||
my ($select) = @_;
|
|
||||||
|
|
||||||
$select //= 0; #/
|
# get label of the currently selected item
|
||||||
|
my $selected = $self->{treectrl}->GetItemText($self->{treectrl}->GetSelection);
|
||||||
|
|
||||||
my $rootItem = $self->{treectrl}->GetRootItem;
|
my $rootItem = $self->{treectrl}->GetRootItem;
|
||||||
$self->{treectrl}->DeleteChildren($rootItem);
|
$self->{treectrl}->DeleteChildren($rootItem);
|
||||||
|
my $have_selection = 0;
|
||||||
foreach my $page (@{$self->{pages}}) {
|
foreach my $page (@{$self->{pages}}) {
|
||||||
my $itemId = $self->{treectrl}->AppendItem($rootItem, $page->{title}, $page->{iconID});
|
my $itemId = $self->{treectrl}->AppendItem($rootItem, $page->{title}, $page->{iconID});
|
||||||
$self->{treectrl}->SelectItem($itemId) if $self->{treectrl}->GetChildrenCount($rootItem) == $select + 1;
|
if ($page->{title} eq $selected) {
|
||||||
|
$self->{treectrl}->SelectItem($itemId);
|
||||||
|
$have_selection = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$have_selection) {
|
||||||
|
$self->{treectrl}->SelectItem($self->{treectrl}->GetFirstChild($rootItem));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,6 +399,7 @@ sub load_config {
|
||||||
$self->update_dirty;
|
$self->update_dirty;
|
||||||
}
|
}
|
||||||
$self->reload_config;
|
$self->reload_config;
|
||||||
|
$self->_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_preset_config {
|
sub get_preset_config {
|
||||||
|
@ -453,7 +462,7 @@ sub build {
|
||||||
raft_layers
|
raft_layers
|
||||||
support_material_pattern support_material_spacing support_material_angle
|
support_material_pattern support_material_spacing support_material_angle
|
||||||
support_material_interface_layers support_material_interface_spacing
|
support_material_interface_layers support_material_interface_spacing
|
||||||
dont_support_bridges
|
support_material_contact_distance dont_support_bridges
|
||||||
notes
|
notes
|
||||||
complete_objects extruder_clearance_radius extruder_clearance_height
|
complete_objects extruder_clearance_radius extruder_clearance_height
|
||||||
gcode_comments output_filename_format
|
gcode_comments output_filename_format
|
||||||
|
@ -465,7 +474,7 @@ sub build {
|
||||||
extrusion_width first_layer_extrusion_width perimeter_extrusion_width
|
extrusion_width first_layer_extrusion_width perimeter_extrusion_width
|
||||||
external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width
|
external_perimeter_extrusion_width infill_extrusion_width solid_infill_extrusion_width
|
||||||
top_infill_extrusion_width support_material_extrusion_width
|
top_infill_extrusion_width support_material_extrusion_width
|
||||||
bridge_flow_ratio
|
infill_overlap bridge_flow_ratio
|
||||||
xy_size_compensation threads resolution
|
xy_size_compensation threads resolution
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -556,6 +565,7 @@ sub build {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
my $optgroup = $page->new_optgroup('Options for support material and raft');
|
my $optgroup = $page->new_optgroup('Options for support material and raft');
|
||||||
|
$optgroup->append_single_option_line('support_material_contact_distance');
|
||||||
$optgroup->append_single_option_line('support_material_pattern');
|
$optgroup->append_single_option_line('support_material_pattern');
|
||||||
$optgroup->append_single_option_line('support_material_spacing');
|
$optgroup->append_single_option_line('support_material_spacing');
|
||||||
$optgroup->append_single_option_line('support_material_angle');
|
$optgroup->append_single_option_line('support_material_angle');
|
||||||
|
@ -634,6 +644,10 @@ sub build {
|
||||||
$optgroup->append_single_option_line('top_infill_extrusion_width');
|
$optgroup->append_single_option_line('top_infill_extrusion_width');
|
||||||
$optgroup->append_single_option_line('support_material_extrusion_width');
|
$optgroup->append_single_option_line('support_material_extrusion_width');
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
my $optgroup = $page->new_optgroup('Overlap');
|
||||||
|
$optgroup->append_single_option_line('infill_overlap');
|
||||||
|
}
|
||||||
{
|
{
|
||||||
my $optgroup = $page->new_optgroup('Flow');
|
my $optgroup = $page->new_optgroup('Flow');
|
||||||
$optgroup->append_single_option_line('bridge_flow_ratio');
|
$optgroup->append_single_option_line('bridge_flow_ratio');
|
||||||
|
@ -702,13 +716,20 @@ sub _update {
|
||||||
my $config = $self->{config};
|
my $config = $self->{config};
|
||||||
|
|
||||||
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0)) {
|
if ($config->spiral_vase && !($config->perimeters == 1 && $config->top_solid_layers == 0 && $config->fill_density == 0)) {
|
||||||
my $dialog = Wx::MessageDialog->new($self, "The Spiral Vase mode requires one perimeter, no top solid layers and 0% fill density. Shall I adjust those settings in order to enable Spiral Vase?",
|
my $dialog = Wx::MessageDialog->new($self,
|
||||||
|
"The Spiral Vase mode requires:\n"
|
||||||
|
. "- one perimeter\n"
|
||||||
|
. "- no top solid layers\n"
|
||||||
|
. "- 0% fill density\n"
|
||||||
|
. "- no support material\n"
|
||||||
|
. "\nShall I adjust those settings in order to enable Spiral Vase?",
|
||||||
'Spiral Vase', wxICON_WARNING | wxYES | wxNO);
|
'Spiral Vase', wxICON_WARNING | wxYES | wxNO);
|
||||||
if ($dialog->ShowModal() == wxID_YES) {
|
if ($dialog->ShowModal() == wxID_YES) {
|
||||||
my $new_conf = Slic3r::Config->new;
|
my $new_conf = Slic3r::Config->new;
|
||||||
$new_conf->set("perimeters", 1);
|
$new_conf->set("perimeters", 1);
|
||||||
$new_conf->set("top_solid_layers", 0);
|
$new_conf->set("top_solid_layers", 0);
|
||||||
$new_conf->set("fill_density", 0);
|
$new_conf->set("fill_density", 0);
|
||||||
|
$new_conf->set("support_material", 0);
|
||||||
$self->load_config($new_conf);
|
$self->load_config($new_conf);
|
||||||
} else {
|
} else {
|
||||||
my $new_conf = Slic3r::Config->new;
|
my $new_conf = Slic3r::Config->new;
|
||||||
|
@ -759,7 +780,7 @@ sub _update {
|
||||||
for qw(support_material_threshold support_material_enforce_layers
|
for qw(support_material_threshold support_material_enforce_layers
|
||||||
support_material_pattern support_material_spacing support_material_angle
|
support_material_pattern support_material_spacing support_material_angle
|
||||||
support_material_interface_layers dont_support_bridges
|
support_material_interface_layers dont_support_bridges
|
||||||
support_material_extrusion_width);
|
support_material_extrusion_width support_material_contact_distance);
|
||||||
$self->get_field($_)->toggle($have_support_material && $have_support_interface)
|
$self->get_field($_)->toggle($have_support_material && $have_support_interface)
|
||||||
for qw(support_material_interface_spacing support_material_interface_extruder
|
for qw(support_material_interface_spacing support_material_interface_extruder
|
||||||
support_material_interface_speed);
|
support_material_interface_speed);
|
||||||
|
@ -929,7 +950,7 @@ sub build {
|
||||||
octoprint_host octoprint_apikey
|
octoprint_host octoprint_apikey
|
||||||
use_firmware_retraction pressure_advance vibration_limit
|
use_firmware_retraction pressure_advance vibration_limit
|
||||||
use_volumetric_e
|
use_volumetric_e
|
||||||
start_gcode end_gcode layer_gcode toolchange_gcode
|
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode
|
||||||
nozzle_diameter extruder_offset
|
nozzle_diameter extruder_offset
|
||||||
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe
|
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe
|
||||||
retract_length_toolchange retract_restart_extra_toolchange
|
retract_length_toolchange retract_restart_extra_toolchange
|
||||||
|
@ -1037,7 +1058,7 @@ sub build {
|
||||||
EVT_BUTTON($self, $btn, sub {
|
EVT_BUTTON($self, $btn, sub {
|
||||||
my $dlg = Slic3r::GUI::BonjourBrowser->new($self);
|
my $dlg = Slic3r::GUI::BonjourBrowser->new($self);
|
||||||
if ($dlg->ShowModal == wxID_OK) {
|
if ($dlg->ShowModal == wxID_OK) {
|
||||||
my $value = $dlg->GetValue;
|
my $value = $dlg->GetValue . ":" . $dlg->GetPort;
|
||||||
$self->{config}->set('octoprint_host', $value);
|
$self->{config}->set('octoprint_host', $value);
|
||||||
$self->update_dirty;
|
$self->update_dirty;
|
||||||
$self->_on_value_change('octoprint_host', $value);
|
$self->_on_value_change('octoprint_host', $value);
|
||||||
|
@ -1060,7 +1081,7 @@ sub build {
|
||||||
my $ua = LWP::UserAgent->new;
|
my $ua = LWP::UserAgent->new;
|
||||||
$ua->timeout(10);
|
$ua->timeout(10);
|
||||||
|
|
||||||
my $res = $ua->post(
|
my $res = $ua->get(
|
||||||
"http://" . $self->{config}->octoprint_host . "/api/version",
|
"http://" . $self->{config}->octoprint_host . "/api/version",
|
||||||
'X-Api-Key' => $self->{config}->octoprint_apikey,
|
'X-Api-Key' => $self->{config}->octoprint_apikey,
|
||||||
);
|
);
|
||||||
|
@ -1115,7 +1136,16 @@ sub build {
|
||||||
$optgroup->append_single_option_line($option);
|
$optgroup->append_single_option_line($option);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
my $optgroup = $page->new_optgroup('Layer change G-code',
|
my $optgroup = $page->new_optgroup('Before layer change G-code',
|
||||||
|
label_width => 0,
|
||||||
|
);
|
||||||
|
my $option = $optgroup->get_option('before_layer_gcode');
|
||||||
|
$option->full_width(1);
|
||||||
|
$option->height(150);
|
||||||
|
$optgroup->append_single_option_line($option);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
my $optgroup = $page->new_optgroup('After layer change G-code',
|
||||||
label_width => 0,
|
label_width => 0,
|
||||||
);
|
);
|
||||||
my $option = $optgroup->get_option('layer_gcode');
|
my $option = $optgroup->get_option('layer_gcode');
|
||||||
|
@ -1203,6 +1233,7 @@ sub _build_extruder_pages {
|
||||||
|
|
||||||
# remove extra pages
|
# remove extra pages
|
||||||
if ($self->{extruders_count} <= $#{$self->{extruder_pages}}) {
|
if ($self->{extruders_count} <= $#{$self->{extruder_pages}}) {
|
||||||
|
$_->Destroy for @{$self->{extruder_pages}}[$self->{extruders_count}..$#{$self->{extruder_pages}}];
|
||||||
splice @{$self->{extruder_pages}}, $self->{extruders_count};
|
splice @{$self->{extruder_pages}}, $self->{extruders_count};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1219,7 +1250,7 @@ sub _build_extruder_pages {
|
||||||
(grep $_->{title} !~ /^Extruder \d+/, @{$self->{pages}}),
|
(grep $_->{title} !~ /^Extruder \d+/, @{$self->{pages}}),
|
||||||
@{$self->{extruder_pages}}[ 0 .. $self->{extruders_count}-1 ],
|
@{$self->{extruder_pages}}[ 0 .. $self->{extruders_count}-1 ],
|
||||||
);
|
);
|
||||||
$self->update_tree(0);
|
$self->update_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _update {
|
sub _update {
|
||||||
|
@ -1420,8 +1451,8 @@ sub config {
|
||||||
return Slic3r::Config->new_from_defaults(@$keys);
|
return Slic3r::Config->new_from_defaults(@$keys);
|
||||||
} else {
|
} else {
|
||||||
if (!-e $self->file) {
|
if (!-e $self->file) {
|
||||||
Slic3r::GUI::show_error($self, "The selected preset does not exist anymore (" . $self->file . ").");
|
Slic3r::GUI::show_error(undef, "The selected preset does not exist anymore (" . $self->file . ").");
|
||||||
return;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
# apply preset values on top of defaults
|
# apply preset values on top of defaults
|
||||||
|
|
|
@ -5,7 +5,7 @@ use Slic3r::ExtrusionLoop ':roles';
|
||||||
use Slic3r::ExtrusionPath ':roles';
|
use Slic3r::ExtrusionPath ':roles';
|
||||||
use Slic3r::Geometry qw(scale unscale chained_path);
|
use Slic3r::Geometry qw(scale unscale chained_path);
|
||||||
use Slic3r::Geometry::Clipper qw(union_ex diff diff_ex intersection_ex offset offset2
|
use Slic3r::Geometry::Clipper qw(union_ex diff diff_ex intersection_ex offset offset2
|
||||||
offset_ex offset2_ex union_pt intersection_ppl diff_ppl);
|
offset_ex offset2_ex intersection_ppl diff_ppl);
|
||||||
use Slic3r::Surface ':types';
|
use Slic3r::Surface ':types';
|
||||||
|
|
||||||
has 'slices' => (is => 'ro', required => 1); # SurfaceCollection
|
has 'slices' => (is => 'ro', required => 1); # SurfaceCollection
|
||||||
|
@ -17,6 +17,7 @@ has 'ext_perimeter_flow' => (is => 'ro', required => 1);
|
||||||
has 'overhang_flow' => (is => 'ro', required => 1);
|
has 'overhang_flow' => (is => 'ro', required => 1);
|
||||||
has 'solid_infill_flow' => (is => 'ro', required => 1);
|
has 'solid_infill_flow' => (is => 'ro', required => 1);
|
||||||
has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new });
|
has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new });
|
||||||
|
has 'object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new });
|
||||||
has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
|
has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new });
|
||||||
has '_lower_slices_p' => (is => 'rw', default => sub { [] });
|
has '_lower_slices_p' => (is => 'rw', default => sub { [] });
|
||||||
has '_holes_pt' => (is => 'rw');
|
has '_holes_pt' => (is => 'rw');
|
||||||
|
@ -90,20 +91,23 @@ sub process {
|
||||||
# we need to process each island separately because we might have different
|
# we need to process each island separately because we might have different
|
||||||
# extra perimeters for each one
|
# extra perimeters for each one
|
||||||
foreach my $surface (@{$self->slices}) {
|
foreach my $surface (@{$self->slices}) {
|
||||||
my @contours = (); # array of Polygons with ccw orientation
|
|
||||||
my @holes = (); # array of Polygons with cw orientation
|
|
||||||
my @thin_walls = (); # array of ExPolygons
|
|
||||||
|
|
||||||
# detect how many perimeters must be generated for this island
|
# detect how many perimeters must be generated for this island
|
||||||
my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0);
|
my $loop_number = $self->config->perimeters + ($surface->extra_perimeters || 0);
|
||||||
|
$loop_number--; # 0-indexed loops
|
||||||
|
|
||||||
|
my @gaps = (); # ExPolygons
|
||||||
|
|
||||||
|
my @last = @{$surface->expolygon->simplify_p(&Slic3r::SCALED_RESOLUTION)};
|
||||||
|
if ($loop_number >= 0) { # no loops = -1
|
||||||
|
|
||||||
|
my @contours = (); # depth => [ Polygon, Polygon ... ]
|
||||||
|
my @holes = (); # depth => [ Polygon, Polygon ... ]
|
||||||
|
my @thin_walls = (); # Polylines
|
||||||
|
|
||||||
my @last = @{$surface->expolygon};
|
|
||||||
my @gaps = (); # array of ExPolygons
|
|
||||||
if ($loop_number > 0) {
|
|
||||||
# we loop one time more than needed in order to find gaps after the last perimeter was applied
|
# we loop one time more than needed in order to find gaps after the last perimeter was applied
|
||||||
for my $i (1 .. ($loop_number+1)) { # outer loop is 1
|
for my $i (0..($loop_number+1)) { # outer loop is 0
|
||||||
my @offsets = ();
|
my @offsets = ();
|
||||||
if ($i == 1) {
|
if ($i == 0) {
|
||||||
# the minimum thickness of a single loop is:
|
# the minimum thickness of a single loop is:
|
||||||
# ext_width/2 + ext_spacing/2 + spacing/2 + width/2
|
# ext_width/2 + ext_spacing/2 + spacing/2 + width/2
|
||||||
if ($self->config->thin_walls) {
|
if ($self->config->thin_walls) {
|
||||||
|
@ -121,15 +125,35 @@ sub process {
|
||||||
|
|
||||||
# look for thin walls
|
# look for thin walls
|
||||||
if ($self->config->thin_walls) {
|
if ($self->config->thin_walls) {
|
||||||
my $diff = diff_ex(
|
my $diff = diff(
|
||||||
\@last,
|
\@last,
|
||||||
offset(\@offsets, +0.5*$ext_pwidth),
|
offset(\@offsets, +0.5*$ext_pwidth),
|
||||||
1, # medial axis requires non-overlapping geometry
|
1, # medial axis requires non-overlapping geometry
|
||||||
);
|
);
|
||||||
push @thin_walls, @$diff;
|
|
||||||
|
# the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||||
|
# (actually, something larger than that still may exist due to mitering or other causes)
|
||||||
|
my $min_width = $pwidth / 4;
|
||||||
|
@thin_walls = @{offset2_ex($diff, -$min_width/2, +$min_width/2)};
|
||||||
|
|
||||||
|
# the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
||||||
|
@thin_walls = grep $_->length > $pwidth*2,
|
||||||
|
map @{$_->medial_axis($pwidth + $pspacing, $min_width)}, @thin_walls;
|
||||||
|
Slic3r::debugf " %d thin walls detected\n", scalar(@thin_walls) if $Slic3r::debug;
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
require "Slic3r/SVG.pm";
|
||||||
|
Slic3r::SVG::output(
|
||||||
|
"medial_axis.svg",
|
||||||
|
no_arrows => 1,
|
||||||
|
expolygons => union_ex($diff),
|
||||||
|
green_polylines => [ map $_->polygon->split_at_first_point, @{$self->perimeters} ],
|
||||||
|
red_polylines => $self->_thin_wall_polylines,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
my $distance = ($i == 2) ? $ext_pspacing : $pspacing;
|
my $distance = ($i == 1) ? $ext_pspacing : $pspacing;
|
||||||
|
|
||||||
if ($self->config->thin_walls) {
|
if ($self->config->thin_walls) {
|
||||||
@offsets = @{offset2(
|
@offsets = @{offset2(
|
||||||
|
@ -160,16 +184,92 @@ sub process {
|
||||||
last if !@offsets;
|
last if !@offsets;
|
||||||
last if $i > $loop_number; # we were only looking for gaps this time
|
last if $i > $loop_number; # we were only looking for gaps this time
|
||||||
|
|
||||||
# clone polygons because these ExPolygons will go out of scope very soon
|
|
||||||
@last = @offsets;
|
@last = @offsets;
|
||||||
|
|
||||||
|
$contours[$i] = [];
|
||||||
|
$holes[$i] = [];
|
||||||
foreach my $polygon (@offsets) {
|
foreach my $polygon (@offsets) {
|
||||||
if ($polygon->is_counter_clockwise) {
|
my $loop = Slic3r::Layer::PerimeterGenerator::Loop->new(
|
||||||
push @contours, $polygon;
|
polygon => $polygon,
|
||||||
|
is_contour => $polygon->is_counter_clockwise,
|
||||||
|
depth => $i,
|
||||||
|
);
|
||||||
|
if ($loop->is_contour) {
|
||||||
|
push @{$contours[$i]}, $loop;
|
||||||
} else {
|
} else {
|
||||||
push @holes, $polygon;
|
push @{$holes[$i]}, $loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# nest loops: holes first
|
||||||
|
for my $d (0..$loop_number) {
|
||||||
|
# loop through all holes having depth $d
|
||||||
|
LOOP: for (my $i = 0; $i <= $#{$holes[$d]}; ++$i) {
|
||||||
|
my $loop = $holes[$d][$i];
|
||||||
|
|
||||||
|
# find the hole loop that contains this one, if any
|
||||||
|
for my $t (($d+1)..$loop_number) {
|
||||||
|
for (my $j = 0; $j <= $#{$holes[$t]}; ++$j) {
|
||||||
|
my $candidate_parent = $holes[$t][$j];
|
||||||
|
if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) {
|
||||||
|
$candidate_parent->add_child($loop);
|
||||||
|
splice @{$holes[$d]}, $i, 1;
|
||||||
|
--$i;
|
||||||
|
next LOOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# if no hole contains this hole, find the contour loop that contains it
|
||||||
|
for my $t (reverse 0..$loop_number) {
|
||||||
|
for (my $j = 0; $j <= $#{$contours[$t]}; ++$j) {
|
||||||
|
my $candidate_parent = $contours[$t][$j];
|
||||||
|
if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) {
|
||||||
|
$candidate_parent->add_child($loop);
|
||||||
|
splice @{$holes[$d]}, $i, 1;
|
||||||
|
--$i;
|
||||||
|
next LOOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# nest contour loops
|
||||||
|
for my $d (reverse 1..$loop_number) {
|
||||||
|
# loop through all contours having depth $d
|
||||||
|
LOOP: for (my $i = 0; $i <= $#{$contours[$d]}; ++$i) {
|
||||||
|
my $loop = $contours[$d][$i];
|
||||||
|
|
||||||
|
# find the contour loop that contains it
|
||||||
|
for my $t (reverse 0..($d-1)) {
|
||||||
|
for (my $j = 0; $j <= $#{$contours[$t]}; ++$j) {
|
||||||
|
my $candidate_parent = $contours[$t][$j];
|
||||||
|
if ($candidate_parent->polygon->contains_point($loop->polygon->first_point)) {
|
||||||
|
$candidate_parent->add_child($loop);
|
||||||
|
splice @{$contours[$d]}, $i, 1;
|
||||||
|
--$i;
|
||||||
|
next LOOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# at this point, all loops should be in $contours[0]
|
||||||
|
my @entities = $self->_traverse_loops($contours[0], \@thin_walls);
|
||||||
|
|
||||||
|
# if brim will be printed, reverse the order of perimeters so that
|
||||||
|
# we continue inwards after having finished the brim
|
||||||
|
# TODO: add test for perimeter order
|
||||||
|
@entities = reverse @entities
|
||||||
|
if $self->config->external_perimeters_first
|
||||||
|
|| ($self->layer_id == 0 && $self->print_config->brim_width > 0);
|
||||||
|
|
||||||
|
# append perimeters for this slice as a collection
|
||||||
|
$self->loops->append(Slic3r::ExtrusionPath::Collection->new(@entities))
|
||||||
|
if @entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
# fill gaps
|
# fill gaps
|
||||||
|
@ -216,88 +316,42 @@ sub process {
|
||||||
for map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), # use a bogus surface type
|
for map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL), # use a bogus surface type
|
||||||
@{offset2_ex(
|
@{offset2_ex(
|
||||||
[ map @{$_->simplify_p(&Slic3r::SCALED_RESOLUTION)}, @{union_ex(\@last)} ],
|
[ map @{$_->simplify_p(&Slic3r::SCALED_RESOLUTION)}, @{union_ex(\@last)} ],
|
||||||
-($pspacing/2 + $min_perimeter_infill_spacing/2),
|
-($pspacing/2 - $self->config->get_abs_value_over('infill_overlap', $pwidth) + $min_perimeter_infill_spacing/2),
|
||||||
+$min_perimeter_infill_spacing/2,
|
+$min_perimeter_infill_spacing/2,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
|
|
||||||
# process thin walls by collapsing slices to single passes
|
|
||||||
if (@thin_walls) {
|
|
||||||
# the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
|
||||||
# (actually, something larger than that still may exist due to mitering or other causes)
|
|
||||||
my $min_width = $pwidth / 4;
|
|
||||||
@thin_walls = @{offset2_ex([ map @$_, @thin_walls ], -$min_width/2, +$min_width/2)};
|
|
||||||
|
|
||||||
# the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
|
||||||
$self->_thin_wall_polylines([ map @{$_->medial_axis($pwidth + $pspacing, $min_width)}, @thin_walls ]);
|
|
||||||
Slic3r::debugf " %d thin walls detected\n", scalar(@{$self->_thin_wall_polylines}) if $Slic3r::debug;
|
|
||||||
|
|
||||||
if (0) {
|
|
||||||
require "Slic3r/SVG.pm";
|
|
||||||
Slic3r::SVG::output(
|
|
||||||
"medial_axis.svg",
|
|
||||||
no_arrows => 1,
|
|
||||||
expolygons => \@thin_walls,
|
|
||||||
green_polylines => [ map $_->polygon->split_at_first_point, @{$self->perimeters} ],
|
|
||||||
red_polylines => $self->_thin_wall_polylines,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# find nesting hierarchies separately for contours and holes
|
|
||||||
my $contours_pt = union_pt(\@contours);
|
|
||||||
$self->_holes_pt(union_pt(\@holes));
|
|
||||||
|
|
||||||
# order loops from inner to outer (in terms of object slices)
|
|
||||||
my @loops = $self->_traverse_pt($contours_pt, 0, 1);
|
|
||||||
|
|
||||||
# if brim will be printed, reverse the order of perimeters so that
|
|
||||||
# we continue inwards after having finished the brim
|
|
||||||
# TODO: add test for perimeter order
|
|
||||||
@loops = reverse @loops
|
|
||||||
if $self->config->external_perimeters_first
|
|
||||||
|| ($self->layer_id == 0 && $self->print_config->brim_width > 0);
|
|
||||||
|
|
||||||
# append perimeters for this slice as a collection
|
|
||||||
$self->loops->append(Slic3r::ExtrusionPath::Collection->new(@loops));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _traverse_pt {
|
sub _traverse_loops {
|
||||||
my ($self, $polynodes, $depth, $is_contour) = @_;
|
my ($self, $loops, $thin_walls) = @_;
|
||||||
|
|
||||||
# convert all polynodes to ExtrusionLoop objects
|
# loops is an arrayref of ::Loop objects
|
||||||
my $collection = Slic3r::ExtrusionPath::Collection->new; # temporary collection
|
# turn each one into an ExtrusionLoop object
|
||||||
my @children = ();
|
my $coll = Slic3r::ExtrusionPath::Collection->new;
|
||||||
foreach my $polynode (@$polynodes) {
|
foreach my $loop (@$loops) {
|
||||||
my $polygon = ($polynode->{outer} // $polynode->{hole})->clone;
|
my $is_external = $loop->is_external;
|
||||||
|
|
||||||
my $role = EXTR_ROLE_PERIMETER;
|
my ($role, $loop_role);
|
||||||
my $loop_role = EXTRL_ROLE_DEFAULT;
|
if ($is_external) {
|
||||||
|
$role = EXTR_ROLE_EXTERNAL_PERIMETER;
|
||||||
my $root_level = $depth == 0;
|
} else {
|
||||||
my $no_children = !@{ $polynode->{children} };
|
$role = EXTR_ROLE_PERIMETER;
|
||||||
my $is_external = $is_contour ? $root_level : $no_children;
|
}
|
||||||
my $is_internal = $is_contour ? $no_children : $root_level;
|
if ($loop->is_internal_contour) {
|
||||||
if ($is_contour && $is_internal) {
|
|
||||||
# internal perimeters are root level in case of holes
|
|
||||||
# and items with no children in case of contours
|
|
||||||
# Note that we set loop role to ContourInternalPerimeter
|
# Note that we set loop role to ContourInternalPerimeter
|
||||||
# also when loop is both internal and external (i.e.
|
# also when loop is both internal and external (i.e.
|
||||||
# there's only one contour loop).
|
# there's only one contour loop).
|
||||||
$loop_role = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER;
|
$loop_role = EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER;
|
||||||
}
|
} else {
|
||||||
if ($is_external) {
|
$loop_role = EXTR_ROLE_PERIMETER;
|
||||||
# external perimeters are root level in case of contours
|
|
||||||
# and items with no children in case of holes
|
|
||||||
$role = EXTR_ROLE_EXTERNAL_PERIMETER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# detect overhanging/bridging perimeters
|
# detect overhanging/bridging perimeters
|
||||||
my @paths = ();
|
my @paths = ();
|
||||||
if ($self->config->overhangs && $self->layer_id > 0) {
|
if ($self->config->overhangs && $self->layer_id > 0
|
||||||
|
&& !($self->object_config->support_material && $self->object_config->support_material_contact_distance == 0)) {
|
||||||
# get non-overhang paths by intersecting this loop with the grown lower slices
|
# get non-overhang paths by intersecting this loop with the grown lower slices
|
||||||
foreach my $polyline (@{ intersection_ppl([ $polygon ], $self->_lower_slices_p) }) {
|
foreach my $polyline (@{ intersection_ppl([ $loop->polygon ], $self->_lower_slices_p) }) {
|
||||||
push @paths, Slic3r::ExtrusionPath->new(
|
push @paths, Slic3r::ExtrusionPath->new(
|
||||||
polyline => $polyline,
|
polyline => $polyline,
|
||||||
role => $role,
|
role => $role,
|
||||||
|
@ -310,13 +364,13 @@ sub _traverse_pt {
|
||||||
# get overhang paths by checking what parts of this loop fall
|
# get overhang paths by checking what parts of this loop fall
|
||||||
# outside the grown lower slices (thus where the distance between
|
# outside the grown lower slices (thus where the distance between
|
||||||
# the loop centerline and original lower slices is >= half nozzle diameter
|
# the loop centerline and original lower slices is >= half nozzle diameter
|
||||||
foreach my $polyline (@{ diff_ppl([ $polygon ], $self->_lower_slices_p) }) {
|
foreach my $polyline (@{ diff_ppl([ $loop->polygon ], $self->_lower_slices_p) }) {
|
||||||
push @paths, Slic3r::ExtrusionPath->new(
|
push @paths, Slic3r::ExtrusionPath->new(
|
||||||
polyline => $polyline,
|
polyline => $polyline,
|
||||||
role => EXTR_ROLE_OVERHANG_PERIMETER,
|
role => EXTR_ROLE_OVERHANG_PERIMETER,
|
||||||
mm3_per_mm => $self->_mm3_per_mm_overhang,
|
mm3_per_mm => $self->_mm3_per_mm_overhang,
|
||||||
width => $self->overhang_flow->width,
|
width => $self->overhang_flow->width,
|
||||||
height => $self->layer_height,
|
height => $self->overhang_flow->height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,37 +382,22 @@ sub _traverse_pt {
|
||||||
@paths = map $_->clone, @{$collection->chained_path(0)};
|
@paths = map $_->clone, @{$collection->chained_path(0)};
|
||||||
} else {
|
} else {
|
||||||
push @paths, Slic3r::ExtrusionPath->new(
|
push @paths, Slic3r::ExtrusionPath->new(
|
||||||
polyline => $polygon->split_at_first_point,
|
polyline => $loop->polygon->split_at_first_point,
|
||||||
role => $role,
|
role => $role,
|
||||||
mm3_per_mm => $self->_mm3_per_mm,
|
mm3_per_mm => $self->_mm3_per_mm,
|
||||||
width => $self->perimeter_flow->width,
|
width => $self->perimeter_flow->width,
|
||||||
height => $self->layer_height,
|
height => $self->layer_height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
my $loop = Slic3r::ExtrusionLoop->new_from_paths(@paths);
|
my $eloop = Slic3r::ExtrusionLoop->new_from_paths(@paths);
|
||||||
$loop->role($loop_role);
|
$eloop->role($loop_role);
|
||||||
|
$coll->append($eloop);
|
||||||
# return ccw contours and cw holes
|
|
||||||
# GCode.pm will convert all of them to ccw, but it needs to know
|
|
||||||
# what the holes are in order to compute the correct inwards move
|
|
||||||
# We do this on the final Loop object because overhang clipping
|
|
||||||
# does not keep orientation.
|
|
||||||
if ($is_contour) {
|
|
||||||
$loop->make_counter_clockwise;
|
|
||||||
} else {
|
|
||||||
$loop->make_clockwise;
|
|
||||||
}
|
|
||||||
$collection->append($loop);
|
|
||||||
|
|
||||||
# save the children
|
|
||||||
push @children, $polynode->{children};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# if we're handling the top-level contours, add thin walls as candidates too
|
# append thin walls to the nearest-neighbor search (only for first iteration)
|
||||||
# in order to include them in the nearest-neighbor search
|
if (@$thin_walls) {
|
||||||
if ($is_contour && $depth == 0) {
|
foreach my $polyline (@$thin_walls) {
|
||||||
foreach my $polyline (@{$self->_thin_wall_polylines}) {
|
$coll->append(Slic3r::ExtrusionPath->new(
|
||||||
$collection->append(Slic3r::ExtrusionPath->new(
|
|
||||||
polyline => $polyline,
|
polyline => $polyline,
|
||||||
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
role => EXTR_ROLE_EXTERNAL_PERIMETER,
|
||||||
mm3_per_mm => $self->_mm3_per_mm,
|
mm3_per_mm => $self->_mm3_per_mm,
|
||||||
|
@ -366,51 +405,37 @@ sub _traverse_pt {
|
||||||
height => $self->layer_height,
|
height => $self->layer_height,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@$thin_walls = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
# use a nearest neighbor search to order these children
|
# sort entities
|
||||||
# TODO: supply second argument to chained_path() too?
|
my $sorted_coll = $coll->chained_path_indices(0);
|
||||||
# (We used to skip this chained_path() when $is_contour &&
|
my @indices = @{$sorted_coll->orig_indices};
|
||||||
# $depth == 0 because slices are ordered at G_code export
|
|
||||||
# time, but multiple top-level perimeters might belong to
|
|
||||||
# the same slice actually, so that was a broken optimization.)
|
|
||||||
# We supply no_reverse = false because we want to permit reversal
|
|
||||||
# of thin walls, but we rely on the fact that loops will never
|
|
||||||
# be reversed anyway.
|
|
||||||
my $sorted_collection = $collection->chained_path_indices(0);
|
|
||||||
my @orig_indices = @{$sorted_collection->orig_indices};
|
|
||||||
|
|
||||||
my @loops = ();
|
# traverse children
|
||||||
foreach my $loop (@$sorted_collection) {
|
my @entities = ();
|
||||||
my $orig_index = shift @orig_indices;
|
for my $i (0..$#indices) {
|
||||||
|
my $idx = $indices[$i];
|
||||||
if ($loop->isa('Slic3r::ExtrusionPath')) {
|
if ($idx > $#$loops) {
|
||||||
push @loops, $loop->clone;
|
# this is a thin wall
|
||||||
|
# let's get it from the sorted collection as it might have been reversed
|
||||||
|
push @entities, $sorted_coll->[$i]->clone;
|
||||||
} else {
|
} else {
|
||||||
# if this is an external contour find all holes belonging to this contour(s)
|
my $loop = $loops->[$idx];
|
||||||
# and prepend them
|
my $eloop = $coll->[$idx]->clone;
|
||||||
if ($is_contour && $depth == 0) {
|
|
||||||
# $loop is the outermost loop of an island
|
|
||||||
my @holes = ();
|
|
||||||
for (my $i = 0; $i <= $#{$self->_holes_pt}; $i++) {
|
|
||||||
if ($loop->polygon->contains_point($self->_holes_pt->[$i]{outer}->first_point)) {
|
|
||||||
push @holes, splice @{$self->_holes_pt}, $i, 1; # remove from candidates to reduce complexity
|
|
||||||
$i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# order holes efficiently
|
my @children = $self->_traverse_loops($loop->children, $thin_walls);
|
||||||
@holes = @holes[@{chained_path([ map {($_->{outer} // $_->{hole})->first_point} @holes ])}];
|
if ($loop->is_contour) {
|
||||||
|
$eloop->make_counter_clockwise;
|
||||||
push @loops, reverse map $self->_traverse_pt([$_], 0, 0), @holes;
|
push @entities, @children, $eloop;
|
||||||
}
|
} else {
|
||||||
|
$eloop->make_clockwise;
|
||||||
# traverse children and prepend them to this loop
|
push @entities, $eloop, @children;
|
||||||
push @loops, $self->_traverse_pt($children[$orig_index], $depth+1, $is_contour);
|
|
||||||
push @loops, $loop->clone;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return @loops;
|
}
|
||||||
|
return @entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub _fill_gaps {
|
sub _fill_gaps {
|
||||||
|
@ -459,4 +484,35 @@ sub _fill_gaps {
|
||||||
return @entities;
|
return @entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
package Slic3r::Layer::PerimeterGenerator::Loop;
|
||||||
|
use Moo;
|
||||||
|
|
||||||
|
has 'polygon' => (is => 'ro', required => 1);
|
||||||
|
has 'is_contour' => (is => 'ro', required => 1);
|
||||||
|
has 'depth' => (is => 'ro', required => 1);
|
||||||
|
has 'children' => (is => 'ro', default => sub { [] });
|
||||||
|
|
||||||
|
use List::Util qw(first);
|
||||||
|
|
||||||
|
sub add_child {
|
||||||
|
my ($self, $child) = @_;
|
||||||
|
push @{$self->children}, $child;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_external {
|
||||||
|
my ($self) = @_;
|
||||||
|
return $self->depth == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_internal_contour {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
if ($self->is_contour) {
|
||||||
|
# an internal contour is a contour containing no other contours
|
||||||
|
return !defined first { $_->is_contour } @{$self->children};
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -34,6 +34,7 @@ sub make_perimeters {
|
||||||
my $generator = Slic3r::Layer::PerimeterGenerator->new(
|
my $generator = Slic3r::Layer::PerimeterGenerator->new(
|
||||||
# input:
|
# input:
|
||||||
config => $self->config,
|
config => $self->config,
|
||||||
|
object_config => $self->layer->object->config,
|
||||||
print_config => $self->layer->print->config,
|
print_config => $self->layer->print->config,
|
||||||
layer_height => $self->height,
|
layer_height => $self->height,
|
||||||
layer_id => $self->layer->id,
|
layer_id => $self->layer->id,
|
||||||
|
@ -52,30 +53,6 @@ sub make_perimeters {
|
||||||
$generator->process;
|
$generator->process;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub prepare_fill_surfaces {
|
|
||||||
my $self = shift;
|
|
||||||
|
|
||||||
# Note: in order to make the psPrepareInfill step idempotent, we should never
|
|
||||||
# alter fill_surfaces boundaries on which our idempotency relies since that's
|
|
||||||
# the only meaningful information returned by psPerimeters.
|
|
||||||
|
|
||||||
# if no solid layers are requested, turn top/bottom surfaces to internal
|
|
||||||
if ($self->config->top_solid_layers == 0) {
|
|
||||||
$_->surface_type(S_TYPE_INTERNAL) for @{$self->fill_surfaces->filter_by_type(S_TYPE_TOP)};
|
|
||||||
}
|
|
||||||
if ($self->config->bottom_solid_layers == 0) {
|
|
||||||
$_->surface_type(S_TYPE_INTERNAL)
|
|
||||||
for @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOM)}, @{$self->fill_surfaces->filter_by_type(S_TYPE_BOTTOMBRIDGE)};
|
|
||||||
}
|
|
||||||
|
|
||||||
# turn too small internal regions into solid regions according to the user setting
|
|
||||||
if ($self->config->fill_density > 0) {
|
|
||||||
my $min_area = scale scale $self->config->solid_infill_below_area; # scaling an area requires two calls!
|
|
||||||
$_->surface_type(S_TYPE_INTERNALSOLID)
|
|
||||||
for grep { $_->area <= $min_area } @{$self->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub process_external_surfaces {
|
sub process_external_surfaces {
|
||||||
my ($self, $lower_layer) = @_;
|
my ($self, $lower_layer) = @_;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,9 @@ sub read_from_file {
|
||||||
: $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Format::AMF->read_file($input_file)
|
: $input_file =~ /\.amf(\.xml)?$/i ? Slic3r::Format::AMF->read_file($input_file)
|
||||||
: die "Input file must have .stl, .obj or .amf(.xml) extension\n";
|
: die "Input file must have .stl, .obj or .amf(.xml) extension\n";
|
||||||
|
|
||||||
|
die "The supplied file couldn't be read because it's empty.\n"
|
||||||
|
if $model->objects_count == 0;
|
||||||
|
|
||||||
$_->set_input_file($input_file) for @{$model->objects};
|
$_->set_input_file($input_file) for @{$model->objects};
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
@ -272,6 +275,7 @@ sub rotate {
|
||||||
} elsif ($axis == Z) {
|
} elsif ($axis == Z) {
|
||||||
$_->mesh->rotate_z($angle) for @{$self->volumes};
|
$_->mesh->rotate_z($angle) for @{$self->volumes};
|
||||||
}
|
}
|
||||||
|
$self->set_origin_translation(Slic3r::Pointf3->new(0,0,0));
|
||||||
$self->invalidate_bounding_box;
|
$self->invalidate_bounding_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,6 +289,7 @@ sub flip {
|
||||||
} elsif ($axis == Z) {
|
} elsif ($axis == Z) {
|
||||||
$_->mesh->flip_z for @{$self->volumes};
|
$_->mesh->flip_z for @{$self->volumes};
|
||||||
}
|
}
|
||||||
|
$self->set_origin_translation(Slic3r::Pointf3->new(0,0,0));
|
||||||
$self->invalidate_bounding_box;
|
$self->invalidate_bounding_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,4 +21,13 @@ sub new_unscale {
|
||||||
return $class->new(map Slic3r::Geometry::unscale($_), @_);
|
return $class->new(map Slic3r::Geometry::unscale($_), @_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package Slic3r::Pointf3;
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
sub new_unscale {
|
||||||
|
my $class = shift;
|
||||||
|
return $class->new(map Slic3r::Geometry::unscale($_), @_);
|
||||||
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -87,9 +87,12 @@ sub export_gcode {
|
||||||
if (@{$self->config->post_process}) {
|
if (@{$self->config->post_process}) {
|
||||||
$self->status_cb->(95, "Running post-processing scripts");
|
$self->status_cb->(95, "Running post-processing scripts");
|
||||||
$self->config->setenv;
|
$self->config->setenv;
|
||||||
for (@{$self->config->post_process}) {
|
for my $script (@{$self->config->post_process}) {
|
||||||
Slic3r::debugf " '%s' '%s'\n", $_, $output_file;
|
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
|
||||||
system($_, $output_file);
|
if (!-x $script) {
|
||||||
|
die "The configured post-processing script is not executable: check permissions. ($script)\n";
|
||||||
|
}
|
||||||
|
system($script, $output_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +114,8 @@ sub export_svg {
|
||||||
print "Exporting to $output_file..." unless $params{quiet};
|
print "Exporting to $output_file..." unless $params{quiet};
|
||||||
}
|
}
|
||||||
|
|
||||||
my $print_size = $self->size;
|
my $print_bb = $self->bounding_box;
|
||||||
|
my $print_size = $print_bb->size;
|
||||||
print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]);
|
print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]);
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
@ -137,16 +141,20 @@ EOF
|
||||||
my @previous_layer_slices = ();
|
my @previous_layer_slices = ();
|
||||||
for my $layer (@layers) {
|
for my $layer (@layers) {
|
||||||
$layer_id++;
|
$layer_id++;
|
||||||
# TODO: remove slic3r:z for raft layers
|
if ($layer->slice_z == -1) {
|
||||||
|
printf $fh qq{ <g id="layer%d">\n}, $layer_id;
|
||||||
|
} else {
|
||||||
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, unscale($layer->slice_z);
|
printf $fh qq{ <g id="layer%d" slic3r:z="%s">\n}, $layer_id, unscale($layer->slice_z);
|
||||||
|
}
|
||||||
|
|
||||||
my @current_layer_slices = ();
|
my @current_layer_slices = ();
|
||||||
# sort slices so that the outermost ones come first
|
# sort slices so that the outermost ones come first
|
||||||
my @slices = sort { $a->contour->contains_point($b->contour->[0]) ? 0 : 1 } @{$layer->slices};
|
my @slices = sort { $a->contour->contains_point($b->contour->first_point) ? 0 : 1 } @{$layer->slices};
|
||||||
foreach my $copy (@{$layer->object->copies}) {
|
foreach my $copy (@{$layer->object->_shifted_copies}) {
|
||||||
foreach my $slice (@slices) {
|
foreach my $slice (@slices) {
|
||||||
my $expolygon = $slice->clone;
|
my $expolygon = $slice->clone;
|
||||||
$expolygon->translate(@$copy);
|
$expolygon->translate(@$copy);
|
||||||
|
$expolygon->translate(-$print_bb->x_min, -$print_bb->y_min);
|
||||||
$print_polygon->($expolygon->contour, 'contour');
|
$print_polygon->($expolygon->contour, 'contour');
|
||||||
$print_polygon->($_, 'hole') for @{$expolygon->holes};
|
$print_polygon->($_, 'hole') for @{$expolygon->holes};
|
||||||
push @current_layer_slices, $expolygon;
|
push @current_layer_slices, $expolygon;
|
||||||
|
@ -200,7 +208,7 @@ sub make_skirt {
|
||||||
# checking whether we need to generate them
|
# checking whether we need to generate them
|
||||||
$self->skirt->clear;
|
$self->skirt->clear;
|
||||||
|
|
||||||
if ($self->config->skirts == 0
|
if (($self->config->skirts == 0 || $self->config->skirt_height == 0)
|
||||||
&& (!$self->config->ooze_prevention || @{$self->extruders} == 1)) {
|
&& (!$self->config->ooze_prevention || @{$self->extruders} == 1)) {
|
||||||
$self->set_step_done(STEP_SKIRT);
|
$self->set_step_done(STEP_SKIRT);
|
||||||
return;
|
return;
|
||||||
|
@ -443,7 +451,7 @@ sub expanded_output_filepath {
|
||||||
}
|
}
|
||||||
|
|
||||||
# This method assigns extruders to the volumes having a material
|
# This method assigns extruders to the volumes having a material
|
||||||
# but not having extruders set in the material config.
|
# but not having extruders set in the volume config.
|
||||||
sub auto_assign_extruders {
|
sub auto_assign_extruders {
|
||||||
my ($self, $model_object) = @_;
|
my ($self, $model_object) = @_;
|
||||||
|
|
||||||
|
@ -454,10 +462,8 @@ sub auto_assign_extruders {
|
||||||
foreach my $i (0..$#{$model_object->volumes}) {
|
foreach my $i (0..$#{$model_object->volumes}) {
|
||||||
my $volume = $model_object->volumes->[$i];
|
my $volume = $model_object->volumes->[$i];
|
||||||
if ($volume->material_id ne '') {
|
if ($volume->material_id ne '') {
|
||||||
my $material = $model_object->model->get_material($volume->material_id);
|
|
||||||
my $config = $material->config;
|
|
||||||
my $extruder_id = $i + 1;
|
my $extruder_id = $i + 1;
|
||||||
$config->set_ifndef('extruder', $extruder_id);
|
$volume->config->set_ifndef('extruder', $extruder_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,8 +169,8 @@ sub export {
|
||||||
require "Slic3r/SVG.pm";
|
require "Slic3r/SVG.pm";
|
||||||
Slic3r::SVG::output(
|
Slic3r::SVG::output(
|
||||||
"ooze_prevention.svg",
|
"ooze_prevention.svg",
|
||||||
polygons => [$outer_skirt],
|
|
||||||
red_polygons => \@skirts,
|
red_polygons => \@skirts,
|
||||||
|
polygons => [$outer_skirt],
|
||||||
points => $gcodegen->ooze_prevention->standby_points,
|
points => $gcodegen->ooze_prevention->standby_points,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -197,7 +197,7 @@ sub export {
|
||||||
$gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
|
$gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
|
||||||
print $fh $gcodegen->retract;
|
print $fh $gcodegen->retract;
|
||||||
print $fh $gcodegen->travel_to(
|
print $fh $gcodegen->travel_to(
|
||||||
$object->_copies_shift->negative,
|
Slic3r::Point->new(0,0),
|
||||||
undef,
|
undef,
|
||||||
'move to origin position for next object',
|
'move to origin position for next object',
|
||||||
);
|
);
|
||||||
|
@ -321,9 +321,14 @@ sub process_layer {
|
||||||
}
|
}
|
||||||
|
|
||||||
# set new layer - this will change Z and force a retraction if retract_layer_change is enabled
|
# set new layer - this will change Z and force a retraction if retract_layer_change is enabled
|
||||||
|
$gcode .= $self->_gcodegen->placeholder_parser->process($self->print->config->before_layer_gcode, {
|
||||||
|
layer_num => $layer->id,
|
||||||
|
layer_z => $layer->print_z,
|
||||||
|
}) . "\n" if $self->print->config->before_layer_gcode;
|
||||||
$gcode .= $self->_gcodegen->change_layer($layer);
|
$gcode .= $self->_gcodegen->change_layer($layer);
|
||||||
$gcode .= $self->_gcodegen->placeholder_parser->process($self->print->config->layer_gcode, {
|
$gcode .= $self->_gcodegen->placeholder_parser->process($self->print->config->layer_gcode, {
|
||||||
layer_num => $layer->id,
|
layer_num => $layer->id,
|
||||||
|
layer_z => $layer->print_z,
|
||||||
}) . "\n" if $self->print->config->layer_gcode;
|
}) . "\n" if $self->print->config->layer_gcode;
|
||||||
|
|
||||||
# extrude skirt
|
# extrude skirt
|
||||||
|
@ -428,6 +433,8 @@ sub process_layer {
|
||||||
{
|
{
|
||||||
my $extruder_id = $region->config->perimeter_extruder-1;
|
my $extruder_id = $region->config->perimeter_extruder-1;
|
||||||
foreach my $perimeter_coll (@{$layerm->perimeters}) {
|
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
|
# init by_extruder item only if we actually use the extruder
|
||||||
$by_extruder{$extruder_id} //= [];
|
$by_extruder{$extruder_id} //= [];
|
||||||
|
|
||||||
|
@ -450,6 +457,8 @@ sub process_layer {
|
||||||
# throughout the code). We can redefine the order of such Collections but we have to
|
# throughout the code). We can redefine the order of such Collections but we have to
|
||||||
# do each one completely at once.
|
# do each one completely at once.
|
||||||
foreach my $fill (@{$layerm->fills}) {
|
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
|
# init by_extruder item only if we actually use the extruder
|
||||||
my $extruder_id = $fill->[0]->is_solid_infill
|
my $extruder_id = $fill->[0]->is_solid_infill
|
||||||
? $region->config->solid_infill_extruder-1
|
? $region->config->solid_infill_extruder-1
|
||||||
|
|
|
@ -55,13 +55,14 @@ sub slice {
|
||||||
|
|
||||||
# raise first object layer Z by the thickness of the raft itself
|
# raise first object layer Z by the thickness of the raft itself
|
||||||
# plus the extra distance required by the support material logic
|
# plus the extra distance required by the support material logic
|
||||||
$print_z += $self->config->get_value('first_layer_height');
|
my $first_layer_height = $self->config->get_value('first_layer_height');
|
||||||
|
$print_z += $first_layer_height;
|
||||||
$print_z += $self->config->layer_height * ($self->config->raft_layers - 1);
|
$print_z += $self->config->layer_height * ($self->config->raft_layers - 1);
|
||||||
|
|
||||||
# at this stage we don't know which nozzles are actually used for the first layer
|
# at this stage we don't know which nozzles are actually used for the first layer
|
||||||
# so we compute the average of all of them
|
# so we compute the average of all of them
|
||||||
my $nozzle_diameter = sum(@{$self->print->config->nozzle_diameter})/@{$self->print->config->nozzle_diameter};
|
my $nozzle_diameter = sum(@{$self->print->config->nozzle_diameter})/@{$self->print->config->nozzle_diameter};
|
||||||
my $distance = Slic3r::Print::SupportMaterial::contact_distance($nozzle_diameter);
|
my $distance = $self->_support_material->contact_distance($first_layer_height, $nozzle_diameter);
|
||||||
|
|
||||||
# force first layer print_z according to the contact distance
|
# force first layer print_z according to the contact distance
|
||||||
# (the loop below will raise print_z by such height)
|
# (the loop below will raise print_z by such height)
|
||||||
|
@ -291,7 +292,7 @@ sub slice {
|
||||||
while (@{$self->layers} && !@{$self->get_layer(0)->slices}) {
|
while (@{$self->layers} && !@{$self->get_layer(0)->slices}) {
|
||||||
shift @{$self->layers};
|
shift @{$self->layers};
|
||||||
for (my $i = 0; $i <= $#{$self->layers}; $i++) {
|
for (my $i = 0; $i <= $#{$self->layers}; $i++) {
|
||||||
$self->get_layer($i)->id( $self->get_layer($i)->id-1 );
|
$self->get_layer($i)->set_id( $self->get_layer($i)->id-1 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,6 +538,14 @@ sub generate_support_material {
|
||||||
}
|
}
|
||||||
$self->print->status_cb->(85, "Generating support material");
|
$self->print->status_cb->(85, "Generating support material");
|
||||||
|
|
||||||
|
$self->_support_material->generate($self);
|
||||||
|
|
||||||
|
$self->set_step_done(STEP_SUPPORTMATERIAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _support_material {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
my $first_layer_flow = Slic3r::Flow->new_from_width(
|
my $first_layer_flow = Slic3r::Flow->new_from_width(
|
||||||
width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
|
width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
|
||||||
role => FLOW_ROLE_SUPPORT_MATERIAL,
|
role => FLOW_ROLE_SUPPORT_MATERIAL,
|
||||||
|
@ -546,16 +555,13 @@ sub generate_support_material {
|
||||||
bridge_flow_ratio => 0,
|
bridge_flow_ratio => 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
my $s = Slic3r::Print::SupportMaterial->new(
|
return Slic3r::Print::SupportMaterial->new(
|
||||||
print_config => $self->print->config,
|
print_config => $self->print->config,
|
||||||
object_config => $self->config,
|
object_config => $self->config,
|
||||||
first_layer_flow => $first_layer_flow,
|
first_layer_flow => $first_layer_flow,
|
||||||
flow => $self->support_material_flow,
|
flow => $self->support_material_flow,
|
||||||
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
|
interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE),
|
||||||
);
|
);
|
||||||
$s->generate($self);
|
|
||||||
|
|
||||||
$self->set_step_done(STEP_SUPPORTMATERIAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub detect_surfaces_type {
|
sub detect_surfaces_type {
|
||||||
|
@ -572,6 +578,7 @@ sub detect_surfaces_type {
|
||||||
my $diff = diff(
|
my $diff = diff(
|
||||||
[ map @$_, @$subject ],
|
[ map @$_, @$subject ],
|
||||||
[ map @$_, @$clip ],
|
[ map @$_, @$clip ],
|
||||||
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
# collapse very narrow parts (using the safety offset in the diff is not enough)
|
# collapse very narrow parts (using the safety offset in the diff is not enough)
|
||||||
|
@ -616,6 +623,11 @@ sub detect_surfaces_type {
|
||||||
S_TYPE_BOTTOMBRIDGE,
|
S_TYPE_BOTTOMBRIDGE,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
# if we have soluble support material, don't bridge
|
||||||
|
if ($self->config->support_material && $self->config->support_material_contact_distance == 0) {
|
||||||
|
$_->surface_type(S_TYPE_BOTTOM) for @bottom;
|
||||||
|
}
|
||||||
|
|
||||||
# if user requested internal shells, we need to identify surfaces
|
# if user requested internal shells, we need to identify surfaces
|
||||||
# lying on other slices not belonging to this region
|
# lying on other slices not belonging to this region
|
||||||
if ($self->config->interface_shells) {
|
if ($self->config->interface_shells) {
|
||||||
|
@ -1009,7 +1021,7 @@ sub combine_infill {
|
||||||
# Because fill areas for rectilinear and honeycomb are grown
|
# Because fill areas for rectilinear and honeycomb are grown
|
||||||
# later to overlap perimeters, we need to counteract that too.
|
# later to overlap perimeters, we need to counteract that too.
|
||||||
+ (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/)
|
+ (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/)
|
||||||
? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING
|
? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width
|
||||||
: 0)
|
: 0)
|
||||||
)}, @$intersection;
|
)}, @$intersection;
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ sub generate {
|
||||||
$i, # id
|
$i, # id
|
||||||
($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]), # height
|
($i == 0) ? $support_z->[$i] : ($support_z->[$i] - $support_z->[$i-1]), # height
|
||||||
$support_z->[$i], # print_z
|
$support_z->[$i], # print_z
|
||||||
-1); # slice_z
|
);
|
||||||
if ($i >= 1) {
|
if ($i >= 1) {
|
||||||
$object->support_layers->[-2]->set_upper_layer($object->support_layers->[-1]);
|
$object->support_layers->[-2]->set_upper_layer($object->support_layers->[-1]);
|
||||||
$object->support_layers->[-1]->set_lower_layer($object->support_layers->[-2]);
|
$object->support_layers->[-1]->set_lower_layer($object->support_layers->[-2]);
|
||||||
|
@ -270,8 +270,7 @@ sub contact_area {
|
||||||
@{$layer->regions};
|
@{$layer->regions};
|
||||||
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
|
my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters;
|
||||||
|
|
||||||
my $contact_z = $layer->print_z - contact_distance($nozzle_diameter);
|
my $contact_z = $layer->print_z - $self->contact_distance($layer->height, $nozzle_diameter);
|
||||||
###$contact_z = $layer->print_z - $layer->height;
|
|
||||||
|
|
||||||
# ignore this contact area if it's too low
|
# ignore this contact area if it's too low
|
||||||
next if $contact_z < $self->object_config->get_value('first_layer_height');
|
next if $contact_z < $self->object_config->get_value('first_layer_height');
|
||||||
|
@ -339,12 +338,13 @@ sub support_layers_z {
|
||||||
# layer_height > nozzle_diameter * 0.75
|
# layer_height > nozzle_diameter * 0.75
|
||||||
my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1);
|
my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1);
|
||||||
my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75);
|
my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75);
|
||||||
|
my $contact_distance = $self->contact_distance($support_material_height, $nozzle_diameter);
|
||||||
|
|
||||||
# initialize known, fixed, support layers
|
# initialize known, fixed, support layers
|
||||||
my @z = sort { $a <=> $b }
|
my @z = sort { $a <=> $b }
|
||||||
@$contact_z,
|
@$contact_z,
|
||||||
@$top_z, # TODO: why we have this?
|
@$top_z, # TODO: why we have this?
|
||||||
(map $_ + contact_distance($nozzle_diameter), @$top_z);
|
(map $_ + $contact_distance, @$top_z);
|
||||||
|
|
||||||
# enforce first layer height
|
# enforce first layer height
|
||||||
my $first_layer_height = $self->object_config->get_value('first_layer_height');
|
my $first_layer_height = $self->object_config->get_value('first_layer_height');
|
||||||
|
@ -752,8 +752,12 @@ sub generate_toolpaths {
|
||||||
# TODO: use offset2_ex()
|
# TODO: use offset2_ex()
|
||||||
$to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing);
|
$to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing);
|
||||||
}
|
}
|
||||||
$filler->spacing($base_flow->spacing);
|
|
||||||
|
|
||||||
|
# We don't use $base_flow->spacing because we need a constant spacing
|
||||||
|
# value that guarantees that all layers are correctly aligned.
|
||||||
|
$filler->spacing($flow->spacing);
|
||||||
|
|
||||||
|
my $mm3_per_mm = $base_flow->mm3_per_mm;
|
||||||
foreach my $expolygon (@$to_infill) {
|
foreach my $expolygon (@$to_infill) {
|
||||||
my @p = $filler->fill_surface(
|
my @p = $filler->fill_surface(
|
||||||
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
|
Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL),
|
||||||
|
@ -761,7 +765,6 @@ sub generate_toolpaths {
|
||||||
layer_height => $layer->height,
|
layer_height => $layer->height,
|
||||||
complete => 1,
|
complete => 1,
|
||||||
);
|
);
|
||||||
my $mm3_per_mm = $base_flow->mm3_per_mm;
|
|
||||||
|
|
||||||
push @paths, map Slic3r::ExtrusionPath->new(
|
push @paths, map Slic3r::ExtrusionPath->new(
|
||||||
polyline => Slic3r::Polyline->new(@$_),
|
polyline => Slic3r::Polyline->new(@$_),
|
||||||
|
@ -906,10 +909,15 @@ sub overlapping_layers {
|
||||||
} 0..$#$support_z;
|
} 0..$#$support_z;
|
||||||
}
|
}
|
||||||
|
|
||||||
# class method
|
|
||||||
sub contact_distance {
|
sub contact_distance {
|
||||||
my ($nozzle_diameter) = @_;
|
my ($self, $layer_height, $nozzle_diameter) = @_;
|
||||||
return $nozzle_diameter * 1.5;
|
|
||||||
|
my $extra = $self->object_config->support_material_contact_distance;
|
||||||
|
if ($extra == 0) {
|
||||||
|
return $layer_height;
|
||||||
|
} else {
|
||||||
|
return $nozzle_diameter + $extra;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -156,7 +156,7 @@ sub init_print {
|
||||||
$model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
|
$model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
|
||||||
}
|
}
|
||||||
$model->arrange_objects($print->config->min_object_distance);
|
$model->arrange_objects($print->config->min_object_distance);
|
||||||
$model->center_instances_around_point(Slic3r::Pointf->new(@{$params{print_center}}) // Slic3r::Pointf->new(100,100));
|
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
|
||||||
foreach my $model_object (@{$model->objects}) {
|
foreach my $model_object (@{$model->objects}) {
|
||||||
$print->auto_assign_extruders($model_object);
|
$print->auto_assign_extruders($model_object);
|
||||||
$print->add_model_object($model_object);
|
$print->add_model_object($model_object);
|
||||||
|
|
10
slic3r.pl
10
slic3r.pl
|
@ -371,6 +371,7 @@ $j
|
||||||
--end-gcode Load final G-code from the supplied file. This will overwrite
|
--end-gcode Load final G-code from the supplied file. This will overwrite
|
||||||
the default commands (turn off temperature [M104 S0],
|
the default commands (turn off temperature [M104 S0],
|
||||||
home X axis [G28 X], disable motors [M84]).
|
home X axis [G28 X], disable motors [M84]).
|
||||||
|
--before-layer-gcode Load before-layer-change G-code from the supplied file (default: nothing).
|
||||||
--layer-gcode Load layer-change G-code from the supplied file (default: nothing).
|
--layer-gcode Load layer-change G-code from the supplied file (default: nothing).
|
||||||
--toolchange-gcode Load tool-change G-code from the supplied file (default: nothing).
|
--toolchange-gcode Load tool-change G-code from the supplied file (default: nothing).
|
||||||
--seam-position Position of loop starting points (random/nearest/aligned, default: $config->{seam_position}).
|
--seam-position Position of loop starting points (random/nearest/aligned, default: $config->{seam_position}).
|
||||||
|
@ -405,6 +406,8 @@ $j
|
||||||
Spacing between pattern lines (mm, default: $config->{support_material_spacing})
|
Spacing between pattern lines (mm, default: $config->{support_material_spacing})
|
||||||
--support-material-angle
|
--support-material-angle
|
||||||
Support material angle in degrees (range: 0-90, default: $config->{support_material_angle})
|
Support material angle in degrees (range: 0-90, default: $config->{support_material_angle})
|
||||||
|
--support-material-contact-distance
|
||||||
|
Vertical distance between object and support material (0+, default: $config->{support_material_contact_distance})
|
||||||
--support-material-interface-layers
|
--support-material-interface-layers
|
||||||
Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers})
|
Number of perpendicular layers between support material and object (0+, default: $config->{support_material_interface_layers})
|
||||||
--support-material-interface-spacing
|
--support-material-interface-spacing
|
||||||
|
@ -431,10 +434,10 @@ $j
|
||||||
|
|
||||||
Retraction options for multi-extruder setups:
|
Retraction options for multi-extruder setups:
|
||||||
--retract-length-toolchange
|
--retract-length-toolchange
|
||||||
Length of retraction in mm when disabling tool (default: $config->{retract_length}[0])
|
Length of retraction in mm when disabling tool (default: $config->{retract_length_toolchange}[0])
|
||||||
--retract-restart-extra-toolchnage
|
--retract-restart-extra-toolchange
|
||||||
Additional amount of filament in mm to push after
|
Additional amount of filament in mm to push after
|
||||||
switching tool (default: $config->{retract_restart_extra}[0])
|
switching tool (default: $config->{retract_restart_extra_toolchange}[0])
|
||||||
|
|
||||||
Cooling options:
|
Cooling options:
|
||||||
--cooling Enable fan and cooling control
|
--cooling Enable fan and cooling control
|
||||||
|
@ -498,6 +501,7 @@ $j
|
||||||
Set a different extrusion width for top infill
|
Set a different extrusion width for top infill
|
||||||
--support-material-extrusion-width
|
--support-material-extrusion-width
|
||||||
Set a different extrusion width for support material
|
Set a different extrusion width for support material
|
||||||
|
--infill-overlap Overlap between infill and perimeters (default: $config->{infill_overlap})
|
||||||
--bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: $config->{bridge_flow_ratio})
|
--bridge-flow-ratio Multiplier for extrusion when bridging (> 0, default: $config->{bridge_flow_ratio})
|
||||||
|
|
||||||
Multiple extruder options:
|
Multiple extruder options:
|
||||||
|
|
1
t/fill.t
1
t/fill.t
|
@ -206,6 +206,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||||
$config->set('bottom_solid_layers', 0);
|
$config->set('bottom_solid_layers', 0);
|
||||||
$config->set('infill_extruder', 2);
|
$config->set('infill_extruder', 2);
|
||||||
$config->set('infill_extrusion_width', 0.5);
|
$config->set('infill_extrusion_width', 0.5);
|
||||||
|
$config->set('fill_density', 40);
|
||||||
$config->set('cooling', 0); # for preventing speeds from being altered
|
$config->set('cooling', 0); # for preventing speeds from being altered
|
||||||
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
|
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
|
||||||
|
|
||||||
|
|
3
t/flow.t
3
t/flow.t
|
@ -34,7 +34,8 @@ use Slic3r::Test;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
my $E_per_mm_avg = sum(@E_per_mm) / @E_per_mm;
|
my $E_per_mm_avg = sum(@E_per_mm) / @E_per_mm;
|
||||||
ok !(defined first { abs($_ - $E_per_mm_avg) > 0.01 } @E_per_mm),
|
# allow some tolerance because solid rectilinear infill might be adjusted/stretched
|
||||||
|
ok !(defined first { abs($_ - $E_per_mm_avg) > 0.015 } @E_per_mm),
|
||||||
'first_layer_extrusion_width applies to everything on first layer';
|
'first_layer_extrusion_width applies to everything on first layer';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
t/multi.t
10
t/multi.t
|
@ -44,7 +44,6 @@ use Slic3r::Test;
|
||||||
if $tool_temp[$tool] != $expected_temp + $config->standby_temperature_delta;
|
if $tool_temp[$tool] != $expected_temp + $config->standby_temperature_delta;
|
||||||
|
|
||||||
push @toolchange_points, my $point = Slic3r::Point->new_scale($self->X, $self->Y);
|
push @toolchange_points, my $point = Slic3r::Point->new_scale($self->X, $self->Y);
|
||||||
$point->translate(map +scale($_), @{ $config->extruder_offset->[$tool] });
|
|
||||||
}
|
}
|
||||||
$tool = $1;
|
$tool = $1;
|
||||||
} elsif ($cmd eq 'M104' || $cmd eq 'M109') {
|
} elsif ($cmd eq 'M104' || $cmd eq 'M109') {
|
||||||
|
@ -73,11 +72,11 @@ use Slic3r::Test;
|
||||||
if (0) {
|
if (0) {
|
||||||
require "Slic3r/SVG.pm";
|
require "Slic3r/SVG.pm";
|
||||||
Slic3r::SVG::output(
|
Slic3r::SVG::output(
|
||||||
"ooze_prevention.svg",
|
"ooze_prevention_test.svg",
|
||||||
no_arrows => 1,
|
no_arrows => 1,
|
||||||
polygons => [$convex_hull],
|
polygons => [$convex_hull],
|
||||||
points => \@toolchange_points,
|
|
||||||
red_points => \@t,
|
red_points => \@t,
|
||||||
|
points => \@toolchange_points,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +168,7 @@ use Slic3r::Test;
|
||||||
|
|
||||||
{
|
{
|
||||||
my $model = stacked_cubes();
|
my $model = stacked_cubes();
|
||||||
|
my $object = $model->objects->[0];
|
||||||
|
|
||||||
my $config = Slic3r::Config->new_from_defaults;
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
$config->set('layer_height', 0.4);
|
$config->set('layer_height', 0.4);
|
||||||
|
@ -176,8 +176,8 @@ use Slic3r::Test;
|
||||||
$config->set('skirts', 0);
|
$config->set('skirts', 0);
|
||||||
my $print = Slic3r::Test::init_print($model, config => $config);
|
my $print = Slic3r::Test::init_print($model, config => $config);
|
||||||
|
|
||||||
is $model->get_material('lower')->config->extruder, 1, 'auto_assign_extruders() assigned correct extruder to first volume';
|
is $object->volumes->[0]->config->extruder, 1, 'auto_assign_extruders() assigned correct extruder to first volume';
|
||||||
is $model->get_material('upper')->config->extruder, 2, 'auto_assign_extruders() assigned correct extruder to second volume';
|
is $object->volumes->[1]->config->extruder, 2, 'auto_assign_extruders() assigned correct extruder to second volume';
|
||||||
|
|
||||||
my $tool = undef;
|
my $tool = undef;
|
||||||
my %T0 = my %T1 = (); # Z => 1
|
my %T0 = my %T1 = (); # Z => 1
|
||||||
|
|
|
@ -29,7 +29,7 @@ use Slic3r::Test;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ok abs($retracted) < epsilon, 'all retractions are compensated';
|
ok abs($retracted) < 0.01, 'all retractions are compensated';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,7 @@ use Slic3r::Test;
|
||||||
$config->set('top_solid_layers', 3);
|
$config->set('top_solid_layers', 3);
|
||||||
$config->set('solid_infill_speed', 99);
|
$config->set('solid_infill_speed', 99);
|
||||||
$config->set('top_solid_infill_speed', 99);
|
$config->set('top_solid_infill_speed', 99);
|
||||||
|
$config->set('bridge_speed', 99);
|
||||||
|
|
||||||
my $print = Slic3r::Test::init_print('sloping_hole', config => $config);
|
my $print = Slic3r::Test::init_print('sloping_hole', config => $config);
|
||||||
my %solid_layers = (); # Z => 1
|
my %solid_layers = (); # Z => 1
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Test::More tests => 5;
|
use Test::More tests => 6;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
@ -79,6 +79,15 @@ use Slic3r::Test;
|
||||||
ok Slic3r::Test::gcode($print), 'successful G-code generation when skirt is smaller than brim width';
|
ok Slic3r::Test::gcode($print), 'successful G-code generation when skirt is smaller than brim width';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
|
$config->set('skirts', 1);
|
||||||
|
$config->set('skirt_height', 0);
|
||||||
|
|
||||||
|
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||||
|
ok Slic3r::Test::gcode($print), 'successful G-code generation when skirt_height = 0 and skirts > 0';
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my $config = Slic3r::Config->new_from_defaults;
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
$config->set('layer_height', 0.4);
|
$config->set('layer_height', 0.4);
|
||||||
|
|
|
@ -30,7 +30,7 @@ use Slic3r::Test;
|
||||||
first_layer_flow => $flow,
|
first_layer_flow => $flow,
|
||||||
);
|
);
|
||||||
my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
|
my $support_z = $support->support_layers_z(\@contact_z, \@top_z, $config->layer_height);
|
||||||
my $expected_top_spacing = Slic3r::Print::SupportMaterial::contact_distance($config->nozzle_diameter->[0]);
|
my $expected_top_spacing = $support->contact_distance($config->layer_height, $config->nozzle_diameter->[0]);
|
||||||
|
|
||||||
is $support_z->[0], $config->first_layer_height,
|
is $support_z->[0], $config->first_layer_height,
|
||||||
'first layer height is honored';
|
'first layer height is honored';
|
||||||
|
|
|
@ -12,7 +12,7 @@ BEGIN {
|
||||||
use Getopt::Long qw(:config no_auto_abbrev);
|
use Getopt::Long qw(:config no_auto_abbrev);
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
use Slic3r::GUI;
|
use Slic3r::GUI;
|
||||||
use Slic3r::GUI::PreviewCanvas;
|
use Slic3r::GUI::3DScene;
|
||||||
$|++;
|
$|++;
|
||||||
|
|
||||||
my %opt = ();
|
my %opt = ();
|
||||||
|
@ -31,11 +31,12 @@ my %opt = ();
|
||||||
|
|
||||||
# make sure all objects have at least one defined instance
|
# make sure all objects have at least one defined instance
|
||||||
$model->add_default_instances;
|
$model->add_default_instances;
|
||||||
|
$_->center_around_origin for @{$model->objects}; # and align to Z = 0
|
||||||
|
|
||||||
my $app = Slic3r::ViewMesh->new;
|
my $app = Slic3r::ViewMesh->new;
|
||||||
$app->{canvas}->enable_picking(1);
|
$app->{canvas}->enable_picking(1);
|
||||||
$app->{canvas}->enable_moving($opt{enable_moving});
|
$app->{canvas}->enable_moving($opt{enable_moving});
|
||||||
$app->{canvas}->load_object($model->objects->[0]);
|
$app->{canvas}->load_object($model, 0);
|
||||||
$app->{canvas}->set_auto_bed_shape;
|
$app->{canvas}->set_auto_bed_shape;
|
||||||
$app->{canvas}->zoom_to_volumes;
|
$app->{canvas}->zoom_to_volumes;
|
||||||
$app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut};
|
$app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut};
|
||||||
|
@ -66,7 +67,7 @@ sub OnInit {
|
||||||
my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]);
|
my $frame = Wx::Frame->new(undef, -1, 'Mesh Viewer', [-1, -1], [500, 400]);
|
||||||
my $panel = Wx::Panel->new($frame, -1);
|
my $panel = Wx::Panel->new($frame, -1);
|
||||||
|
|
||||||
$self->{canvas} = Slic3r::GUI::PreviewCanvas->new($panel);
|
$self->{canvas} = Slic3r::GUI::3DScene->new($panel);
|
||||||
|
|
||||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||||
$sizer->Add($self->{canvas}, 1, wxEXPAND, 0);
|
$sizer->Add($self->{canvas}, 1, wxEXPAND, 0);
|
||||||
|
|
|
@ -12,7 +12,7 @@ BEGIN {
|
||||||
use Getopt::Long qw(:config no_auto_abbrev);
|
use Getopt::Long qw(:config no_auto_abbrev);
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
use Slic3r::GUI;
|
use Slic3r::GUI;
|
||||||
use Slic3r::GUI::PreviewCanvas;
|
use Slic3r::GUI::3DScene;
|
||||||
$|++;
|
$|++;
|
||||||
|
|
||||||
my %opt = ();
|
my %opt = ();
|
||||||
|
@ -82,13 +82,13 @@ sub OnInit {
|
||||||
|
|
||||||
my $canvas;
|
my $canvas;
|
||||||
if ($d3) {
|
if ($d3) {
|
||||||
$canvas = Slic3r::GUI::PreviewCanvas->new($panel);
|
$canvas = Slic3r::GUI::3DScene->new($panel);
|
||||||
$canvas->print($print);
|
|
||||||
|
|
||||||
$canvas->set_bed_shape($print->config->bed_shape);
|
$canvas->set_bed_shape($print->config->bed_shape);
|
||||||
|
|
||||||
foreach my $object (@{$print->objects}) {
|
foreach my $object (@{$print->objects}) {
|
||||||
$canvas->load_object($object->model_object);
|
#$canvas->load_print_object_slices($object);
|
||||||
|
$canvas->load_print_object_toolpaths($object);
|
||||||
|
#$canvas->load_object($object->model_object);
|
||||||
}
|
}
|
||||||
$canvas->zoom_to_volumes;
|
$canvas->zoom_to_volumes;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1677,6 +1677,8 @@ src/libslic3r/GCodeWriter.cpp
|
||||||
src/libslic3r/GCodeWriter.hpp
|
src/libslic3r/GCodeWriter.hpp
|
||||||
src/libslic3r/Geometry.cpp
|
src/libslic3r/Geometry.cpp
|
||||||
src/libslic3r/Geometry.hpp
|
src/libslic3r/Geometry.hpp
|
||||||
|
src/libslic3r/GUI/3DScene.cpp
|
||||||
|
src/libslic3r/GUI/3DScene.hpp
|
||||||
src/libslic3r/Layer.cpp
|
src/libslic3r/Layer.cpp
|
||||||
src/libslic3r/Layer.hpp
|
src/libslic3r/Layer.hpp
|
||||||
src/libslic3r/LayerRegion.cpp
|
src/libslic3r/LayerRegion.cpp
|
||||||
|
@ -1765,6 +1767,7 @@ xsp/Flow.xsp
|
||||||
xsp/GCodeSender.xsp
|
xsp/GCodeSender.xsp
|
||||||
xsp/GCodeWriter.xsp
|
xsp/GCodeWriter.xsp
|
||||||
xsp/Geometry.xsp
|
xsp/Geometry.xsp
|
||||||
|
xsp/GUI_3DScene.xsp
|
||||||
xsp/Layer.xsp
|
xsp/Layer.xsp
|
||||||
xsp/Line.xsp
|
xsp/Line.xsp
|
||||||
xsp/Model.xsp
|
xsp/Model.xsp
|
||||||
|
|
|
@ -23,6 +23,11 @@ use overload
|
||||||
'@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #,
|
'@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #,
|
||||||
'fallback' => 1;
|
'fallback' => 1;
|
||||||
|
|
||||||
|
sub pp {
|
||||||
|
my ($self) = @_;
|
||||||
|
return [ @$self ];
|
||||||
|
}
|
||||||
|
|
||||||
package Slic3r::Pointf;
|
package Slic3r::Pointf;
|
||||||
use overload
|
use overload
|
||||||
'@{}' => sub { $_[0]->arrayref },
|
'@{}' => sub { $_[0]->arrayref },
|
||||||
|
@ -33,6 +38,11 @@ use overload
|
||||||
'@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #,
|
'@{}' => sub { [ $_[0]->x, $_[0]->y, $_[0]->z ] }, #,
|
||||||
'fallback' => 1;
|
'fallback' => 1;
|
||||||
|
|
||||||
|
sub pp {
|
||||||
|
my ($self) = @_;
|
||||||
|
return [ @$self ];
|
||||||
|
}
|
||||||
|
|
||||||
package Slic3r::ExPolygon;
|
package Slic3r::ExPolygon;
|
||||||
use overload
|
use overload
|
||||||
'@{}' => sub { $_[0]->arrayref },
|
'@{}' => sub { $_[0]->arrayref },
|
||||||
|
@ -186,6 +196,9 @@ sub new {
|
||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
package Slic3r::GUI::_3DScene::GLVertexArray;
|
||||||
|
sub CLONE_SKIP { 1 }
|
||||||
|
|
||||||
package main;
|
package main;
|
||||||
for my $class (qw(
|
for my $class (qw(
|
||||||
Slic3r::BridgeDetector
|
Slic3r::BridgeDetector
|
||||||
|
|
|
@ -89,8 +89,7 @@ BridgeDetector::detect_angle()
|
||||||
{
|
{
|
||||||
Polygons pp = this->expolygon;
|
Polygons pp = this->expolygon;
|
||||||
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
|
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
|
||||||
Lines lines;
|
Lines lines = p->lines();
|
||||||
p->lines(&lines);
|
|
||||||
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
|
||||||
angles.push_back(line->direction());
|
angles.push_back(line->direction());
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,18 +295,21 @@ class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector<Pointf
|
||||||
};
|
};
|
||||||
|
|
||||||
bool deserialize(std::string str) {
|
bool deserialize(std::string str) {
|
||||||
std::vector<Pointf> values;
|
this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string point_str;
|
std::string point_str;
|
||||||
while (std::getline(is, point_str, ',')) {
|
while (std::getline(is, point_str, ',')) {
|
||||||
Pointf point;
|
Pointf point;
|
||||||
std::istringstream iss(point_str);
|
std::istringstream iss(point_str);
|
||||||
iss >> point.x;
|
std::string coord_str;
|
||||||
iss.ignore(std::numeric_limits<std::streamsize>::max(), 'x');
|
if (std::getline(iss, coord_str, 'x')) {
|
||||||
iss >> point.y;
|
std::istringstream(coord_str) >> point.x;
|
||||||
values.push_back(point);
|
if (std::getline(iss, coord_str, 'x')) {
|
||||||
|
std::istringstream(coord_str) >> point.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->values.push_back(point);
|
||||||
}
|
}
|
||||||
this->values = values;
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -129,14 +129,20 @@ ExPolygon::simplify_p(double tolerance) const
|
||||||
pp.reserve(this->holes.size() + 1);
|
pp.reserve(this->holes.size() + 1);
|
||||||
|
|
||||||
// contour
|
// contour
|
||||||
|
{
|
||||||
Polygon p = this->contour;
|
Polygon p = this->contour;
|
||||||
|
p.points.push_back(p.points.front());
|
||||||
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
||||||
|
p.points.pop_back();
|
||||||
pp.push_back(p);
|
pp.push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
// holes
|
// holes
|
||||||
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
|
||||||
p = *it;
|
Polygon p = *it;
|
||||||
|
p.points.push_back(p.points.front());
|
||||||
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
||||||
|
p.points.pop_back();
|
||||||
pp.push_back(p);
|
pp.push_back(p);
|
||||||
}
|
}
|
||||||
simplify_polygons(pp, &pp);
|
simplify_polygons(pp, &pp);
|
||||||
|
@ -167,9 +173,11 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
||||||
Slic3r::Geometry::MedialAxis ma(max_width, min_width);
|
Slic3r::Geometry::MedialAxis ma(max_width, min_width);
|
||||||
|
|
||||||
// populate list of segments for the Voronoi diagram
|
// populate list of segments for the Voronoi diagram
|
||||||
this->contour.lines(&ma.lines);
|
ma.lines = this->contour.lines();
|
||||||
for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole)
|
for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) {
|
||||||
hole->lines(&ma.lines);
|
Lines lines = hole->lines();
|
||||||
|
ma.lines.insert(ma.lines.end(), lines.begin(), lines.end());
|
||||||
|
}
|
||||||
|
|
||||||
// compute the Voronoi diagram
|
// compute the Voronoi diagram
|
||||||
ma.build(polylines);
|
ma.build(polylines);
|
||||||
|
@ -384,10 +392,11 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||||
Lines
|
Lines
|
||||||
ExPolygon::lines() const
|
ExPolygon::lines() const
|
||||||
{
|
{
|
||||||
Lines lines;
|
Lines lines = this->contour.lines();
|
||||||
this->contour.lines(&lines);
|
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
||||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h)
|
Lines hole_lines = h->lines();
|
||||||
h->lines(&lines);
|
lines.insert(lines.end(), hole_lines.begin(), hole_lines.end());
|
||||||
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,13 +92,13 @@ ExPolygonCollection::simplify(double tolerance)
|
||||||
this->expolygons = expp;
|
this->expolygons = expp;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polygon
|
||||||
ExPolygonCollection::convex_hull(Polygon* hull) const
|
ExPolygonCollection::convex_hull() const
|
||||||
{
|
{
|
||||||
Points pp;
|
Points pp;
|
||||||
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it)
|
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it)
|
||||||
pp.insert(pp.end(), it->contour.points.begin(), it->contour.points.end());
|
pp.insert(pp.end(), it->contour.points.begin(), it->contour.points.end());
|
||||||
Slic3r::Geometry::convex_hull(pp, hull);
|
return Slic3r::Geometry::convex_hull(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
Lines
|
Lines
|
||||||
|
|
|
@ -28,7 +28,7 @@ class ExPolygonCollection
|
||||||
template <class T> bool contains(const T &item) const;
|
template <class T> bool contains(const T &item) const;
|
||||||
bool contains_b(const Point &point) const;
|
bool contains_b(const Point &point) const;
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
void convex_hull(Polygon* hull) const;
|
Polygon convex_hull() const;
|
||||||
Lines lines() const;
|
Lines lines() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -164,11 +164,12 @@ ExtrusionPath::gcode(Extruder* extruder, double e, double F,
|
||||||
return stream.str();
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtrusionLoop::operator Polygon() const
|
Polygons
|
||||||
|
ExtrusionPath::grow() const
|
||||||
{
|
{
|
||||||
Polygon polygon;
|
Polygons pp;
|
||||||
this->polygon(&polygon);
|
offset(this->polyline, &pp, +this->width/2);
|
||||||
return polygon;
|
return pp;
|
||||||
}
|
}
|
||||||
|
|
||||||
ExtrusionLoop*
|
ExtrusionLoop*
|
||||||
|
@ -180,8 +181,7 @@ ExtrusionLoop::clone() const
|
||||||
bool
|
bool
|
||||||
ExtrusionLoop::make_clockwise()
|
ExtrusionLoop::make_clockwise()
|
||||||
{
|
{
|
||||||
Polygon polygon = *this;
|
bool was_ccw = this->polygon().is_counter_clockwise();
|
||||||
bool was_ccw = polygon.is_counter_clockwise();
|
|
||||||
if (was_ccw) this->reverse();
|
if (was_ccw) this->reverse();
|
||||||
return was_ccw;
|
return was_ccw;
|
||||||
}
|
}
|
||||||
|
@ -189,8 +189,7 @@ ExtrusionLoop::make_clockwise()
|
||||||
bool
|
bool
|
||||||
ExtrusionLoop::make_counter_clockwise()
|
ExtrusionLoop::make_counter_clockwise()
|
||||||
{
|
{
|
||||||
Polygon polygon = *this;
|
bool was_cw = this->polygon().is_clockwise();
|
||||||
bool was_cw = polygon.is_clockwise();
|
|
||||||
if (was_cw) this->reverse();
|
if (was_cw) this->reverse();
|
||||||
return was_cw;
|
return was_cw;
|
||||||
}
|
}
|
||||||
|
@ -215,13 +214,15 @@ ExtrusionLoop::last_point() const
|
||||||
return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way
|
return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polygon
|
||||||
ExtrusionLoop::polygon(Polygon* polygon) const
|
ExtrusionLoop::polygon() const
|
||||||
{
|
{
|
||||||
|
Polygon polygon;
|
||||||
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
||||||
// for each polyline, append all points except the last one (because it coincides with the first one of the next polyline)
|
// for each polyline, append all points except the last one (because it coincides with the first one of the next polyline)
|
||||||
polygon->points.insert(polygon->points.end(), path->polyline.points.begin(), path->polyline.points.end()-1);
|
polygon.points.insert(polygon.points.end(), path->polyline.points.begin(), path->polyline.points.end()-1);
|
||||||
}
|
}
|
||||||
|
return polygon;
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
|
@ -363,6 +364,17 @@ ExtrusionLoop::is_solid_infill() const
|
||||||
|| this->paths.front().role == erTopSolidInfill;
|
|| this->paths.front().role == erTopSolidInfill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygons
|
||||||
|
ExtrusionLoop::grow() const
|
||||||
|
{
|
||||||
|
Polygons pp;
|
||||||
|
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
|
||||||
|
Polygons path_pp = path->grow();
|
||||||
|
pp.insert(pp.end(), path_pp.begin(), path_pp.end());
|
||||||
|
}
|
||||||
|
return pp;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -43,6 +43,7 @@ class ExtrusionEntity
|
||||||
virtual void reverse() = 0;
|
virtual void reverse() = 0;
|
||||||
virtual Point first_point() const = 0;
|
virtual Point first_point() const = 0;
|
||||||
virtual Point last_point() const = 0;
|
virtual Point last_point() const = 0;
|
||||||
|
virtual Polygons grow() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
|
||||||
|
@ -73,6 +74,7 @@ class ExtrusionPath : public ExtrusionEntity
|
||||||
std::string gcode(Extruder* extruder, double e, double F,
|
std::string gcode(Extruder* extruder, double e, double F,
|
||||||
double xofs, double yofs, std::string extrusion_axis,
|
double xofs, double yofs, std::string extrusion_axis,
|
||||||
std::string gcode_line_suffix) const;
|
std::string gcode_line_suffix) const;
|
||||||
|
Polygons grow() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
|
||||||
|
@ -90,14 +92,13 @@ class ExtrusionLoop : public ExtrusionEntity
|
||||||
bool is_loop() const {
|
bool is_loop() const {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
operator Polygon() const;
|
|
||||||
ExtrusionLoop* clone() const;
|
ExtrusionLoop* clone() const;
|
||||||
bool make_clockwise();
|
bool make_clockwise();
|
||||||
bool make_counter_clockwise();
|
bool make_counter_clockwise();
|
||||||
void reverse();
|
void reverse();
|
||||||
Point first_point() const;
|
Point first_point() const;
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
void polygon(Polygon* polygon) const;
|
Polygon polygon() const;
|
||||||
double length() const;
|
double length() const;
|
||||||
bool split_at_vertex(const Point &point);
|
bool split_at_vertex(const Point &point);
|
||||||
void split_at(const Point &point);
|
void split_at(const Point &point);
|
||||||
|
@ -106,6 +107,7 @@ class ExtrusionLoop : public ExtrusionEntity
|
||||||
bool is_perimeter() const;
|
bool is_perimeter() const;
|
||||||
bool is_infill() const;
|
bool is_infill() const;
|
||||||
bool is_solid_infill() const;
|
bool is_solid_infill() const;
|
||||||
|
Polygons grow() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,6 +110,17 @@ ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygons
|
||||||
|
ExtrusionEntityCollection::grow() const
|
||||||
|
{
|
||||||
|
Polygons pp;
|
||||||
|
for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) {
|
||||||
|
Polygons entity_pp = (*it)->grow();
|
||||||
|
pp.insert(pp.end(), entity_pp.begin(), entity_pp.end());
|
||||||
|
}
|
||||||
|
return pp;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
// there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection
|
// there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection
|
||||||
REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
|
REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
|
||||||
|
|
|
@ -22,6 +22,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity
|
||||||
void reverse();
|
void reverse();
|
||||||
Point first_point() const;
|
Point first_point() const;
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
|
Polygons grow() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool
|
||||||
if (height <= 0 && !bridge) CONFESS("Invalid flow height supplied to new_from_spacing()");
|
if (height <= 0 && !bridge) CONFESS("Invalid flow height supplied to new_from_spacing()");
|
||||||
|
|
||||||
float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge);
|
float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge);
|
||||||
|
if (bridge) height = w;
|
||||||
return Flow(w, height, nozzle_diameter, bridge);
|
return Flow(w, height, nozzle_diameter, bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,16 +60,6 @@ GCodeWriter::preamble()
|
||||||
} else {
|
} else {
|
||||||
gcode << "M82 ; use absolute distances for extrusion\n";
|
gcode << "M82 ; use absolute distances for extrusion\n";
|
||||||
}
|
}
|
||||||
if (this->config.use_volumetric_e && this->config.start_gcode.value.find("M200") == std::string::npos) {
|
|
||||||
for (std::map<unsigned int,Extruder>::const_iterator it = this->extruders.begin(); it != this->extruders.end(); ++it) {
|
|
||||||
unsigned int extruder_id = it->first;
|
|
||||||
gcode << "M200 D" << E_NUM(this->config.filament_diameter.get_at(extruder_id));
|
|
||||||
if (this->multiple_extruders || extruder_id != 0) {
|
|
||||||
gcode << " T" << extruder_id;
|
|
||||||
}
|
|
||||||
gcode << " ; set filament diameter\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gcode << this->reset_e(true);
|
gcode << this->reset_e(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
248
xs/src/libslic3r/GUI/3DScene.cpp
Normal file
248
xs/src/libslic3r/GUI/3DScene.cpp
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
#include "3DScene.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// caller is responsible for supplying NO lines with zero length
|
||||||
|
void
|
||||||
|
_3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector<double> &widths,
|
||||||
|
const std::vector<double> &heights, bool closed, double top_z, const Point ©,
|
||||||
|
GLVertexArray* qverts, GLVertexArray* tverts)
|
||||||
|
{
|
||||||
|
/* It looks like it's faster without reserving capacity...
|
||||||
|
// each segment has 4 quads, thus 16 vertices; + 2 caps
|
||||||
|
qverts->reserve_more(3 * 4 * (4 * lines.size() + 2));
|
||||||
|
|
||||||
|
// two triangles for each corner
|
||||||
|
tverts->reserve_more(3 * 3 * 2 * (lines.size() + 1));
|
||||||
|
*/
|
||||||
|
|
||||||
|
Line prev_line;
|
||||||
|
Pointf prev_b1, prev_b2;
|
||||||
|
Vectorf3 prev_xy_left_normal, prev_xy_right_normal;
|
||||||
|
|
||||||
|
// loop once more in case of closed loops
|
||||||
|
bool first_done = false;
|
||||||
|
for (size_t i = 0; i <= lines.size(); ++i) {
|
||||||
|
if (i == lines.size()) i = 0;
|
||||||
|
|
||||||
|
const Line &line = lines.at(i);
|
||||||
|
if (i == 0 && first_done && !closed) break;
|
||||||
|
|
||||||
|
double len = line.length();
|
||||||
|
double unscaled_len = unscale(len);
|
||||||
|
|
||||||
|
double bottom_z = top_z - heights.at(i);
|
||||||
|
double middle_z = (top_z + bottom_z) / 2;
|
||||||
|
double dist = widths.at(i)/2; // scaled
|
||||||
|
|
||||||
|
Vectorf v = Vectorf::new_unscale(line.vector());
|
||||||
|
v.scale(1/unscaled_len);
|
||||||
|
|
||||||
|
Pointf a = Pointf::new_unscale(line.a);
|
||||||
|
Pointf b = Pointf::new_unscale(line.b);
|
||||||
|
Pointf a1 = a;
|
||||||
|
Pointf a2 = a;
|
||||||
|
a1.translate(+dist*v.y, -dist*v.x);
|
||||||
|
a2.translate(-dist*v.y, +dist*v.x);
|
||||||
|
Pointf b1 = b;
|
||||||
|
Pointf b2 = b;
|
||||||
|
b1.translate(+dist*v.y, -dist*v.x);
|
||||||
|
b2.translate(-dist*v.y, +dist*v.x);
|
||||||
|
|
||||||
|
// calculate new XY normals
|
||||||
|
Vector n = line.normal();
|
||||||
|
Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0);
|
||||||
|
xy_right_normal.scale(1/unscaled_len);
|
||||||
|
Vectorf3 xy_left_normal = xy_right_normal;
|
||||||
|
xy_left_normal.scale(-1);
|
||||||
|
|
||||||
|
if (first_done) {
|
||||||
|
// if we're making a ccw turn, draw the triangles on the right side, otherwise draw them on the left side
|
||||||
|
double ccw = line.b.ccw(prev_line);
|
||||||
|
if (ccw > EPSILON) {
|
||||||
|
// top-right vertex triangle between previous line and this one
|
||||||
|
{
|
||||||
|
// use the normal going to the right calculated for the previous line
|
||||||
|
tverts->push_norm(prev_xy_right_normal);
|
||||||
|
tverts->push_vert(prev_b1.x, prev_b1.y, middle_z);
|
||||||
|
|
||||||
|
// use the normal going to the right calculated for this line
|
||||||
|
tverts->push_norm(xy_right_normal);
|
||||||
|
tverts->push_vert(a1.x, a1.y, middle_z);
|
||||||
|
|
||||||
|
// normal going upwards
|
||||||
|
tverts->push_norm(0,0,1);
|
||||||
|
tverts->push_vert(a.x, a.y, top_z);
|
||||||
|
}
|
||||||
|
// bottom-right vertex triangle between previous line and this one
|
||||||
|
{
|
||||||
|
// use the normal going to the right calculated for the previous line
|
||||||
|
tverts->push_norm(prev_xy_right_normal);
|
||||||
|
tverts->push_vert(prev_b1.x, prev_b1.y, middle_z);
|
||||||
|
|
||||||
|
// normal going downwards
|
||||||
|
tverts->push_norm(0,0,-1);
|
||||||
|
tverts->push_vert(a.x, a.y, bottom_z);
|
||||||
|
|
||||||
|
// use the normal going to the right calculated for this line
|
||||||
|
tverts->push_norm(xy_right_normal);
|
||||||
|
tverts->push_vert(a1.x, a1.y, middle_z);
|
||||||
|
}
|
||||||
|
} else if (ccw < -EPSILON) {
|
||||||
|
// top-left vertex triangle between previous line and this one
|
||||||
|
{
|
||||||
|
// use the normal going to the left calculated for the previous line
|
||||||
|
tverts->push_norm(prev_xy_left_normal);
|
||||||
|
tverts->push_vert(prev_b2.x, prev_b2.y, middle_z);
|
||||||
|
|
||||||
|
// normal going upwards
|
||||||
|
tverts->push_norm(0,0,1);
|
||||||
|
tverts->push_vert(a.x, a.y, top_z);
|
||||||
|
|
||||||
|
// use the normal going to the right calculated for this line
|
||||||
|
tverts->push_norm(xy_left_normal);
|
||||||
|
tverts->push_vert(a2.x, a2.y, middle_z);
|
||||||
|
}
|
||||||
|
// bottom-left vertex triangle between previous line and this one
|
||||||
|
{
|
||||||
|
// use the normal going to the left calculated for the previous line
|
||||||
|
tverts->push_norm(prev_xy_left_normal);
|
||||||
|
tverts->push_vert(prev_b2.x, prev_b2.y, middle_z);
|
||||||
|
|
||||||
|
// use the normal going to the right calculated for this line
|
||||||
|
tverts->push_norm(xy_left_normal);
|
||||||
|
tverts->push_vert(a2.x, a2.y, middle_z);
|
||||||
|
|
||||||
|
// normal going downwards
|
||||||
|
tverts->push_norm(0,0,-1);
|
||||||
|
tverts->push_vert(a.x, a.y, bottom_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if this was the extra iteration we were only interested in the triangles
|
||||||
|
if (first_done && i == 0) break;
|
||||||
|
|
||||||
|
prev_line = line;
|
||||||
|
prev_b1 = b1;
|
||||||
|
prev_b2 = b2;
|
||||||
|
prev_xy_right_normal = xy_right_normal;
|
||||||
|
prev_xy_left_normal = xy_left_normal;
|
||||||
|
|
||||||
|
if (!closed) {
|
||||||
|
// terminate open paths with caps
|
||||||
|
if (i == 0) {
|
||||||
|
// normal pointing downwards
|
||||||
|
qverts->push_norm(0,0,-1);
|
||||||
|
qverts->push_vert(a.x, a.y, bottom_z);
|
||||||
|
|
||||||
|
// normal pointing to the right
|
||||||
|
qverts->push_norm(xy_right_normal);
|
||||||
|
qverts->push_vert(a1.x, a1.y, middle_z);
|
||||||
|
|
||||||
|
// normal pointing upwards
|
||||||
|
qverts->push_norm(0,0,1);
|
||||||
|
qverts->push_vert(a.x, a.y, top_z);
|
||||||
|
|
||||||
|
// normal pointing to the left
|
||||||
|
qverts->push_norm(xy_left_normal);
|
||||||
|
qverts->push_vert(a2.x, a2.y, middle_z);
|
||||||
|
}
|
||||||
|
// we don't use 'else' because both cases are true if we have only one line
|
||||||
|
if (i == lines.size()-1) {
|
||||||
|
// normal pointing downwards
|
||||||
|
qverts->push_norm(0,0,-1);
|
||||||
|
qverts->push_vert(b.x, b.y, bottom_z);
|
||||||
|
|
||||||
|
// normal pointing to the left
|
||||||
|
qverts->push_norm(xy_left_normal);
|
||||||
|
qverts->push_vert(b2.x, b2.y, middle_z);
|
||||||
|
|
||||||
|
// normal pointing upwards
|
||||||
|
qverts->push_norm(0,0,1);
|
||||||
|
qverts->push_vert(b.x, b.y, top_z);
|
||||||
|
|
||||||
|
// normal pointing to the right
|
||||||
|
qverts->push_norm(xy_right_normal);
|
||||||
|
qverts->push_vert(b1.x, b1.y, middle_z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom-right face
|
||||||
|
{
|
||||||
|
// normal going downwards
|
||||||
|
qverts->push_norm(0,0,-1);
|
||||||
|
qverts->push_norm(0,0,-1);
|
||||||
|
qverts->push_vert(a.x, a.y, bottom_z);
|
||||||
|
qverts->push_vert(b.x, b.y, bottom_z);
|
||||||
|
|
||||||
|
qverts->push_norm(xy_right_normal);
|
||||||
|
qverts->push_norm(xy_right_normal);
|
||||||
|
qverts->push_vert(b1.x, b1.y, middle_z);
|
||||||
|
qverts->push_vert(a1.x, a1.y, middle_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// top-right face
|
||||||
|
{
|
||||||
|
qverts->push_norm(xy_right_normal);
|
||||||
|
qverts->push_norm(xy_right_normal);
|
||||||
|
qverts->push_vert(a1.x, a1.y, middle_z);
|
||||||
|
qverts->push_vert(b1.x, b1.y, middle_z);
|
||||||
|
|
||||||
|
// normal going upwards
|
||||||
|
qverts->push_norm(0,0,1);
|
||||||
|
qverts->push_norm(0,0,1);
|
||||||
|
qverts->push_vert(b.x, b.y, top_z);
|
||||||
|
qverts->push_vert(a.x, a.y, top_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// top-left face
|
||||||
|
{
|
||||||
|
qverts->push_norm(0,0,1);
|
||||||
|
qverts->push_norm(0,0,1);
|
||||||
|
qverts->push_vert(a.x, a.y, top_z);
|
||||||
|
qverts->push_vert(b.x, b.y, top_z);
|
||||||
|
|
||||||
|
qverts->push_norm(xy_left_normal);
|
||||||
|
qverts->push_norm(xy_left_normal);
|
||||||
|
qverts->push_vert(b2.x, b2.y, middle_z);
|
||||||
|
qverts->push_vert(a2.x, a2.y, middle_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom-left face
|
||||||
|
{
|
||||||
|
qverts->push_norm(xy_left_normal);
|
||||||
|
qverts->push_norm(xy_left_normal);
|
||||||
|
qverts->push_vert(a2.x, a2.y, middle_z);
|
||||||
|
qverts->push_vert(b2.x, b2.y, middle_z);
|
||||||
|
|
||||||
|
// normal going downwards
|
||||||
|
qverts->push_norm(0,0,-1);
|
||||||
|
qverts->push_norm(0,0,-1);
|
||||||
|
qverts->push_vert(b.x, b.y, bottom_z);
|
||||||
|
qverts->push_vert(a.x, a.y, bottom_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
first_done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GLVertexArray::load_mesh(const TriangleMesh &mesh)
|
||||||
|
{
|
||||||
|
this->reserve_more(3 * 3 * mesh.facets_count());
|
||||||
|
|
||||||
|
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) {
|
||||||
|
stl_facet &facet = mesh.stl.facet_start[i];
|
||||||
|
for (int j = 0; j <= 2; ++j) {
|
||||||
|
this->push_norm(facet.normal.x, facet.normal.y, facet.normal.z);
|
||||||
|
this->push_vert(facet.vertex[j].x, facet.vertex[j].y, facet.vertex[j].z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SLIC3RXS
|
||||||
|
REGISTER_CLASS(GLVertexArray, "GUI::_3DScene::GLVertexArray");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
56
xs/src/libslic3r/GUI/3DScene.hpp
Normal file
56
xs/src/libslic3r/GUI/3DScene.hpp
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef slic3r_3DScene_hpp_
|
||||||
|
#define slic3r_3DScene_hpp_
|
||||||
|
|
||||||
|
#include <myinit.h>
|
||||||
|
#include "../Point.hpp"
|
||||||
|
#include "../Line.hpp"
|
||||||
|
#include "../TriangleMesh.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
class GLVertexArray {
|
||||||
|
public:
|
||||||
|
std::vector<float> verts, norms;
|
||||||
|
|
||||||
|
void reserve(size_t len) {
|
||||||
|
this->verts.reserve(len);
|
||||||
|
this->norms.reserve(len);
|
||||||
|
};
|
||||||
|
void reserve_more(size_t len) {
|
||||||
|
len += this->verts.size();
|
||||||
|
this->reserve(len);
|
||||||
|
};
|
||||||
|
void push_vert(const Pointf3 &point) {
|
||||||
|
this->verts.push_back(point.x);
|
||||||
|
this->verts.push_back(point.y);
|
||||||
|
this->verts.push_back(point.z);
|
||||||
|
};
|
||||||
|
void push_vert(float x, float y, float z) {
|
||||||
|
this->verts.push_back(x);
|
||||||
|
this->verts.push_back(y);
|
||||||
|
this->verts.push_back(z);
|
||||||
|
};
|
||||||
|
void push_norm(const Pointf3 &point) {
|
||||||
|
this->norms.push_back(point.x);
|
||||||
|
this->norms.push_back(point.y);
|
||||||
|
this->norms.push_back(point.z);
|
||||||
|
};
|
||||||
|
void push_norm(float x, float y, float z) {
|
||||||
|
this->norms.push_back(x);
|
||||||
|
this->norms.push_back(y);
|
||||||
|
this->norms.push_back(z);
|
||||||
|
};
|
||||||
|
void load_mesh(const TriangleMesh &mesh);
|
||||||
|
};
|
||||||
|
|
||||||
|
class _3DScene
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void _extrusionentity_to_verts_do(const Lines &lines, const std::vector<double> &widths,
|
||||||
|
const std::vector<double> &heights, bool closed, double top_z, const Point ©,
|
||||||
|
GLVertexArray* qverts, GLVertexArray* tverts);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,4 +1,5 @@
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
|
#include "ClipperUtils.hpp"
|
||||||
#include "ExPolygon.hpp"
|
#include "ExPolygon.hpp"
|
||||||
#include "Line.hpp"
|
#include "Line.hpp"
|
||||||
#include "PolylineCollection.hpp"
|
#include "PolylineCollection.hpp"
|
||||||
|
@ -25,42 +26,45 @@ sort_points (Point a, Point b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
|
/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
|
||||||
void
|
Polygon
|
||||||
convex_hull(Points points, Polygon* hull)
|
convex_hull(Points points)
|
||||||
{
|
{
|
||||||
assert(points.size() >= 3);
|
assert(points.size() >= 3);
|
||||||
// sort input points
|
// sort input points
|
||||||
std::sort(points.begin(), points.end(), sort_points);
|
std::sort(points.begin(), points.end(), sort_points);
|
||||||
|
|
||||||
int n = points.size(), k = 0;
|
int n = points.size(), k = 0;
|
||||||
hull->points.resize(2*n);
|
Polygon hull;
|
||||||
|
hull.points.resize(2*n);
|
||||||
|
|
||||||
// Build lower hull
|
// Build lower hull
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
while (k >= 2 && points[i].ccw(hull->points[k-2], hull->points[k-1]) <= 0) k--;
|
while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
|
||||||
hull->points[k++] = points[i];
|
hull.points[k++] = points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build upper hull
|
// Build upper hull
|
||||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||||
while (k >= t && points[i].ccw(hull->points[k-2], hull->points[k-1]) <= 0) k--;
|
while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
|
||||||
hull->points[k++] = points[i];
|
hull.points[k++] = points[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
hull->points.resize(k);
|
hull.points.resize(k);
|
||||||
|
|
||||||
assert( hull->points.front().coincides_with(hull->points.back()) );
|
assert( hull.points.front().coincides_with(hull.points.back()) );
|
||||||
hull->points.pop_back();
|
hull.points.pop_back();
|
||||||
|
|
||||||
|
return hull;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polygon
|
||||||
convex_hull(const Polygons &polygons, Polygon* hull)
|
convex_hull(const Polygons &polygons)
|
||||||
{
|
{
|
||||||
Points pp;
|
Points pp;
|
||||||
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
|
||||||
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
pp.insert(pp.end(), p->points.begin(), p->points.end());
|
||||||
}
|
}
|
||||||
convex_hull(pp, hull);
|
return convex_hull(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* accepts an arrayref of points and returns a list of indices
|
/* accepts an arrayref of points and returns a list of indices
|
||||||
|
@ -143,6 +147,20 @@ deg2rad(double angle)
|
||||||
return PI * angle / 180.0;
|
return PI * angle / 180.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval)
|
||||||
|
{
|
||||||
|
Polygons pp;
|
||||||
|
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) {
|
||||||
|
Polygon p = *it;
|
||||||
|
p.points.push_back(p.points.front());
|
||||||
|
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
|
||||||
|
p.points.pop_back();
|
||||||
|
pp.push_back(p);
|
||||||
|
}
|
||||||
|
Slic3r::simplify_polygons(pp, retval);
|
||||||
|
}
|
||||||
|
|
||||||
Line
|
Line
|
||||||
MedialAxis::edge_to_line(const VD::edge_type &edge) const
|
MedialAxis::edge_to_line(const VD::edge_type &edge) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,8 +11,8 @@ using boost::polygon::voronoi_diagram;
|
||||||
|
|
||||||
namespace Slic3r { namespace Geometry {
|
namespace Slic3r { namespace Geometry {
|
||||||
|
|
||||||
void convex_hull(Points points, Polygon* hull);
|
Polygon convex_hull(Points points);
|
||||||
void convex_hull(const Polygons &polygons, Polygon* hull);
|
Polygon convex_hull(const Polygons &polygons);
|
||||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
|
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
|
||||||
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
|
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
|
||||||
template<class T> void chained_path_items(Points &points, T &items, T &retval);
|
template<class T> void chained_path_items(Points &points, T &items, T &retval);
|
||||||
|
@ -21,6 +21,7 @@ template<class T> bool contains(const std::vector<T> &vector, const Point &point
|
||||||
double rad2deg(double angle);
|
double rad2deg(double angle);
|
||||||
double rad2deg_dir(double angle);
|
double rad2deg_dir(double angle);
|
||||||
double deg2rad(double angle);
|
double deg2rad(double angle);
|
||||||
|
void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval);
|
||||||
|
|
||||||
class MedialAxis {
|
class MedialAxis {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
Layer::Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z,
|
Layer::Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
|
||||||
coordf_t slice_z)
|
coordf_t slice_z)
|
||||||
: _id(id),
|
: _id(id),
|
||||||
_object(object),
|
_object(object),
|
||||||
|
@ -35,12 +35,18 @@ Layer::~Layer()
|
||||||
this->clear_regions();
|
this->clear_regions();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
size_t
|
||||||
Layer::id()
|
Layer::id() const
|
||||||
{
|
{
|
||||||
return this->_id;
|
return this->_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Layer::set_id(size_t id)
|
||||||
|
{
|
||||||
|
this->_id = id;
|
||||||
|
}
|
||||||
|
|
||||||
PrintObject*
|
PrintObject*
|
||||||
Layer::object()
|
Layer::object()
|
||||||
{
|
{
|
||||||
|
@ -147,7 +153,7 @@ REGISTER_CLASS(Layer, "Layer");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
SupportLayer::SupportLayer(int id, PrintObject *object, coordf_t height,
|
SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height,
|
||||||
coordf_t print_z, coordf_t slice_z)
|
coordf_t print_z, coordf_t slice_z)
|
||||||
: Layer(id, object, height, print_z, slice_z)
|
: Layer(id, object, height, print_z, slice_z)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,6 +56,7 @@ class LayerRegion
|
||||||
|
|
||||||
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
|
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
|
||||||
void merge_slices();
|
void merge_slices();
|
||||||
|
void prepare_fill_surfaces();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Layer *_layer;
|
Layer *_layer;
|
||||||
|
@ -72,7 +73,8 @@ class Layer {
|
||||||
friend class PrintObject;
|
friend class PrintObject;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
int id();
|
size_t id() const;
|
||||||
|
void set_id(size_t id);
|
||||||
PrintObject* object();
|
PrintObject* object();
|
||||||
|
|
||||||
Layer *upper_layer;
|
Layer *upper_layer;
|
||||||
|
@ -97,11 +99,11 @@ class Layer {
|
||||||
template <class T> bool any_bottom_region_slice_contains(const T &item) const;
|
template <class T> bool any_bottom_region_slice_contains(const T &item) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int _id; // sequential number of layer, 0-based
|
size_t _id; // sequential number of layer, 0-based
|
||||||
PrintObject *_object;
|
PrintObject *_object;
|
||||||
|
|
||||||
|
|
||||||
Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z,
|
Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
|
||||||
coordf_t slice_z);
|
coordf_t slice_z);
|
||||||
virtual ~Layer();
|
virtual ~Layer();
|
||||||
|
|
||||||
|
@ -119,7 +121,7 @@ class SupportLayer : public Layer {
|
||||||
ExtrusionEntityCollection support_interface_fills;
|
ExtrusionEntityCollection support_interface_fills;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SupportLayer(int id, PrintObject *object, coordf_t height, coordf_t print_z,
|
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z,
|
||||||
coordf_t slice_z);
|
coordf_t slice_z);
|
||||||
virtual ~SupportLayer();
|
virtual ~SupportLayer();
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,6 +53,38 @@ LayerRegion::merge_slices()
|
||||||
this->slices.surfaces.push_back(Surface(stInternal, *expoly));
|
this->slices.surfaces.push_back(Surface(stInternal, *expoly));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LayerRegion::prepare_fill_surfaces()
|
||||||
|
{
|
||||||
|
/* Note: in order to make the psPrepareInfill step idempotent, we should never
|
||||||
|
alter fill_surfaces boundaries on which our idempotency relies since that's
|
||||||
|
the only meaningful information returned by psPerimeters. */
|
||||||
|
|
||||||
|
// if no solid layers are requested, turn top/bottom surfaces to internal
|
||||||
|
if (this->_region->config.top_solid_layers == 0) {
|
||||||
|
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
|
||||||
|
if (surface->surface_type == stTop)
|
||||||
|
surface->surface_type = stInternal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this->_region->config.bottom_solid_layers == 0) {
|
||||||
|
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
|
||||||
|
if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge)
|
||||||
|
surface->surface_type = stInternal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn too small internal regions into solid regions according to the user setting
|
||||||
|
if (this->_region->config.fill_density.value > 0) {
|
||||||
|
// scaling an area requires two calls!
|
||||||
|
double min_area = scale_(scale_(this->_region->config.solid_infill_below_area.value));
|
||||||
|
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
|
||||||
|
if (surface->surface_type == stInternal && surface->area() <= min_area)
|
||||||
|
surface->surface_type = stInternalSolid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
REGISTER_CLASS(LayerRegion, "Layer::Region");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -64,10 +64,10 @@ Line::length() const
|
||||||
return this->a.distance_to(this->b);
|
return this->a.distance_to(this->b);
|
||||||
}
|
}
|
||||||
|
|
||||||
Point*
|
Point
|
||||||
Line::midpoint() const
|
Line::midpoint() const
|
||||||
{
|
{
|
||||||
return new Point ((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0);
|
return Point((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -89,6 +89,23 @@ Line::point_at(double distance) const
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Line::intersection_infinite(const Line &other, Point* point) const
|
||||||
|
{
|
||||||
|
Vector x = this->a.vector_to(other.a);
|
||||||
|
Vector d1 = this->vector();
|
||||||
|
Vector d2 = other.vector();
|
||||||
|
|
||||||
|
double cross = d1.x * d2.y - d1.y * d2.x;
|
||||||
|
if (std::fabs(cross) < EPSILON)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double t1 = (x.x * d2.y - x.y * d2.x)/cross;
|
||||||
|
point->x = this->a.x + d1.x * t1;
|
||||||
|
point->y = this->a.y + d1.y * t1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Line::coincides_with(const Line &line) const
|
Line::coincides_with(const Line &line) const
|
||||||
{
|
{
|
||||||
|
@ -201,6 +218,13 @@ Linef3::intersect_plane(double z) const
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Linef3::scale(double factor)
|
||||||
|
{
|
||||||
|
this->a.scale(factor);
|
||||||
|
this->b.scale(factor);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
REGISTER_CLASS(Linef3, "Linef3");
|
REGISTER_CLASS(Linef3, "Linef3");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,9 +26,10 @@ class Line
|
||||||
void rotate(double angle, const Point ¢er);
|
void rotate(double angle, const Point ¢er);
|
||||||
void reverse();
|
void reverse();
|
||||||
double length() const;
|
double length() const;
|
||||||
Point* midpoint() const;
|
Point midpoint() const;
|
||||||
void point_at(double distance, Point* point) const;
|
void point_at(double distance, Point* point) const;
|
||||||
Point point_at(double distance) const;
|
Point point_at(double distance) const;
|
||||||
|
bool intersection_infinite(const Line &other, Point* point) const;
|
||||||
bool coincides_with(const Line &line) const;
|
bool coincides_with(const Line &line) const;
|
||||||
double distance_to(const Point &point) const;
|
double distance_to(const Point &point) const;
|
||||||
bool parallel_to(double angle) const;
|
bool parallel_to(double angle) const;
|
||||||
|
@ -47,6 +48,15 @@ class Line
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Linef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pointf a;
|
||||||
|
Pointf b;
|
||||||
|
Linef() {};
|
||||||
|
explicit Linef(Pointf _a, Pointf _b): a(_a), b(_b) {};
|
||||||
|
};
|
||||||
|
|
||||||
class Linef3
|
class Linef3
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -55,6 +65,7 @@ class Linef3
|
||||||
Linef3() {};
|
Linef3() {};
|
||||||
explicit Linef3(Pointf3 _a, Pointf3 _b): a(_a), b(_b) {};
|
explicit Linef3(Pointf3 _a, Pointf3 _b): a(_a), b(_b) {};
|
||||||
Pointf3 intersect_plane(double z) const;
|
Pointf3 intersect_plane(double z) const;
|
||||||
|
void scale(double factor);
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
void from_SV(SV* line_sv);
|
void from_SV(SV* line_sv);
|
||||||
|
|
|
@ -130,8 +130,7 @@ Model::duplicate_objects_grid(unsigned int x, unsigned int y, coordf_t distance)
|
||||||
ModelObject* object = this->objects.front();
|
ModelObject* object = this->objects.front();
|
||||||
object->clear_instances();
|
object->clear_instances();
|
||||||
|
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb = object->bounding_box();
|
||||||
object->bounding_box(&bb);
|
|
||||||
Sizef3 size = bb.size();
|
Sizef3 size = bb.size();
|
||||||
|
|
||||||
for (unsigned int x_copy = 1; x_copy <= x; ++x_copy) {
|
for (unsigned int x_copy = 1; x_copy <= x; ++x_copy) {
|
||||||
|
@ -174,21 +173,20 @@ Model::add_default_instances()
|
||||||
}
|
}
|
||||||
|
|
||||||
// this returns the bounding box of the *transformed* instances
|
// this returns the bounding box of the *transformed* instances
|
||||||
void
|
BoundingBoxf3
|
||||||
Model::bounding_box(BoundingBoxf3* bb)
|
Model::bounding_box() const
|
||||||
{
|
{
|
||||||
|
BoundingBoxf3 bb;
|
||||||
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
||||||
BoundingBoxf3 obb;
|
bb.merge((*o)->bounding_box());
|
||||||
(*o)->bounding_box(&obb);
|
|
||||||
bb->merge(obb);
|
|
||||||
}
|
}
|
||||||
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Model::center_instances_around_point(const Pointf &point)
|
Model::center_instances_around_point(const Pointf &point)
|
||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb = this->bounding_box();
|
||||||
this->bounding_box(&bb);
|
|
||||||
|
|
||||||
Sizef3 size = bb.size();
|
Sizef3 size = bb.size();
|
||||||
double shift_x = -bb.min.x + point.x - size.x/2;
|
double shift_x = -bb.min.x + point.x - size.x/2;
|
||||||
|
@ -205,8 +203,7 @@ Model::center_instances_around_point(const Pointf &point)
|
||||||
void
|
void
|
||||||
Model::align_instances_to_origin()
|
Model::align_instances_to_origin()
|
||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb = this->bounding_box();
|
||||||
this->bounding_box(&bb);
|
|
||||||
|
|
||||||
Pointf new_center = (Pointf)bb.size();
|
Pointf new_center = (Pointf)bb.size();
|
||||||
new_center.translate(-new_center.x/2, -new_center.y/2);
|
new_center.translate(-new_center.x/2, -new_center.y/2);
|
||||||
|
@ -222,25 +219,25 @@ Model::translate(coordf_t x, coordf_t y, coordf_t z)
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattens everything to a single mesh
|
// flattens everything to a single mesh
|
||||||
void
|
TriangleMesh
|
||||||
Model::mesh(TriangleMesh* mesh) const
|
Model::mesh() const
|
||||||
{
|
{
|
||||||
|
TriangleMesh mesh;
|
||||||
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
||||||
TriangleMesh omesh;
|
mesh.merge((*o)->mesh());
|
||||||
(*o)->mesh(&omesh);
|
|
||||||
mesh->merge(omesh);
|
|
||||||
}
|
}
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattens everything to a single mesh
|
// flattens everything to a single mesh
|
||||||
void
|
TriangleMesh
|
||||||
Model::raw_mesh(TriangleMesh* mesh) const
|
Model::raw_mesh() const
|
||||||
{
|
{
|
||||||
|
TriangleMesh mesh;
|
||||||
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) {
|
||||||
TriangleMesh omesh;
|
mesh.merge((*o)->raw_mesh());
|
||||||
(*o)->raw_mesh(&omesh);
|
|
||||||
mesh->merge(omesh);
|
|
||||||
}
|
}
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
@ -393,11 +390,11 @@ ModelObject::clear_instances()
|
||||||
}
|
}
|
||||||
|
|
||||||
// this returns the bounding box of the *transformed* instances
|
// this returns the bounding box of the *transformed* instances
|
||||||
void
|
BoundingBoxf3
|
||||||
ModelObject::bounding_box(BoundingBoxf3* bb)
|
ModelObject::bounding_box()
|
||||||
{
|
{
|
||||||
if (!this->_bounding_box_valid) this->update_bounding_box();
|
if (!this->_bounding_box_valid) this->update_bounding_box();
|
||||||
*bb = this->_bounding_box;
|
return this->_bounding_box;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -409,40 +406,40 @@ ModelObject::invalidate_bounding_box()
|
||||||
void
|
void
|
||||||
ModelObject::update_bounding_box()
|
ModelObject::update_bounding_box()
|
||||||
{
|
{
|
||||||
TriangleMesh mesh;
|
this->_bounding_box = this->mesh().bounding_box();
|
||||||
this->mesh(&mesh);
|
|
||||||
|
|
||||||
mesh.bounding_box(&this->_bounding_box);
|
|
||||||
this->_bounding_box_valid = true;
|
this->_bounding_box_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// flattens all volumes and instances into a single mesh
|
// flattens all volumes and instances into a single mesh
|
||||||
void
|
TriangleMesh
|
||||||
ModelObject::mesh(TriangleMesh* mesh) const
|
ModelObject::mesh() const
|
||||||
{
|
{
|
||||||
TriangleMesh raw_mesh;
|
TriangleMesh mesh;
|
||||||
this->raw_mesh(&raw_mesh);
|
TriangleMesh raw_mesh = this->raw_mesh();
|
||||||
|
|
||||||
|
|
||||||
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) {
|
for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) {
|
||||||
TriangleMesh m = raw_mesh;
|
TriangleMesh m = raw_mesh;
|
||||||
(*i)->transform_mesh(&m);
|
(*i)->transform_mesh(&m);
|
||||||
mesh->merge(m);
|
mesh.merge(m);
|
||||||
}
|
}
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
TriangleMesh
|
||||||
ModelObject::raw_mesh(TriangleMesh* mesh) const
|
ModelObject::raw_mesh() const
|
||||||
{
|
{
|
||||||
|
TriangleMesh mesh;
|
||||||
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
||||||
if ((*v)->modifier) continue;
|
if ((*v)->modifier) continue;
|
||||||
mesh->merge((*v)->mesh);
|
mesh.merge((*v)->mesh);
|
||||||
}
|
}
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
BoundingBoxf3
|
||||||
ModelObject::raw_bounding_box(BoundingBoxf3* bb) const
|
ModelObject::raw_bounding_box() const
|
||||||
{
|
{
|
||||||
|
BoundingBoxf3 bb;
|
||||||
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
||||||
if ((*v)->modifier) continue;
|
if ((*v)->modifier) continue;
|
||||||
TriangleMesh mesh = (*v)->mesh;
|
TriangleMesh mesh = (*v)->mesh;
|
||||||
|
@ -450,22 +447,18 @@ ModelObject::raw_bounding_box(BoundingBoxf3* bb) const
|
||||||
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
|
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
|
||||||
this->instances.front()->transform_mesh(&mesh, true);
|
this->instances.front()->transform_mesh(&mesh, true);
|
||||||
|
|
||||||
BoundingBoxf3 mbb;
|
bb.merge(mesh.bounding_box());
|
||||||
mesh.bounding_box(&mbb);
|
|
||||||
bb->merge(mbb);
|
|
||||||
}
|
}
|
||||||
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this returns the bounding box of the *transformed* given instance
|
// this returns the bounding box of the *transformed* given instance
|
||||||
void
|
BoundingBoxf3
|
||||||
ModelObject::instance_bounding_box(size_t instance_idx, BoundingBoxf3* bb) const
|
ModelObject::instance_bounding_box(size_t instance_idx) const
|
||||||
{
|
{
|
||||||
TriangleMesh mesh;
|
TriangleMesh mesh = this->raw_mesh();
|
||||||
this->raw_mesh(&mesh);
|
|
||||||
|
|
||||||
this->instances[instance_idx]->transform_mesh(&mesh);
|
this->instances[instance_idx]->transform_mesh(&mesh);
|
||||||
|
return mesh.bounding_box();
|
||||||
mesh.bounding_box(bb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -473,12 +466,7 @@ ModelObject::center_around_origin()
|
||||||
{
|
{
|
||||||
// calculate the displacements needed to
|
// calculate the displacements needed to
|
||||||
// center this object around the origin
|
// center this object around the origin
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb = this->raw_mesh().bounding_box();
|
||||||
{
|
|
||||||
TriangleMesh mesh;
|
|
||||||
this->raw_mesh(&mesh);
|
|
||||||
mesh.bounding_box(&bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// first align to origin on XYZ
|
// first align to origin on XYZ
|
||||||
Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z);
|
Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z);
|
||||||
|
@ -520,6 +508,9 @@ ModelObject::scale(const Pointf3 &versor)
|
||||||
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) {
|
||||||
(*v)->mesh.scale(versor);
|
(*v)->mesh.scale(versor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reset origin translation since it doesn't make sense anymore
|
||||||
|
this->origin_translation = Pointf3(0,0,0);
|
||||||
this->invalidate_bounding_box();
|
this->invalidate_bounding_box();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,12 +54,12 @@ class Model
|
||||||
// void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb);
|
// void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb);
|
||||||
bool has_objects_with_no_instances() const;
|
bool has_objects_with_no_instances() const;
|
||||||
bool add_default_instances();
|
bool add_default_instances();
|
||||||
void bounding_box(BoundingBoxf3* bb);
|
BoundingBoxf3 bounding_box() const;
|
||||||
void center_instances_around_point(const Pointf &point);
|
void center_instances_around_point(const Pointf &point);
|
||||||
void align_instances_to_origin();
|
void align_instances_to_origin();
|
||||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||||
void mesh(TriangleMesh* mesh) const;
|
TriangleMesh mesh() const;
|
||||||
void raw_mesh(TriangleMesh* mesh) const;
|
TriangleMesh raw_mesh() const;
|
||||||
// std::string get_material_name(t_model_material_id material_id);
|
// std::string get_material_name(t_model_material_id material_id);
|
||||||
|
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ class ModelObject
|
||||||
|
|
||||||
/* This vector accumulates the total translation applied to the object by the
|
/* This vector accumulates the total translation applied to the object by the
|
||||||
center_around_origin() method. Callers might want to apply the same translation
|
center_around_origin() method. Callers might want to apply the same translation
|
||||||
to new volumes before adding them to this object in order to preset alignment
|
to new volumes before adding them to this object in order to preserve alignment
|
||||||
when user expects that. */
|
when user expects that. */
|
||||||
Pointf3 origin_translation;
|
Pointf3 origin_translation;
|
||||||
|
|
||||||
|
@ -118,13 +118,13 @@ class ModelObject
|
||||||
void delete_last_instance();
|
void delete_last_instance();
|
||||||
void clear_instances();
|
void clear_instances();
|
||||||
|
|
||||||
void bounding_box(BoundingBoxf3* bb);
|
BoundingBoxf3 bounding_box();
|
||||||
void invalidate_bounding_box();
|
void invalidate_bounding_box();
|
||||||
|
|
||||||
void mesh(TriangleMesh* mesh) const;
|
TriangleMesh mesh() const;
|
||||||
void raw_mesh(TriangleMesh* mesh) const;
|
TriangleMesh raw_mesh() const;
|
||||||
void raw_bounding_box(BoundingBoxf3* bb) const;
|
BoundingBoxf3 raw_bounding_box() const;
|
||||||
void instance_bounding_box(size_t instance_idx, BoundingBoxf3* bb) const;
|
BoundingBoxf3 instance_bounding_box(size_t instance_idx) const;
|
||||||
void center_around_origin();
|
void center_around_origin();
|
||||||
void translate(const Vectorf3 &vector);
|
void translate(const Vectorf3 &vector);
|
||||||
void translate(coordf_t x, coordf_t y, coordf_t z);
|
void translate(coordf_t x, coordf_t y, coordf_t z);
|
||||||
|
|
|
@ -82,17 +82,18 @@ MotionPlanner::get_env(size_t island_idx) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polyline
|
||||||
MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline)
|
MotionPlanner::shortest_path(const Point &from, const Point &to)
|
||||||
{
|
{
|
||||||
// lazy generation of configuration space
|
// lazy generation of configuration space
|
||||||
if (!this->initialized) this->initialize();
|
if (!this->initialized) this->initialize();
|
||||||
|
|
||||||
// if we have an empty configuration space, return a straight move
|
// if we have an empty configuration space, return a straight move
|
||||||
if (this->islands.empty()) {
|
if (this->islands.empty()) {
|
||||||
polyline->points.push_back(from);
|
Polyline p;
|
||||||
polyline->points.push_back(to);
|
p.points.push_back(from);
|
||||||
return;
|
p.points.push_back(to);
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are both points in the same island?
|
// Are both points in the same island?
|
||||||
|
@ -102,9 +103,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
||||||
// since both points are in the same island, is a direct move possible?
|
// since both points are in the same island, is a direct move possible?
|
||||||
// if so, we avoid generating the visibility environment
|
// if so, we avoid generating the visibility environment
|
||||||
if (island->contains(Line(from, to))) {
|
if (island->contains(Line(from, to))) {
|
||||||
polyline->points.push_back(from);
|
Polyline p;
|
||||||
polyline->points.push_back(to);
|
p.points.push_back(from);
|
||||||
return;
|
p.points.push_back(to);
|
||||||
|
return p;
|
||||||
}
|
}
|
||||||
island_idx = island - this->islands.begin();
|
island_idx = island - this->islands.begin();
|
||||||
break;
|
break;
|
||||||
|
@ -116,9 +118,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
||||||
if (env.expolygons.empty()) {
|
if (env.expolygons.empty()) {
|
||||||
// if this environment is empty (probably because it's too small), perform straight move
|
// if this environment is empty (probably because it's too small), perform straight move
|
||||||
// and avoid running the algorithms on empty dataset
|
// and avoid running the algorithms on empty dataset
|
||||||
polyline->points.push_back(from);
|
Polyline p;
|
||||||
polyline->points.push_back(to);
|
p.points.push_back(from);
|
||||||
return; // bye bye
|
p.points.push_back(to);
|
||||||
|
return p; // bye bye
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now check whether points are inside the environment.
|
// Now check whether points are inside the environment.
|
||||||
|
@ -137,10 +140,10 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
||||||
|
|
||||||
// perform actual path search
|
// perform actual path search
|
||||||
MotionPlannerGraph* graph = this->init_graph(island_idx);
|
MotionPlannerGraph* graph = this->init_graph(island_idx);
|
||||||
graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to), polyline);
|
Polyline polyline = graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to));
|
||||||
|
|
||||||
polyline->points.insert(polyline->points.begin(), from);
|
polyline.points.insert(polyline.points.begin(), from);
|
||||||
polyline->points.push_back(to);
|
polyline.points.push_back(to);
|
||||||
|
|
||||||
{
|
{
|
||||||
// grow our environment slightly in order for simplify_by_visibility()
|
// grow our environment slightly in order for simplify_by_visibility()
|
||||||
|
@ -149,7 +152,7 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
||||||
offset(env, &grown_env.expolygons, +SCALED_EPSILON);
|
offset(env, &grown_env.expolygons, +SCALED_EPSILON);
|
||||||
|
|
||||||
// remove unnecessary vertices
|
// remove unnecessary vertices
|
||||||
polyline->simplify_by_visibility(grown_env);
|
polyline.simplify_by_visibility(grown_env);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -171,6 +174,8 @@ MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyl
|
||||||
svg.draw(*polyline, "red");
|
svg.draw(*polyline, "red");
|
||||||
svg.Close();
|
svg.Close();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
return polyline;
|
||||||
}
|
}
|
||||||
|
|
||||||
Point
|
Point
|
||||||
|
@ -310,11 +315,11 @@ MotionPlannerGraph::find_node(const Point &point) const
|
||||||
return point.nearest_point_index(this->nodes);
|
return point.nearest_point_index(this->nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polyline
|
||||||
MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline)
|
MotionPlannerGraph::shortest_path(size_t from, size_t to)
|
||||||
{
|
{
|
||||||
// this prevents a crash in case for some reason we got here with an empty adjacency list
|
// this prevents a crash in case for some reason we got here with an empty adjacency list
|
||||||
if (this->adjacency_list.empty()) return;
|
if (this->adjacency_list.empty()) return Polyline();
|
||||||
|
|
||||||
const weight_t max_weight = std::numeric_limits<weight_t>::infinity();
|
const weight_t max_weight = std::numeric_limits<weight_t>::infinity();
|
||||||
|
|
||||||
|
@ -379,10 +384,12 @@ MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polyline polyline;
|
||||||
for (node_t vertex = to; vertex != -1; vertex = previous[vertex])
|
for (node_t vertex = to; vertex != -1; vertex = previous[vertex])
|
||||||
polyline->points.push_back(this->nodes[vertex]);
|
polyline.points.push_back(this->nodes[vertex]);
|
||||||
polyline->points.push_back(this->nodes[from]);
|
polyline.points.push_back(this->nodes[from]);
|
||||||
polyline->reverse();
|
polyline.reverse();
|
||||||
|
return polyline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
|
|
@ -21,7 +21,7 @@ class MotionPlanner
|
||||||
public:
|
public:
|
||||||
MotionPlanner(const ExPolygons &islands);
|
MotionPlanner(const ExPolygons &islands);
|
||||||
~MotionPlanner();
|
~MotionPlanner();
|
||||||
void shortest_path(const Point &from, const Point &to, Polyline* polyline);
|
Polyline shortest_path(const Point &from, const Point &to);
|
||||||
size_t islands_count() const;
|
size_t islands_count() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -58,7 +58,7 @@ class MotionPlannerGraph
|
||||||
//std::map<std::pair<size_t,size_t>, double> edges;
|
//std::map<std::pair<size_t,size_t>, double> edges;
|
||||||
void add_edge(size_t from, size_t to, double weight);
|
void add_edge(size_t from, size_t to, double weight);
|
||||||
size_t find_node(const Point &point) const;
|
size_t find_node(const Point &point) const;
|
||||||
void shortest_path(size_t from, size_t to, Polyline* polyline);
|
Polyline shortest_path(size_t from, size_t to);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,10 +83,21 @@ MultiPoint::has_boundary_point(const Point &point) const
|
||||||
return dist < SCALED_EPSILON;
|
return dist < SCALED_EPSILON;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
BoundingBox
|
||||||
MultiPoint::bounding_box(BoundingBox* bb) const
|
MultiPoint::bounding_box() const
|
||||||
{
|
{
|
||||||
*bb = BoundingBox(this->points);
|
return BoundingBox(this->points);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MultiPoint::remove_duplicate_points()
|
||||||
|
{
|
||||||
|
for (size_t i = 1; i < this->points.size(); ++i) {
|
||||||
|
if (this->points.at(i).coincides_with(this->points.at(i-1))) {
|
||||||
|
this->points.erase(this->points.begin() + i);
|
||||||
|
--i;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Points
|
Points
|
||||||
|
|
|
@ -31,7 +31,8 @@ class MultiPoint
|
||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
int find_point(const Point &point) const;
|
int find_point(const Point &point) const;
|
||||||
bool has_boundary_point(const Point &point) const;
|
bool has_boundary_point(const Point &point) const;
|
||||||
void bounding_box(BoundingBox* bb) const;
|
BoundingBox bounding_box() const;
|
||||||
|
void remove_duplicate_points();
|
||||||
|
|
||||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class Line;
|
class Line;
|
||||||
|
class Linef;
|
||||||
class MultiPoint;
|
class MultiPoint;
|
||||||
class Point;
|
class Point;
|
||||||
class Pointf;
|
class Pointf;
|
||||||
|
@ -82,6 +83,12 @@ class Pointf
|
||||||
coordf_t x;
|
coordf_t x;
|
||||||
coordf_t y;
|
coordf_t y;
|
||||||
explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {};
|
explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {};
|
||||||
|
static Pointf new_unscale(coord_t x, coord_t y) {
|
||||||
|
return Pointf(unscale(x), unscale(y));
|
||||||
|
};
|
||||||
|
static Pointf new_unscale(const Point &p) {
|
||||||
|
return Pointf(unscale(p.x), unscale(p.y));
|
||||||
|
};
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
void rotate(double angle, const Pointf ¢er);
|
void rotate(double angle, const Pointf ¢er);
|
||||||
|
@ -100,6 +107,9 @@ class Pointf3 : public Pointf
|
||||||
public:
|
public:
|
||||||
coordf_t z;
|
coordf_t z;
|
||||||
explicit Pointf3(coordf_t _x = 0, coordf_t _y = 0, coordf_t _z = 0): Pointf(_x, _y), z(_z) {};
|
explicit Pointf3(coordf_t _x = 0, coordf_t _y = 0, coordf_t _z = 0): Pointf(_x, _y), z(_z) {};
|
||||||
|
static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) {
|
||||||
|
return Pointf3(unscale(x), unscale(y), unscale(z));
|
||||||
|
};
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(const Vectorf3 &vector);
|
void translate(const Vectorf3 &vector);
|
||||||
void translate(double x, double y, double z);
|
void translate(double x, double y, double z);
|
||||||
|
|
|
@ -14,9 +14,7 @@ Polygon::operator Polygons() const
|
||||||
|
|
||||||
Polygon::operator Polyline() const
|
Polygon::operator Polyline() const
|
||||||
{
|
{
|
||||||
Polyline polyline;
|
return this->split_at_first_point();
|
||||||
this->split_at_first_point(&polyline);
|
|
||||||
return polyline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Point&
|
Point&
|
||||||
|
@ -41,55 +39,49 @@ Lines
|
||||||
Polygon::lines() const
|
Polygon::lines() const
|
||||||
{
|
{
|
||||||
Lines lines;
|
Lines lines;
|
||||||
this->lines(&lines);
|
lines.reserve(this->points.size());
|
||||||
|
for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) {
|
||||||
|
lines.push_back(Line(*it, *(it + 1)));
|
||||||
|
}
|
||||||
|
lines.push_back(Line(this->points.back(), this->points.front()));
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polyline
|
||||||
Polygon::lines(Lines* lines) const
|
Polygon::split_at_vertex(const Point &point) const
|
||||||
{
|
|
||||||
lines->reserve(lines->size() + this->points.size());
|
|
||||||
for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) {
|
|
||||||
lines->push_back(Line(*it, *(it + 1)));
|
|
||||||
}
|
|
||||||
lines->push_back(Line(this->points.back(), this->points.front()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Polygon::split_at_vertex(const Point &point, Polyline* polyline) const
|
|
||||||
{
|
{
|
||||||
// find index of point
|
// find index of point
|
||||||
for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
|
for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
|
||||||
if (it->coincides_with(point)) {
|
if (it->coincides_with(point)) {
|
||||||
this->split_at_index(it - this->points.begin(), polyline);
|
return this->split_at_index(it - this->points.begin());
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CONFESS("Point not found");
|
CONFESS("Point not found");
|
||||||
|
return Polyline();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polyline
|
||||||
Polygon::split_at_index(int index, Polyline* polyline) const
|
Polygon::split_at_index(int index) const
|
||||||
{
|
|
||||||
polyline->points.reserve(this->points.size() + 1);
|
|
||||||
for (Points::const_iterator it = this->points.begin() + index; it != this->points.end(); ++it)
|
|
||||||
polyline->points.push_back(*it);
|
|
||||||
for (Points::const_iterator it = this->points.begin(); it != this->points.begin() + index + 1; ++it)
|
|
||||||
polyline->points.push_back(*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Polygon::split_at_first_point(Polyline* polyline) const
|
|
||||||
{
|
|
||||||
this->split_at_index(0, polyline);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Polygon::equally_spaced_points(double distance, Points* points) const
|
|
||||||
{
|
{
|
||||||
Polyline polyline;
|
Polyline polyline;
|
||||||
this->split_at_first_point(&polyline);
|
polyline.points.reserve(this->points.size() + 1);
|
||||||
polyline.equally_spaced_points(distance, points);
|
for (Points::const_iterator it = this->points.begin() + index; it != this->points.end(); ++it)
|
||||||
|
polyline.points.push_back(*it);
|
||||||
|
for (Points::const_iterator it = this->points.begin(); it != this->points.begin() + index + 1; ++it)
|
||||||
|
polyline.points.push_back(*it);
|
||||||
|
return polyline;
|
||||||
|
}
|
||||||
|
|
||||||
|
Polyline
|
||||||
|
Polygon::split_at_first_point() const
|
||||||
|
{
|
||||||
|
return this->split_at_index(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Points
|
||||||
|
Polygon::equally_spaced_points(double distance) const
|
||||||
|
{
|
||||||
|
return this->split_at_first_point().equally_spaced_points(distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
double
|
double
|
||||||
|
@ -155,6 +147,7 @@ Polygon::contains(const Point &point) const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this only works on CCW polygons as CW will be ripped out by Clipper's simplify_polygons()
|
||||||
Polygons
|
Polygons
|
||||||
Polygon::simplify(double tolerance) const
|
Polygon::simplify(double tolerance) const
|
||||||
{
|
{
|
||||||
|
@ -203,8 +196,7 @@ Polygon::centroid() const
|
||||||
double x_temp = 0;
|
double x_temp = 0;
|
||||||
double y_temp = 0;
|
double y_temp = 0;
|
||||||
|
|
||||||
Polyline polyline;
|
Polyline polyline = this->split_at_first_point();
|
||||||
this->split_at_first_point(&polyline);
|
|
||||||
for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) {
|
for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) {
|
||||||
x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
|
x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
|
||||||
y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
|
y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
|
||||||
|
@ -227,55 +219,49 @@ Polygon::wkt() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// find all concave vertices (i.e. having an internal angle greater than the supplied angle) */
|
// find all concave vertices (i.e. having an internal angle greater than the supplied angle) */
|
||||||
void
|
Points
|
||||||
Polygon::concave_points(double angle, Points* points) const
|
Polygon::concave_points(double angle) const
|
||||||
{
|
{
|
||||||
|
Points points;
|
||||||
angle = 2*PI - angle;
|
angle = 2*PI - angle;
|
||||||
|
|
||||||
// check whether first point forms a concave angle
|
// check whether first point forms a concave angle
|
||||||
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle)
|
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) <= angle)
|
||||||
points->push_back(this->points.front());
|
points.push_back(this->points.front());
|
||||||
|
|
||||||
// check whether points 1..(n-1) form concave angles
|
// check whether points 1..(n-1) form concave angles
|
||||||
for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) {
|
for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) {
|
||||||
if (p->ccw_angle(*(p-1), *(p+1)) <= angle) points->push_back(*p);
|
if (p->ccw_angle(*(p-1), *(p+1)) <= angle) points.push_back(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether last point forms a concave angle
|
// check whether last point forms a concave angle
|
||||||
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle)
|
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) <= angle)
|
||||||
points->push_back(this->points.back());
|
points.push_back(this->points.back());
|
||||||
}
|
|
||||||
|
|
||||||
void
|
return points;
|
||||||
Polygon::concave_points(Points* points) const
|
|
||||||
{
|
|
||||||
this->concave_points(PI, points);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle) */
|
// find all convex vertices (i.e. having an internal angle smaller than the supplied angle) */
|
||||||
void
|
Points
|
||||||
Polygon::convex_points(double angle, Points* points) const
|
Polygon::convex_points(double angle) const
|
||||||
{
|
{
|
||||||
|
Points points;
|
||||||
angle = 2*PI - angle;
|
angle = 2*PI - angle;
|
||||||
|
|
||||||
// check whether first point forms a convex angle
|
// check whether first point forms a convex angle
|
||||||
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle)
|
if (this->points.front().ccw_angle(this->points.back(), *(this->points.begin()+1)) >= angle)
|
||||||
points->push_back(this->points.front());
|
points.push_back(this->points.front());
|
||||||
|
|
||||||
// check whether points 1..(n-1) form convex angles
|
// check whether points 1..(n-1) form convex angles
|
||||||
for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) {
|
for (Points::const_iterator p = this->points.begin()+1; p != this->points.end()-1; ++p) {
|
||||||
if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points->push_back(*p);
|
if (p->ccw_angle(*(p-1), *(p+1)) >= angle) points.push_back(*p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check whether last point forms a convex angle
|
// check whether last point forms a convex angle
|
||||||
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle)
|
if (this->points.back().ccw_angle(*(this->points.end()-2), this->points.front()) >= angle)
|
||||||
points->push_back(this->points.back());
|
points.push_back(this->points.back());
|
||||||
}
|
|
||||||
|
|
||||||
void
|
return points;
|
||||||
Polygon::convex_points(Points* points) const
|
|
||||||
{
|
|
||||||
this->convex_points(PI, points);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
|
|
|
@ -24,11 +24,10 @@ class Polygon : public MultiPoint {
|
||||||
explicit Polygon(const Points &points): MultiPoint(points) {};
|
explicit Polygon(const Points &points): MultiPoint(points) {};
|
||||||
Point last_point() const;
|
Point last_point() const;
|
||||||
Lines lines() const;
|
Lines lines() const;
|
||||||
void lines(Lines* lines) const;
|
Polyline split_at_vertex(const Point &point) const;
|
||||||
void split_at_vertex(const Point &point, Polyline* polyline) const;
|
Polyline split_at_index(int index) const;
|
||||||
void split_at_index(int index, Polyline* polyline) const;
|
Polyline split_at_first_point() const;
|
||||||
void split_at_first_point(Polyline* polyline) const;
|
Points equally_spaced_points(double distance) const;
|
||||||
void equally_spaced_points(double distance, Points* points) const;
|
|
||||||
double area() const;
|
double area() const;
|
||||||
bool is_counter_clockwise() const;
|
bool is_counter_clockwise() const;
|
||||||
bool is_clockwise() const;
|
bool is_clockwise() const;
|
||||||
|
@ -41,10 +40,8 @@ class Polygon : public MultiPoint {
|
||||||
void triangulate_convex(Polygons* polygons) const;
|
void triangulate_convex(Polygons* polygons) const;
|
||||||
Point centroid() const;
|
Point centroid() const;
|
||||||
std::string wkt() const;
|
std::string wkt() const;
|
||||||
void concave_points(double angle, Points* points) const;
|
Points concave_points(double angle = PI) const;
|
||||||
void concave_points(Points* points) const;
|
Points convex_points(double angle = PI) const;
|
||||||
void convex_points(double angle, Points* points) const;
|
|
||||||
void convex_points(Points* points) const;
|
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
void from_SV_check(SV* poly_sv);
|
void from_SV_check(SV* poly_sv);
|
||||||
|
|
|
@ -98,10 +98,11 @@ Polyline::extend_start(double distance)
|
||||||
|
|
||||||
/* this method returns a collection of points picked on the polygon contour
|
/* this method returns a collection of points picked on the polygon contour
|
||||||
so that they are evenly spaced according to the input distance */
|
so that they are evenly spaced according to the input distance */
|
||||||
void
|
Points
|
||||||
Polyline::equally_spaced_points(double distance, Points* points) const
|
Polyline::equally_spaced_points(double distance) const
|
||||||
{
|
{
|
||||||
points->push_back(this->first_point());
|
Points points;
|
||||||
|
points.push_back(this->first_point());
|
||||||
double len = 0;
|
double len = 0;
|
||||||
|
|
||||||
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
|
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
|
||||||
|
@ -110,17 +111,18 @@ Polyline::equally_spaced_points(double distance, Points* points) const
|
||||||
if (len < distance) continue;
|
if (len < distance) continue;
|
||||||
|
|
||||||
if (len == distance) {
|
if (len == distance) {
|
||||||
points->push_back(*it);
|
points.push_back(*it);
|
||||||
len = 0;
|
len = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
double take = segment_length - (len - distance); // how much we take of this segment
|
double take = segment_length - (len - distance); // how much we take of this segment
|
||||||
Line segment(*(it-1), *it);
|
Line segment(*(it-1), *it);
|
||||||
points->push_back(segment.point_at(take));
|
points.push_back(segment.point_at(take));
|
||||||
it--;
|
it--;
|
||||||
len = -take;
|
len = -take;
|
||||||
}
|
}
|
||||||
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -22,7 +22,7 @@ class Polyline : public MultiPoint {
|
||||||
void clip_start(double distance);
|
void clip_start(double distance);
|
||||||
void extend_end(double distance);
|
void extend_end(double distance);
|
||||||
void extend_start(double distance);
|
void extend_start(double distance);
|
||||||
void equally_spaced_points(double distance, Points* points) const;
|
Points equally_spaced_points(double distance) const;
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
template <class T> void simplify_by_visibility(const T &area);
|
template <class T> void simplify_by_visibility(const T &area);
|
||||||
void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
|
void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
|
||||||
|
|
|
@ -358,8 +358,7 @@ Print::add_model_object(ModelObject* model_object, int idx)
|
||||||
// initialize print object and store it at the given position
|
// initialize print object and store it at the given position
|
||||||
PrintObject* o;
|
PrintObject* o;
|
||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb = model_object->raw_bounding_box();
|
||||||
model_object->raw_bounding_box(&bb);
|
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
// replacing existing object
|
// replacing existing object
|
||||||
PrintObjectPtrs::iterator old_it = this->objects.begin() + idx;
|
PrintObjectPtrs::iterator old_it = this->objects.begin() + idx;
|
||||||
|
@ -569,14 +568,13 @@ Print::validate() const
|
||||||
Polygons mesh_convex_hulls;
|
Polygons mesh_convex_hulls;
|
||||||
for (size_t i = 0; i < this->regions.size(); ++i) {
|
for (size_t i = 0; i < this->regions.size(); ++i) {
|
||||||
for (std::vector<int>::const_iterator it = object->region_volumes[i].begin(); it != object->region_volumes[i].end(); ++it) {
|
for (std::vector<int>::const_iterator it = object->region_volumes[i].begin(); it != object->region_volumes[i].end(); ++it) {
|
||||||
Polygon hull;
|
Polygon hull = object->model_object()->volumes[*it]->mesh.convex_hull();
|
||||||
object->model_object()->volumes[*it]->mesh.convex_hull(&hull);
|
|
||||||
mesh_convex_hulls.push_back(hull);
|
mesh_convex_hulls.push_back(hull);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a single convex hull for all of them
|
// make a single convex hull for all of them
|
||||||
Slic3r::Geometry::convex_hull(mesh_convex_hulls, &convex_hull);
|
convex_hull = Slic3r::Geometry::convex_hull(mesh_convex_hulls);
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply the same transformations we apply to the actual meshes when slicing them
|
// apply the same transformations we apply to the actual meshes when slicing them
|
||||||
|
|
|
@ -111,7 +111,7 @@ class PrintObject
|
||||||
bool delete_all_copies();
|
bool delete_all_copies();
|
||||||
bool set_copies(const Points &points);
|
bool set_copies(const Points &points);
|
||||||
bool reload_model_instances();
|
bool reload_model_instances();
|
||||||
void bounding_box(BoundingBox* bb) const;
|
BoundingBox bounding_box() const;
|
||||||
|
|
||||||
// adds region_id, too, if necessary
|
// adds region_id, too, if necessary
|
||||||
void add_region_volume(int region_id, int volume_id);
|
void add_region_volume(int region_id, int volume_id);
|
||||||
|
@ -126,7 +126,7 @@ class PrintObject
|
||||||
size_t support_layer_count() const;
|
size_t support_layer_count() const;
|
||||||
void clear_support_layers();
|
void clear_support_layers();
|
||||||
SupportLayer* get_support_layer(int idx);
|
SupportLayer* get_support_layer(int idx);
|
||||||
SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z);
|
||||||
void delete_support_layer(int idx);
|
void delete_support_layer(int idx);
|
||||||
|
|
||||||
// methods for handling state
|
// methods for handling state
|
||||||
|
|
|
@ -22,6 +22,14 @@ PrintConfigDef::build_def() {
|
||||||
Options["bed_temperature"].min = 0;
|
Options["bed_temperature"].min = 0;
|
||||||
Options["bed_temperature"].max = 300;
|
Options["bed_temperature"].max = 300;
|
||||||
|
|
||||||
|
Options["before_layer_gcode"].type = coString;
|
||||||
|
Options["before_layer_gcode"].label = "Before layer change G-code";
|
||||||
|
Options["before_layer_gcode"].tooltip = "This custom code is inserted at every layer change, right before the Z move. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z].";
|
||||||
|
Options["before_layer_gcode"].cli = "before-layer-gcode=s";
|
||||||
|
Options["before_layer_gcode"].multiline = true;
|
||||||
|
Options["before_layer_gcode"].full_width = true;
|
||||||
|
Options["before_layer_gcode"].height = 50;
|
||||||
|
|
||||||
Options["bottom_solid_layers"].type = coInt;
|
Options["bottom_solid_layers"].type = coInt;
|
||||||
Options["bottom_solid_layers"].label = "Bottom";
|
Options["bottom_solid_layers"].label = "Bottom";
|
||||||
Options["bottom_solid_layers"].category = "Layers and Perimeters";
|
Options["bottom_solid_layers"].category = "Layers and Perimeters";
|
||||||
|
@ -119,7 +127,7 @@ PrintConfigDef::build_def() {
|
||||||
Options["external_fill_pattern"].label = "Top/bottom fill pattern";
|
Options["external_fill_pattern"].label = "Top/bottom fill pattern";
|
||||||
Options["external_fill_pattern"].category = "Infill";
|
Options["external_fill_pattern"].category = "Infill";
|
||||||
Options["external_fill_pattern"].tooltip = "Fill pattern for top/bottom infill. This only affects the external visible layer, and not its adjacent solid shells.";
|
Options["external_fill_pattern"].tooltip = "Fill pattern for top/bottom infill. This only affects the external visible layer, and not its adjacent solid shells.";
|
||||||
Options["external_fill_pattern"].cli = "external-fill-pattern=s";
|
Options["external_fill_pattern"].cli = "external-fill-pattern|solid-fill-pattern=s";
|
||||||
Options["external_fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
Options["external_fill_pattern"].enum_keys_map = ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||||
Options["external_fill_pattern"].enum_values.push_back("rectilinear");
|
Options["external_fill_pattern"].enum_values.push_back("rectilinear");
|
||||||
Options["external_fill_pattern"].enum_values.push_back("concentric");
|
Options["external_fill_pattern"].enum_values.push_back("concentric");
|
||||||
|
@ -318,7 +326,7 @@ PrintConfigDef::build_def() {
|
||||||
|
|
||||||
Options["first_layer_extrusion_width"].type = coFloatOrPercent;
|
Options["first_layer_extrusion_width"].type = coFloatOrPercent;
|
||||||
Options["first_layer_extrusion_width"].label = "First layer";
|
Options["first_layer_extrusion_width"].label = "First layer";
|
||||||
Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height.";
|
Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height. If set to zero, it will use the Default Extrusion Width.";
|
||||||
Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)";
|
||||||
Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s";
|
Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s";
|
||||||
Options["first_layer_extrusion_width"].ratio_over = "first_layer_height";
|
Options["first_layer_extrusion_width"].ratio_over = "first_layer_height";
|
||||||
|
@ -421,6 +429,14 @@ PrintConfigDef::build_def() {
|
||||||
Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material).";
|
Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material).";
|
||||||
Options["infill_only_where_needed"].cli = "infill-only-where-needed!";
|
Options["infill_only_where_needed"].cli = "infill-only-where-needed!";
|
||||||
|
|
||||||
|
Options["infill_overlap"].type = coFloatOrPercent;
|
||||||
|
Options["infill_overlap"].label = "Infill/perimeters overlap";
|
||||||
|
Options["infill_overlap"].category = "Advanced";
|
||||||
|
Options["infill_overlap"].tooltip = "This setting applies an additional overlap between infill and perimeters for better bonding. Theoretically this shouldn't be needed, but backlash might cause gaps. If expressed as percentage (example: 15%) it is calculated over perimeter extrusion width.";
|
||||||
|
Options["infill_overlap"].sidetext = "mm or %";
|
||||||
|
Options["infill_overlap"].cli = "infill-overlap=s";
|
||||||
|
Options["infill_overlap"].ratio_over = "perimeter_extrusion_width";
|
||||||
|
|
||||||
Options["infill_speed"].type = coFloat;
|
Options["infill_speed"].type = coFloat;
|
||||||
Options["infill_speed"].label = "Infill";
|
Options["infill_speed"].label = "Infill";
|
||||||
Options["infill_speed"].category = "Speed";
|
Options["infill_speed"].category = "Speed";
|
||||||
|
@ -438,9 +454,9 @@ PrintConfigDef::build_def() {
|
||||||
Options["interface_shells"].category = "Layers and Perimeters";
|
Options["interface_shells"].category = "Layers and Perimeters";
|
||||||
|
|
||||||
Options["layer_gcode"].type = coString;
|
Options["layer_gcode"].type = coString;
|
||||||
Options["layer_gcode"].label = "Layer change G-code";
|
Options["layer_gcode"].label = "After layer change G-code";
|
||||||
Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings.";
|
Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings as well as [layer_num] and [layer_z].";
|
||||||
Options["layer_gcode"].cli = "layer-gcode=s";
|
Options["layer_gcode"].cli = "after-layer-gcode|layer-gcode=s";
|
||||||
Options["layer_gcode"].multiline = true;
|
Options["layer_gcode"].multiline = true;
|
||||||
Options["layer_gcode"].full_width = true;
|
Options["layer_gcode"].full_width = true;
|
||||||
Options["layer_gcode"].height = 50;
|
Options["layer_gcode"].height = 50;
|
||||||
|
@ -799,6 +815,19 @@ PrintConfigDef::build_def() {
|
||||||
Options["support_material_angle"].min = 0;
|
Options["support_material_angle"].min = 0;
|
||||||
Options["support_material_angle"].max = 359;
|
Options["support_material_angle"].max = 359;
|
||||||
|
|
||||||
|
Options["support_material_contact_distance"].type = coFloat;
|
||||||
|
Options["support_material_contact_distance"].gui_type = "f_enum_open";
|
||||||
|
Options["support_material_contact_distance"].label = "Contact Z distance";
|
||||||
|
Options["support_material_contact_distance"].category = "Support material";
|
||||||
|
Options["support_material_contact_distance"].tooltip = "The vertical distance between object and support material interface. Setting this to 0 will also prevent Slic3r from using bridge flow and speed for the first object layer.";
|
||||||
|
Options["support_material_contact_distance"].sidetext = "mm";
|
||||||
|
Options["support_material_contact_distance"].cli = "support-material-contact-distance=f";
|
||||||
|
Options["support_material_contact_distance"].min = 0;
|
||||||
|
Options["support_material_contact_distance"].enum_values.push_back("0");
|
||||||
|
Options["support_material_contact_distance"].enum_values.push_back("0.2");
|
||||||
|
Options["support_material_contact_distance"].enum_labels.push_back("0 (soluble)");
|
||||||
|
Options["support_material_contact_distance"].enum_labels.push_back("0.2 (detachable)");
|
||||||
|
|
||||||
Options["support_material_enforce_layers"].type = coInt;
|
Options["support_material_enforce_layers"].type = coInt;
|
||||||
Options["support_material_enforce_layers"].label = "Enforce support for the first";
|
Options["support_material_enforce_layers"].label = "Enforce support for the first";
|
||||||
Options["support_material_enforce_layers"].category = "Support material";
|
Options["support_material_enforce_layers"].category = "Support material";
|
||||||
|
@ -966,7 +995,7 @@ PrintConfigDef::build_def() {
|
||||||
|
|
||||||
Options["use_volumetric_e"].type = coBool;
|
Options["use_volumetric_e"].type = coBool;
|
||||||
Options["use_volumetric_e"].label = "Use volumetric E";
|
Options["use_volumetric_e"].label = "Use volumetric E";
|
||||||
Options["use_volumetric_e"].tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. The M200 command is prepended to the generated G-code, unless it is found in the configured start G-code. This is only supported in recent Marlin.";
|
Options["use_volumetric_e"].tooltip = "This experimental setting uses outputs the E values in cubic millimeters instead of linear millimeters. If your firmware doesn't already know filament diameter(s), you can put commands like 'M200 D[filament_diameter_0] T0' in your start G-code in order to turn volumetric mode on and use the filament diameter associated to the filament selected in Slic3r. This is only supported in recent Marlin.";
|
||||||
Options["use_volumetric_e"].cli = "use-volumetric-e!";
|
Options["use_volumetric_e"].cli = "use-volumetric-e!";
|
||||||
|
|
||||||
Options["vibration_limit"].type = coFloat;
|
Options["vibration_limit"].type = coFloat;
|
||||||
|
|
|
@ -133,6 +133,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
||||||
ConfigOptionEnum<SeamPosition> seam_position;
|
ConfigOptionEnum<SeamPosition> seam_position;
|
||||||
ConfigOptionBool support_material;
|
ConfigOptionBool support_material;
|
||||||
ConfigOptionInt support_material_angle;
|
ConfigOptionInt support_material_angle;
|
||||||
|
ConfigOptionFloat support_material_contact_distance;
|
||||||
ConfigOptionInt support_material_enforce_layers;
|
ConfigOptionInt support_material_enforce_layers;
|
||||||
ConfigOptionInt support_material_extruder;
|
ConfigOptionInt support_material_extruder;
|
||||||
ConfigOptionFloatOrPercent support_material_extrusion_width;
|
ConfigOptionFloatOrPercent support_material_extrusion_width;
|
||||||
|
@ -159,6 +160,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
||||||
this->seam_position.value = spAligned;
|
this->seam_position.value = spAligned;
|
||||||
this->support_material.value = false;
|
this->support_material.value = false;
|
||||||
this->support_material_angle.value = 0;
|
this->support_material_angle.value = 0;
|
||||||
|
this->support_material_contact_distance.value = 0.2;
|
||||||
this->support_material_enforce_layers.value = 0;
|
this->support_material_enforce_layers.value = 0;
|
||||||
this->support_material_extruder.value = 1;
|
this->support_material_extruder.value = 1;
|
||||||
this->support_material_extrusion_width.value = 0;
|
this->support_material_extrusion_width.value = 0;
|
||||||
|
@ -186,6 +188,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig
|
||||||
if (opt_key == "seam_position") return &this->seam_position;
|
if (opt_key == "seam_position") return &this->seam_position;
|
||||||
if (opt_key == "support_material") return &this->support_material;
|
if (opt_key == "support_material") return &this->support_material;
|
||||||
if (opt_key == "support_material_angle") return &this->support_material_angle;
|
if (opt_key == "support_material_angle") return &this->support_material_angle;
|
||||||
|
if (opt_key == "support_material_contact_distance") return &this->support_material_contact_distance;
|
||||||
if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers;
|
if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers;
|
||||||
if (opt_key == "support_material_extruder") return &this->support_material_extruder;
|
if (opt_key == "support_material_extruder") return &this->support_material_extruder;
|
||||||
if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width;
|
if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width;
|
||||||
|
@ -221,6 +224,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
||||||
ConfigOptionInt infill_extruder;
|
ConfigOptionInt infill_extruder;
|
||||||
ConfigOptionFloatOrPercent infill_extrusion_width;
|
ConfigOptionFloatOrPercent infill_extrusion_width;
|
||||||
ConfigOptionInt infill_every_layers;
|
ConfigOptionInt infill_every_layers;
|
||||||
|
ConfigOptionFloatOrPercent infill_overlap;
|
||||||
ConfigOptionFloat infill_speed;
|
ConfigOptionFloat infill_speed;
|
||||||
ConfigOptionBool overhangs;
|
ConfigOptionBool overhangs;
|
||||||
ConfigOptionInt perimeter_extruder;
|
ConfigOptionInt perimeter_extruder;
|
||||||
|
@ -245,38 +249,40 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
||||||
this->external_fill_pattern.value = ipRectilinear;
|
this->external_fill_pattern.value = ipRectilinear;
|
||||||
this->external_perimeter_extrusion_width.value = 0;
|
this->external_perimeter_extrusion_width.value = 0;
|
||||||
this->external_perimeter_extrusion_width.percent = false;
|
this->external_perimeter_extrusion_width.percent = false;
|
||||||
this->external_perimeter_speed.value = 70;
|
this->external_perimeter_speed.value = 50;
|
||||||
this->external_perimeter_speed.percent = true;
|
this->external_perimeter_speed.percent = true;
|
||||||
this->external_perimeters_first.value = false;
|
this->external_perimeters_first.value = false;
|
||||||
this->extra_perimeters.value = true;
|
this->extra_perimeters.value = true;
|
||||||
this->fill_angle.value = 45;
|
this->fill_angle.value = 45;
|
||||||
this->fill_density.value = 40;
|
this->fill_density.value = 20;
|
||||||
this->fill_pattern.value = ipHoneycomb;
|
this->fill_pattern.value = ipHoneycomb;
|
||||||
this->gap_fill_speed.value = 20;
|
this->gap_fill_speed.value = 20;
|
||||||
this->infill_extruder.value = 1;
|
this->infill_extruder.value = 1;
|
||||||
this->infill_extrusion_width.value = 0;
|
this->infill_extrusion_width.value = 0;
|
||||||
this->infill_extrusion_width.percent = false;
|
this->infill_extrusion_width.percent = false;
|
||||||
this->infill_every_layers.value = 1;
|
this->infill_every_layers.value = 1;
|
||||||
this->infill_speed.value = 60;
|
this->infill_overlap.value = 15;
|
||||||
|
this->infill_overlap.percent = true;
|
||||||
|
this->infill_speed.value = 80;
|
||||||
this->overhangs.value = true;
|
this->overhangs.value = true;
|
||||||
this->perimeter_extruder.value = 1;
|
this->perimeter_extruder.value = 1;
|
||||||
this->perimeter_extrusion_width.value = 0;
|
this->perimeter_extrusion_width.value = 0;
|
||||||
this->perimeter_extrusion_width.percent = false;
|
this->perimeter_extrusion_width.percent = false;
|
||||||
this->perimeter_speed.value = 30;
|
this->perimeter_speed.value = 60;
|
||||||
this->perimeters.value = 3;
|
this->perimeters.value = 3;
|
||||||
this->solid_infill_extruder.value = 1;
|
this->solid_infill_extruder.value = 1;
|
||||||
this->small_perimeter_speed.value = 30;
|
this->small_perimeter_speed.value = 15;
|
||||||
this->small_perimeter_speed.percent = false;
|
this->small_perimeter_speed.percent = false;
|
||||||
this->solid_infill_below_area.value = 70;
|
this->solid_infill_below_area.value = 70;
|
||||||
this->solid_infill_extrusion_width.value = 0;
|
this->solid_infill_extrusion_width.value = 0;
|
||||||
this->solid_infill_extrusion_width.percent = false;
|
this->solid_infill_extrusion_width.percent = false;
|
||||||
this->solid_infill_every_layers.value = 0;
|
this->solid_infill_every_layers.value = 0;
|
||||||
this->solid_infill_speed.value = 60;
|
this->solid_infill_speed.value = 20;
|
||||||
this->solid_infill_speed.percent = false;
|
this->solid_infill_speed.percent = false;
|
||||||
this->thin_walls.value = true;
|
this->thin_walls.value = true;
|
||||||
this->top_infill_extrusion_width.value = 0;
|
this->top_infill_extrusion_width.value = 0;
|
||||||
this->top_infill_extrusion_width.percent = false;
|
this->top_infill_extrusion_width.percent = false;
|
||||||
this->top_solid_infill_speed.value = 50;
|
this->top_solid_infill_speed.value = 15;
|
||||||
this->top_solid_infill_speed.percent = false;
|
this->top_solid_infill_speed.percent = false;
|
||||||
this->top_solid_layers.value = 3;
|
this->top_solid_layers.value = 3;
|
||||||
};
|
};
|
||||||
|
@ -297,6 +303,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
||||||
if (opt_key == "infill_extruder") return &this->infill_extruder;
|
if (opt_key == "infill_extruder") return &this->infill_extruder;
|
||||||
if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width;
|
if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width;
|
||||||
if (opt_key == "infill_every_layers") return &this->infill_every_layers;
|
if (opt_key == "infill_every_layers") return &this->infill_every_layers;
|
||||||
|
if (opt_key == "infill_overlap") return &this->infill_overlap;
|
||||||
if (opt_key == "infill_speed") return &this->infill_speed;
|
if (opt_key == "infill_speed") return &this->infill_speed;
|
||||||
if (opt_key == "overhangs") return &this->overhangs;
|
if (opt_key == "overhangs") return &this->overhangs;
|
||||||
if (opt_key == "perimeter_extruder") return &this->perimeter_extruder;
|
if (opt_key == "perimeter_extruder") return &this->perimeter_extruder;
|
||||||
|
@ -321,6 +328,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig
|
||||||
class GCodeConfig : public virtual StaticPrintConfig
|
class GCodeConfig : public virtual StaticPrintConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
ConfigOptionString before_layer_gcode;
|
||||||
ConfigOptionString end_gcode;
|
ConfigOptionString end_gcode;
|
||||||
ConfigOptionString extrusion_axis;
|
ConfigOptionString extrusion_axis;
|
||||||
ConfigOptionFloats extrusion_multiplier;
|
ConfigOptionFloats extrusion_multiplier;
|
||||||
|
@ -343,6 +351,7 @@ class GCodeConfig : public virtual StaticPrintConfig
|
||||||
ConfigOptionBool use_volumetric_e;
|
ConfigOptionBool use_volumetric_e;
|
||||||
|
|
||||||
GCodeConfig() : StaticPrintConfig() {
|
GCodeConfig() : StaticPrintConfig() {
|
||||||
|
this->before_layer_gcode.value = "";
|
||||||
this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n";
|
this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n";
|
||||||
this->extrusion_axis.value = "E";
|
this->extrusion_axis.value = "E";
|
||||||
this->extrusion_multiplier.values.resize(1);
|
this->extrusion_multiplier.values.resize(1);
|
||||||
|
@ -354,7 +363,7 @@ class GCodeConfig : public virtual StaticPrintConfig
|
||||||
this->layer_gcode.value = "";
|
this->layer_gcode.value = "";
|
||||||
this->pressure_advance.value = 0;
|
this->pressure_advance.value = 0;
|
||||||
this->retract_length.values.resize(1);
|
this->retract_length.values.resize(1);
|
||||||
this->retract_length.values[0] = 1;
|
this->retract_length.values[0] = 2;
|
||||||
this->retract_length_toolchange.values.resize(1);
|
this->retract_length_toolchange.values.resize(1);
|
||||||
this->retract_length_toolchange.values[0] = 10;
|
this->retract_length_toolchange.values[0] = 10;
|
||||||
this->retract_lift.values.resize(1);
|
this->retract_lift.values.resize(1);
|
||||||
|
@ -364,7 +373,7 @@ class GCodeConfig : public virtual StaticPrintConfig
|
||||||
this->retract_restart_extra_toolchange.values.resize(1);
|
this->retract_restart_extra_toolchange.values.resize(1);
|
||||||
this->retract_restart_extra_toolchange.values[0] = 0;
|
this->retract_restart_extra_toolchange.values[0] = 0;
|
||||||
this->retract_speed.values.resize(1);
|
this->retract_speed.values.resize(1);
|
||||||
this->retract_speed.values[0] = 30;
|
this->retract_speed.values[0] = 40;
|
||||||
this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
|
this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n";
|
||||||
this->toolchange_gcode.value = "";
|
this->toolchange_gcode.value = "";
|
||||||
this->travel_speed.value = 130;
|
this->travel_speed.value = 130;
|
||||||
|
@ -374,6 +383,7 @@ class GCodeConfig : public virtual StaticPrintConfig
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
ConfigOption* option(const t_config_option_key opt_key, bool create = false) {
|
||||||
|
if (opt_key == "before_layer_gcode") return &this->before_layer_gcode;
|
||||||
if (opt_key == "end_gcode") return &this->end_gcode;
|
if (opt_key == "end_gcode") return &this->end_gcode;
|
||||||
if (opt_key == "extrusion_axis") return &this->extrusion_axis;
|
if (opt_key == "extrusion_axis") return &this->extrusion_axis;
|
||||||
if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier;
|
if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier;
|
||||||
|
@ -476,7 +486,7 @@ class PrintConfig : public GCodeConfig
|
||||||
this->complete_objects.value = false;
|
this->complete_objects.value = false;
|
||||||
this->cooling.value = true;
|
this->cooling.value = true;
|
||||||
this->default_acceleration.value = 0;
|
this->default_acceleration.value = 0;
|
||||||
this->disable_fan_first_layers.value = 1;
|
this->disable_fan_first_layers.value = 3;
|
||||||
this->duplicate_distance.value = 6;
|
this->duplicate_distance.value = 6;
|
||||||
this->extruder_clearance_height.value = 20;
|
this->extruder_clearance_height.value = 20;
|
||||||
this->extruder_clearance_radius.value = 20;
|
this->extruder_clearance_radius.value = 20;
|
||||||
|
@ -489,7 +499,7 @@ class PrintConfig : public GCodeConfig
|
||||||
this->first_layer_extrusion_width.value = 200;
|
this->first_layer_extrusion_width.value = 200;
|
||||||
this->first_layer_extrusion_width.percent = true;
|
this->first_layer_extrusion_width.percent = true;
|
||||||
this->first_layer_speed.value = 30;
|
this->first_layer_speed.value = 30;
|
||||||
this->first_layer_speed.percent = true;
|
this->first_layer_speed.percent = false;
|
||||||
this->first_layer_temperature.values.resize(1);
|
this->first_layer_temperature.values.resize(1);
|
||||||
this->first_layer_temperature.values[0] = 200;
|
this->first_layer_temperature.values[0] = 200;
|
||||||
this->gcode_arcs.value = false;
|
this->gcode_arcs.value = false;
|
||||||
|
@ -510,11 +520,11 @@ class PrintConfig : public GCodeConfig
|
||||||
this->retract_before_travel.values.resize(1);
|
this->retract_before_travel.values.resize(1);
|
||||||
this->retract_before_travel.values[0] = 2;
|
this->retract_before_travel.values[0] = 2;
|
||||||
this->retract_layer_change.values.resize(1);
|
this->retract_layer_change.values.resize(1);
|
||||||
this->retract_layer_change.values[0] = true;
|
this->retract_layer_change.values[0] = false;
|
||||||
this->skirt_distance.value = 6;
|
this->skirt_distance.value = 6;
|
||||||
this->skirt_height.value = 1;
|
this->skirt_height.value = 1;
|
||||||
this->skirts.value = 1;
|
this->skirts.value = 1;
|
||||||
this->slowdown_below_layer_time.value = 30;
|
this->slowdown_below_layer_time.value = 5;
|
||||||
this->spiral_vase.value = false;
|
this->spiral_vase.value = false;
|
||||||
this->standby_temperature_delta.value = -5;
|
this->standby_temperature_delta.value = -5;
|
||||||
this->temperature.values.resize(1);
|
this->temperature.values.resize(1);
|
||||||
|
|
|
@ -110,14 +110,14 @@ PrintObject::reload_model_instances()
|
||||||
return this->set_copies(copies);
|
return this->set_copies(copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
BoundingBox
|
||||||
PrintObject::bounding_box(BoundingBox* bb) const
|
PrintObject::bounding_box() const
|
||||||
{
|
{
|
||||||
// since the object is aligned to origin, bounding box coincides with size
|
// since the object is aligned to origin, bounding box coincides with size
|
||||||
Points pp;
|
Points pp;
|
||||||
pp.push_back(Point(0,0));
|
pp.push_back(Point(0,0));
|
||||||
pp.push_back(this->size);
|
pp.push_back(this->size);
|
||||||
*bb = BoundingBox(pp);
|
return BoundingBox(pp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -190,10 +190,9 @@ PrintObject::get_support_layer(int idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
SupportLayer*
|
SupportLayer*
|
||||||
PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z,
|
PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z)
|
||||||
coordf_t slice_z)
|
|
||||||
{
|
{
|
||||||
SupportLayer* layer = new SupportLayer(id, this, height, print_z, slice_z);
|
SupportLayer* layer = new SupportLayer(id, this, height, print_z, -1);
|
||||||
support_layers.push_back(layer);
|
support_layers.push_back(layer);
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
@ -219,6 +218,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector<t_config_optio
|
||||||
|| *opt_key == "overhangs"
|
|| *opt_key == "overhangs"
|
||||||
|| *opt_key == "first_layer_extrusion_width"
|
|| *opt_key == "first_layer_extrusion_width"
|
||||||
|| *opt_key == "perimeter_extrusion_width"
|
|| *opt_key == "perimeter_extrusion_width"
|
||||||
|
|| *opt_key == "infill_overlap"
|
||||||
|| *opt_key == "thin_walls"
|
|| *opt_key == "thin_walls"
|
||||||
|| *opt_key == "external_perimeters_first") {
|
|| *opt_key == "external_perimeters_first") {
|
||||||
steps.insert(posPerimeters);
|
steps.insert(posPerimeters);
|
||||||
|
|
|
@ -32,9 +32,17 @@ SVG::draw(const Line &line, std::string stroke)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SVG::AddLine(const IntersectionLine &line)
|
SVG::draw(const Lines &lines, std::string stroke)
|
||||||
{
|
{
|
||||||
this->draw(Line(line.a, line.b));
|
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||||
|
this->draw(*it, stroke);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVG::draw(const IntersectionLines &lines, std::string stroke)
|
||||||
|
{
|
||||||
|
for (IntersectionLines::const_iterator it = lines.begin(); it != lines.end(); ++it)
|
||||||
|
this->draw((Line)*it, stroke);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -50,11 +58,25 @@ SVG::draw(const ExPolygon &expolygon, std::string fill)
|
||||||
this->path(d, true);
|
this->path(d, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVG::draw(const ExPolygons &expolygons, std::string fill)
|
||||||
|
{
|
||||||
|
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it)
|
||||||
|
this->draw(*it, fill);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SVG::draw(const Polygon &polygon, std::string fill)
|
SVG::draw(const Polygon &polygon, std::string fill)
|
||||||
{
|
{
|
||||||
this->fill = fill;
|
this->fill = fill;
|
||||||
this->path(this->get_path_d(polygon, true), true);
|
this->path(this->get_path_d(polygon, true), !fill.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVG::draw(const Polygons &polygons, std::string fill)
|
||||||
|
{
|
||||||
|
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
|
||||||
|
this->draw(*it, fill);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -64,6 +86,13 @@ SVG::draw(const Polyline &polyline, std::string stroke)
|
||||||
this->path(this->get_path_d(polyline, false), false);
|
this->path(this->get_path_d(polyline, false), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVG::draw(const Polylines &polylines, std::string stroke)
|
||||||
|
{
|
||||||
|
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
||||||
|
this->draw(*it, fill);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SVG::draw(const Point &point, std::string fill, unsigned int radius)
|
SVG::draw(const Point &point, std::string fill, unsigned int radius)
|
||||||
{
|
{
|
||||||
|
@ -75,6 +104,13 @@ SVG::draw(const Point &point, std::string fill, unsigned int radius)
|
||||||
fprintf(this->f, "%s\n", svg.str().c_str());
|
fprintf(this->f, "%s\n", svg.str().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SVG::draw(const Points &points, std::string fill, unsigned int radius)
|
||||||
|
{
|
||||||
|
for (Points::const_iterator it = points.begin(); it != points.end(); ++it)
|
||||||
|
this->draw(*it, fill, radius);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SVG::path(const std::string &d, bool fill)
|
SVG::path(const std::string &d, bool fill)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,12 +15,17 @@ class SVG
|
||||||
std::string fill, stroke;
|
std::string fill, stroke;
|
||||||
|
|
||||||
SVG(const char* filename);
|
SVG(const char* filename);
|
||||||
void AddLine(const IntersectionLine &line);
|
|
||||||
void draw(const Line &line, std::string stroke = "black");
|
void draw(const Line &line, std::string stroke = "black");
|
||||||
|
void draw(const Lines &lines, std::string stroke = "black");
|
||||||
|
void draw(const IntersectionLines &lines, std::string stroke = "black");
|
||||||
void draw(const ExPolygon &expolygon, std::string fill = "grey");
|
void draw(const ExPolygon &expolygon, std::string fill = "grey");
|
||||||
|
void draw(const ExPolygons &expolygons, std::string fill = "grey");
|
||||||
void draw(const Polygon &polygon, std::string fill = "grey");
|
void draw(const Polygon &polygon, std::string fill = "grey");
|
||||||
|
void draw(const Polygons &polygons, std::string fill = "grey");
|
||||||
void draw(const Polyline &polyline, std::string stroke = "black");
|
void draw(const Polyline &polyline, std::string stroke = "black");
|
||||||
|
void draw(const Polylines &polylines, std::string stroke = "black");
|
||||||
void draw(const Point &point, std::string fill = "black", unsigned int radius = 3);
|
void draw(const Point &point, std::string fill = "black", unsigned int radius = 3);
|
||||||
|
void draw(const Points &points, std::string fill = "black", unsigned int radius = 3);
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -181,6 +181,7 @@ TriangleMesh::WriteOBJFile(char* output_file) {
|
||||||
void TriangleMesh::scale(float factor)
|
void TriangleMesh::scale(float factor)
|
||||||
{
|
{
|
||||||
stl_scale(&(this->stl), factor);
|
stl_scale(&(this->stl), factor);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::scale(const Pointf3 &versor)
|
void TriangleMesh::scale(const Pointf3 &versor)
|
||||||
|
@ -190,41 +191,49 @@ void TriangleMesh::scale(const Pointf3 &versor)
|
||||||
fversor[1] = versor.y;
|
fversor[1] = versor.y;
|
||||||
fversor[2] = versor.z;
|
fversor[2] = versor.z;
|
||||||
stl_scale_versor(&this->stl, fversor);
|
stl_scale_versor(&this->stl, fversor);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::translate(float x, float y, float z)
|
void TriangleMesh::translate(float x, float y, float z)
|
||||||
{
|
{
|
||||||
stl_translate_relative(&(this->stl), x, y, z);
|
stl_translate_relative(&(this->stl), x, y, z);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::rotate_x(float angle)
|
void TriangleMesh::rotate_x(float angle)
|
||||||
{
|
{
|
||||||
stl_rotate_x(&(this->stl), angle);
|
stl_rotate_x(&(this->stl), angle);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::rotate_y(float angle)
|
void TriangleMesh::rotate_y(float angle)
|
||||||
{
|
{
|
||||||
stl_rotate_y(&(this->stl), angle);
|
stl_rotate_y(&(this->stl), angle);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::rotate_z(float angle)
|
void TriangleMesh::rotate_z(float angle)
|
||||||
{
|
{
|
||||||
stl_rotate_z(&(this->stl), angle);
|
stl_rotate_z(&(this->stl), angle);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::flip_x()
|
void TriangleMesh::flip_x()
|
||||||
{
|
{
|
||||||
stl_mirror_yz(&this->stl);
|
stl_mirror_yz(&this->stl);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::flip_y()
|
void TriangleMesh::flip_y()
|
||||||
{
|
{
|
||||||
stl_mirror_xz(&this->stl);
|
stl_mirror_xz(&this->stl);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::flip_z()
|
void TriangleMesh::flip_z()
|
||||||
{
|
{
|
||||||
stl_mirror_xy(&this->stl);
|
stl_mirror_xy(&this->stl);
|
||||||
|
stl_invalidate_shared_vertices(&this->stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TriangleMesh::align_to_origin()
|
void TriangleMesh::align_to_origin()
|
||||||
|
@ -319,8 +328,8 @@ TriangleMesh::merge(const TriangleMesh &mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* this will return scaled ExPolygons */
|
/* this will return scaled ExPolygons */
|
||||||
void
|
ExPolygons
|
||||||
TriangleMesh::horizontal_projection(ExPolygons &retval) const
|
TriangleMesh::horizontal_projection() const
|
||||||
{
|
{
|
||||||
Polygons pp;
|
Polygons pp;
|
||||||
pp.reserve(this->stl.stats.number_of_facets);
|
pp.reserve(this->stl.stats.number_of_facets);
|
||||||
|
@ -337,11 +346,13 @@ TriangleMesh::horizontal_projection(ExPolygons &retval) const
|
||||||
|
|
||||||
// the offset factor was tuned using groovemount.stl
|
// the offset factor was tuned using groovemount.stl
|
||||||
offset(pp, &pp, 0.01 / SCALING_FACTOR);
|
offset(pp, &pp, 0.01 / SCALING_FACTOR);
|
||||||
|
ExPolygons retval;
|
||||||
union_(pp, &retval, true);
|
union_(pp, &retval, true);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Polygon
|
||||||
TriangleMesh::convex_hull(Polygon* hull)
|
TriangleMesh::convex_hull()
|
||||||
{
|
{
|
||||||
this->require_shared_vertices();
|
this->require_shared_vertices();
|
||||||
Points pp;
|
Points pp;
|
||||||
|
@ -350,25 +361,19 @@ TriangleMesh::convex_hull(Polygon* hull)
|
||||||
stl_vertex* v = &this->stl.v_shared[i];
|
stl_vertex* v = &this->stl.v_shared[i];
|
||||||
pp.push_back(Point(v->x / SCALING_FACTOR, v->y / SCALING_FACTOR));
|
pp.push_back(Point(v->x / SCALING_FACTOR, v->y / SCALING_FACTOR));
|
||||||
}
|
}
|
||||||
Slic3r::Geometry::convex_hull(pp, hull);
|
return Slic3r::Geometry::convex_hull(pp);
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TriangleMesh::bounding_box(BoundingBoxf3* bb) const
|
|
||||||
{
|
|
||||||
bb->min.x = this->stl.stats.min.x;
|
|
||||||
bb->min.y = this->stl.stats.min.y;
|
|
||||||
bb->min.z = this->stl.stats.min.z;
|
|
||||||
bb->max.x = this->stl.stats.max.x;
|
|
||||||
bb->max.y = this->stl.stats.max.y;
|
|
||||||
bb->max.z = this->stl.stats.max.z;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBoxf3
|
BoundingBoxf3
|
||||||
TriangleMesh::bounding_box() const
|
TriangleMesh::bounding_box() const
|
||||||
{
|
{
|
||||||
BoundingBoxf3 bb;
|
BoundingBoxf3 bb;
|
||||||
this->bounding_box(&bb);
|
bb.min.x = this->stl.stats.min.x;
|
||||||
|
bb.min.y = this->stl.stats.min.y;
|
||||||
|
bb.min.z = this->stl.stats.min.z;
|
||||||
|
bb.max.x = this->stl.stats.max.x;
|
||||||
|
bb.max.y = this->stl.stats.max.y;
|
||||||
|
bb.max.z = this->stl.stats.max.z;
|
||||||
return bb;
|
return bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +516,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
|
||||||
for (std::vector<Polygons>::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) {
|
for (std::vector<Polygons>::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) {
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
size_t layer_id = loops - layers_p.begin();
|
size_t layer_id = loops - layers_p.begin();
|
||||||
printf("Layer %zu (slice_z = %.2f): ", layer_id, z[layer_id]);
|
printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]);
|
this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]);
|
||||||
|
@ -546,15 +551,19 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
||||||
if (a->z == b->z && a->z == slice_z) {
|
if (a->z == b->z && a->z == slice_z) {
|
||||||
// edge is horizontal and belongs to the current layer
|
// edge is horizontal and belongs to the current layer
|
||||||
|
|
||||||
/* We assume that this method is never being called for horizontal
|
stl_vertex &v0 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ];
|
||||||
facets, so no other edge is going to be on this layer. */
|
stl_vertex &v1 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ];
|
||||||
stl_vertex* v0 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ];
|
stl_vertex &v2 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ];
|
||||||
stl_vertex* v1 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ];
|
|
||||||
stl_vertex* v2 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ];
|
|
||||||
IntersectionLine line;
|
IntersectionLine line;
|
||||||
if (min_z == max_z) {
|
if (min_z == max_z) {
|
||||||
line.edge_type = feHorizontal;
|
line.edge_type = feHorizontal;
|
||||||
} else if (v0->z < slice_z || v1->z < slice_z || v2->z < slice_z) {
|
if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) {
|
||||||
|
/* if normal points downwards this is a bottom horizontal facet so we reverse
|
||||||
|
its point order */
|
||||||
|
std::swap(a, b);
|
||||||
|
std::swap(a_id, b_id);
|
||||||
|
}
|
||||||
|
} else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) {
|
||||||
line.edge_type = feTop;
|
line.edge_type = feTop;
|
||||||
std::swap(a, b);
|
std::swap(a, b);
|
||||||
std::swap(a_id, b_id);
|
std::swap(a_id, b_id);
|
||||||
|
@ -628,12 +637,9 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
||||||
void
|
void
|
||||||
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
|
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SVG svg("lines.svg");
|
SVG svg("lines.svg");
|
||||||
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) {
|
svg.draw(lines);
|
||||||
svg.AddLine(*line);
|
|
||||||
}
|
|
||||||
svg.Close();
|
svg.Close();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -704,16 +710,16 @@ TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* l
|
||||||
// find a line starting where last one finishes
|
// find a line starting where last one finishes
|
||||||
IntersectionLine* next_line = NULL;
|
IntersectionLine* next_line = NULL;
|
||||||
if (loop.back()->edge_b_id != -1) {
|
if (loop.back()->edge_b_id != -1) {
|
||||||
IntersectionLinePtrs* candidates = &(by_edge_a_id[loop.back()->edge_b_id]);
|
IntersectionLinePtrs &candidates = by_edge_a_id[loop.back()->edge_b_id];
|
||||||
for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) {
|
for (IntersectionLinePtrs::iterator lineptr = candidates.begin(); lineptr != candidates.end(); ++lineptr) {
|
||||||
if ((*lineptr)->skip) continue;
|
if ((*lineptr)->skip) continue;
|
||||||
next_line = *lineptr;
|
next_line = *lineptr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (next_line == NULL && loop.back()->b_id != -1) {
|
if (next_line == NULL && loop.back()->b_id != -1) {
|
||||||
IntersectionLinePtrs* candidates = &(by_a_id[loop.back()->b_id]);
|
IntersectionLinePtrs &candidates = by_a_id[loop.back()->b_id];
|
||||||
for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) {
|
for (IntersectionLinePtrs::iterator lineptr = candidates.begin(); lineptr != candidates.end(); ++lineptr) {
|
||||||
if ((*lineptr)->skip) continue;
|
if ((*lineptr)->skip) continue;
|
||||||
next_line = *lineptr;
|
next_line = *lineptr;
|
||||||
break;
|
break;
|
||||||
|
@ -841,9 +847,9 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
||||||
of the loops, since the Orientation() function provided by Clipper
|
of the loops, since the Orientation() function provided by Clipper
|
||||||
would do the same, thus repeating the calculation */
|
would do the same, thus repeating the calculation */
|
||||||
Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
||||||
if (area[*loop_idx] >= 0) {
|
if (area[*loop_idx] > +EPSILON) {
|
||||||
p_slices.push_back(*loop);
|
p_slices.push_back(*loop);
|
||||||
} else {
|
} else if (area[*loop_idx] < -EPSILON) {
|
||||||
diff(p_slices, *loop, &p_slices);
|
diff(p_slices, *loop, &p_slices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,9 +42,8 @@ class TriangleMesh
|
||||||
void rotate(double angle, Point* center);
|
void rotate(double angle, Point* center);
|
||||||
TriangleMeshPtrs split() const;
|
TriangleMeshPtrs split() const;
|
||||||
void merge(const TriangleMesh &mesh);
|
void merge(const TriangleMesh &mesh);
|
||||||
void horizontal_projection(ExPolygons &retval) const;
|
ExPolygons horizontal_projection() const;
|
||||||
void convex_hull(Polygon* hull);
|
Polygon convex_hull();
|
||||||
void bounding_box(BoundingBoxf3* bb) const;
|
|
||||||
BoundingBoxf3 bounding_box() const;
|
BoundingBoxf3 bounding_box() const;
|
||||||
void reset_repair_stats();
|
void reset_repair_stats();
|
||||||
bool needed_repair() const;
|
bool needed_repair() const;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#define SLIC3R_VERSION "1.2.5-dev"
|
#define SLIC3R_VERSION "1.2.6-dev"
|
||||||
|
|
||||||
#define EPSILON 1e-4
|
#define EPSILON 1e-4
|
||||||
#define SCALING_FACTOR 0.000001
|
#define SCALING_FACTOR 0.000001
|
||||||
|
|
|
@ -4,7 +4,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 46;
|
use Test::More tests => 49;
|
||||||
|
|
||||||
is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
|
is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
|
||||||
'hello world';
|
'hello world';
|
||||||
|
@ -79,7 +79,7 @@ my $cube = {
|
||||||
my $m = Slic3r::TriangleMesh->new;
|
my $m = Slic3r::TriangleMesh->new;
|
||||||
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
|
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
|
||||||
$m->repair;
|
$m->repair;
|
||||||
my @z = (2,4,8,6,8,10,12,14,16,18,20);
|
my @z = (0,2,4,8,6,8,10,12,14,16,18,20);
|
||||||
my $result = $m->slice(\@z);
|
my $result = $m->slice(\@z);
|
||||||
my $SCALING_FACTOR = 0.000001;
|
my $SCALING_FACTOR = 0.000001;
|
||||||
for my $i (0..$#z) {
|
for my $i (0..$#z) {
|
||||||
|
@ -95,8 +95,19 @@ my $cube = {
|
||||||
[ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ],
|
[ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ],
|
||||||
);
|
);
|
||||||
$m->repair;
|
$m->repair;
|
||||||
|
{
|
||||||
|
# at Z = 10 we have a top horizontal surface
|
||||||
my $slices = $m->slice([ 5, 10 ]);
|
my $slices = $m->slice([ 5, 10 ]);
|
||||||
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a tangent plane includes its area';
|
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a top tangent plane includes its area';
|
||||||
|
}
|
||||||
|
$m->flip_z;
|
||||||
|
{
|
||||||
|
# this second test also checks that performing a second slice on a mesh after
|
||||||
|
# a transformation works properly (shared_vertices is correctly invalidated);
|
||||||
|
# at Z = -10 we have a bottom horizontal surface
|
||||||
|
my $slices = $m->slice([ -5, -10 ]);
|
||||||
|
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 39;
|
use Test::More tests => 40;
|
||||||
|
|
||||||
use constant PI => 4 * atan2(1, 1);
|
use constant PI => 4 * atan2(1, 1);
|
||||||
use constant EPSILON => 1E-4;
|
use constant EPSILON => 1E-4;
|
||||||
|
@ -71,4 +71,11 @@ foreach my $base_angle (0, PI/4, PI/2, PI) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $a = Slic3r::Line->new([100, 0], [200, 0]);
|
||||||
|
my $b = Slic3r::Line->new([300, 300], [300, 100]);
|
||||||
|
my $r = $a->intersection_infinite($b);
|
||||||
|
is_deeply $r->pp, [300, 0], 'intersection_infinite';
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
|
@ -4,7 +4,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 18;
|
use Test::More tests => 19;
|
||||||
|
|
||||||
my $square = Slic3r::Polygon->new( # ccw
|
my $square = Slic3r::Polygon->new( # ccw
|
||||||
[200, 100],
|
[200, 100],
|
||||||
|
@ -155,8 +155,8 @@ if (0) { # Clipper does not preserve polyline orientation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Disabled until Clipper bug #96 (our issue #2028) is fixed
|
{
|
||||||
if (0) {
|
# Clipper bug #96 (our issue #2028)
|
||||||
my $subject = Slic3r::Polyline->new(
|
my $subject = Slic3r::Polyline->new(
|
||||||
[44735000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000],[44730000,31936670]
|
[44735000,31936670],[55270000,31936670],[55270000,25270000],[74730000,25270000],[74730000,44730000],[68063296,44730000],[68063296,55270000],[74730000,55270000],[74730000,74730000],[55270000,74730000],[55270000,68063296],[44730000,68063296],[44730000,74730000],[25270000,74730000],[25270000,55270000],[31936670,55270000],[31936670,44730000],[25270000,44730000],[25270000,25270000],[44730000,25270000],[44730000,31936670]
|
||||||
);
|
);
|
||||||
|
@ -178,4 +178,19 @@ if (0) {
|
||||||
is scalar(@$result), 1, 'intersection_ppl - result is not empty';
|
is scalar(@$result), 1, 'intersection_ppl - result is not empty';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
# Disabled until Clipper bug #122 is fixed
|
||||||
|
my $subject = [
|
||||||
|
Slic3r::Polyline->new([1975,1975],[25,1975],[25,25],[1975,25],[1975,1975]),
|
||||||
|
];
|
||||||
|
my $clip = [
|
||||||
|
Slic3r::Polygon->new([2025,2025],[-25,2025],[-25,-25],[2025,-25]),
|
||||||
|
Slic3r::Polygon->new([525,525],[525,1475],[1475,1475],[1475,525]),
|
||||||
|
];
|
||||||
|
my $result = Slic3r::Geometry::Clipper::intersection_pl($subject, $clip);
|
||||||
|
###use XXX; YYY $subject->[0]->wkt, [map $_->wkt, @$clip], $result->[0]->wkt;
|
||||||
|
is scalar(@$result), 1, 'intersection_pl - result is not empty';
|
||||||
|
is scalar(@{$result->[0]}), 5, 'intersection_pl - result is not empty';
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
void scale(double factor);
|
void scale(double factor);
|
||||||
void translate(double x, double y);
|
void translate(double x, double y);
|
||||||
void offset(double delta);
|
void offset(double delta);
|
||||||
Polygon* polygon()
|
Clone<Polygon> polygon();
|
||||||
%code{% RETVAL = new Polygon(); THIS->polygon(RETVAL); %};
|
|
||||||
Clone<Point> size();
|
Clone<Point> size();
|
||||||
Clone<Point> center();
|
Clone<Point> center();
|
||||||
Clone<Point> min_point() %code{% RETVAL = THIS->min; %};
|
Clone<Point> min_point() %code{% RETVAL = THIS->min; %};
|
||||||
|
@ -88,6 +87,8 @@ new_from_points(CLASS, points)
|
||||||
void translate(double x, double y, double z);
|
void translate(double x, double y, double z);
|
||||||
Clone<Pointf3> size();
|
Clone<Pointf3> size();
|
||||||
Clone<Pointf3> center();
|
Clone<Pointf3> center();
|
||||||
|
Clone<Pointf3> min_point() %code{% RETVAL = THIS->min; %};
|
||||||
|
Clone<Pointf3> max_point() %code{% RETVAL = THIS->max; %};
|
||||||
double x_min() %code{% RETVAL = THIS->min.x; %};
|
double x_min() %code{% RETVAL = THIS->min.x; %};
|
||||||
double x_max() %code{% RETVAL = THIS->max.x; %};
|
double x_max() %code{% RETVAL = THIS->max.x; %};
|
||||||
double y_min() %code{% RETVAL = THIS->min.y; %};
|
double y_min() %code{% RETVAL = THIS->min.y; %};
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
Polygons polygons()
|
Polygons polygons()
|
||||||
%code{% RETVAL = *THIS; %};
|
%code{% RETVAL = *THIS; %};
|
||||||
|
Clone<Polygon> convex_hull();
|
||||||
%{
|
%{
|
||||||
|
|
||||||
ExPolygonCollection*
|
ExPolygonCollection*
|
||||||
|
@ -76,13 +77,5 @@ ExPolygonCollection::append(...)
|
||||||
THIS->expolygons.push_back(expolygon);
|
THIS->expolygons.push_back(expolygon);
|
||||||
}
|
}
|
||||||
|
|
||||||
Polygon*
|
|
||||||
ExPolygonCollection::convex_hull()
|
|
||||||
CODE:
|
|
||||||
RETVAL = new Polygon ();
|
|
||||||
THIS->convex_hull(RETVAL);
|
|
||||||
OUTPUT:
|
|
||||||
RETVAL
|
|
||||||
|
|
||||||
%}
|
%}
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,8 +24,11 @@
|
||||||
Clone<Point> last_point();
|
Clone<Point> last_point();
|
||||||
int count()
|
int count()
|
||||||
%code{% RETVAL = THIS->entities.size(); %};
|
%code{% RETVAL = THIS->entities.size(); %};
|
||||||
|
bool empty()
|
||||||
|
%code{% RETVAL = THIS->entities.empty(); %};
|
||||||
std::vector<size_t> orig_indices()
|
std::vector<size_t> orig_indices()
|
||||||
%code{% RETVAL = THIS->orig_indices; %};
|
%code{% RETVAL = THIS->orig_indices; %};
|
||||||
|
Polygons grow();
|
||||||
%{
|
%{
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
bool make_counter_clockwise();
|
bool make_counter_clockwise();
|
||||||
Clone<Point> first_point();
|
Clone<Point> first_point();
|
||||||
Clone<Point> last_point();
|
Clone<Point> last_point();
|
||||||
Polygon* polygon()
|
Clone<Polygon> polygon();
|
||||||
%code{% RETVAL = new Polygon (*THIS); %};
|
|
||||||
void append(ExtrusionPath* path)
|
void append(ExtrusionPath* path)
|
||||||
%code{% THIS->paths.push_back(*path); %};
|
%code{% THIS->paths.push_back(*path); %};
|
||||||
double length();
|
double length();
|
||||||
|
@ -31,6 +30,7 @@
|
||||||
bool is_perimeter();
|
bool is_perimeter();
|
||||||
bool is_infill();
|
bool is_infill();
|
||||||
bool is_solid_infill();
|
bool is_solid_infill();
|
||||||
|
Polygons grow();
|
||||||
%{
|
%{
|
||||||
|
|
||||||
SV*
|
SV*
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
std::string gcode(Extruder* extruder, double e, double F,
|
std::string gcode(Extruder* extruder, double e, double F,
|
||||||
double xofs, double yofs, std::string extrusion_axis,
|
double xofs, double yofs, std::string extrusion_axis,
|
||||||
std::string gcode_line_suffix);
|
std::string gcode_line_suffix);
|
||||||
|
Polygons grow();
|
||||||
%{
|
%{
|
||||||
|
|
||||||
ExtrusionPath*
|
ExtrusionPath*
|
||||||
|
|
28
xs/xsp/GUI_3DScene.xsp
Normal file
28
xs/xsp/GUI_3DScene.xsp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
%module{Slic3r::XS};
|
||||||
|
|
||||||
|
#include <myinit.h>
|
||||||
|
#include "libslic3r/GUI/3DScene.hpp"
|
||||||
|
|
||||||
|
%name{Slic3r::GUI::_3DScene::GLVertexArray} class GLVertexArray {
|
||||||
|
GLVertexArray();
|
||||||
|
~GLVertexArray();
|
||||||
|
void load_mesh(TriangleMesh* mesh) const
|
||||||
|
%code%{ THIS->load_mesh(*mesh); %};
|
||||||
|
size_t size() const
|
||||||
|
%code%{ RETVAL = THIS->verts.size(); %};
|
||||||
|
void* verts_ptr() const
|
||||||
|
%code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->verts.front(); %};
|
||||||
|
void* norms_ptr() const
|
||||||
|
%code%{ RETVAL = THIS->verts.empty() ? 0 : &THIS->norms.front(); %};
|
||||||
|
};
|
||||||
|
|
||||||
|
%package{Slic3r::GUI::_3DScene};
|
||||||
|
%{
|
||||||
|
|
||||||
|
void
|
||||||
|
_extrusionentity_to_verts_do(Lines lines, std::vector<double> widths, std::vector<double> heights, bool closed, double top_z, Point* copy, GLVertexArray* qverts, GLVertexArray* tverts)
|
||||||
|
CODE:
|
||||||
|
_3DScene::_extrusionentity_to_verts_do(lines, widths, heights, closed,
|
||||||
|
top_z, *copy, qverts, tverts);
|
||||||
|
|
||||||
|
%}
|
|
@ -29,12 +29,11 @@ directions_parallel_within(angle1, angle2, max_diff)
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
Polygon*
|
Clone<Polygon>
|
||||||
convex_hull(points)
|
convex_hull(points)
|
||||||
Points points
|
Points points
|
||||||
CODE:
|
CODE:
|
||||||
RETVAL = new Polygon ();
|
RETVAL = Slic3r::Geometry::convex_hull(points);
|
||||||
Slic3r::Geometry::convex_hull(points, RETVAL);
|
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
@ -79,4 +78,13 @@ deg2rad(angle)
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
|
||||||
|
Polygons
|
||||||
|
simplify_polygons(polygons, tolerance)
|
||||||
|
Polygons polygons
|
||||||
|
double tolerance
|
||||||
|
CODE:
|
||||||
|
Slic3r::Geometry::simplify_polygons(polygons, tolerance, &RETVAL);
|
||||||
|
OUTPUT:
|
||||||
|
RETVAL
|
||||||
|
|
||||||
%}
|
%}
|
||||||
|
|
|
@ -26,15 +26,17 @@
|
||||||
Ref<ExtrusionEntityCollection> fills()
|
Ref<ExtrusionEntityCollection> fills()
|
||||||
%code%{ RETVAL = &THIS->fills; %};
|
%code%{ RETVAL = &THIS->fills; %};
|
||||||
|
|
||||||
Flow* flow(FlowRole role, bool bridge = false, double width = -1)
|
Clone<Flow> flow(FlowRole role, bool bridge = false, double width = -1)
|
||||||
%code%{ RETVAL = new Flow(THIS->flow(role, bridge, width)); %};
|
%code%{ RETVAL = THIS->flow(role, bridge, width); %};
|
||||||
void merge_slices();
|
void merge_slices();
|
||||||
|
void prepare_fill_surfaces();
|
||||||
};
|
};
|
||||||
|
|
||||||
%name{Slic3r::Layer} class Layer {
|
%name{Slic3r::Layer} class Layer {
|
||||||
// owned by PrintObject, no constructor/destructor
|
// owned by PrintObject, no constructor/destructor
|
||||||
|
|
||||||
int id();
|
int id();
|
||||||
|
void set_id(int id);
|
||||||
Ref<PrintObject> object();
|
Ref<PrintObject> object();
|
||||||
Ref<Layer> upper_layer()
|
Ref<Layer> upper_layer()
|
||||||
%code%{ RETVAL = THIS->upper_layer; %};
|
%code%{ RETVAL = THIS->upper_layer; %};
|
||||||
|
@ -89,6 +91,7 @@
|
||||||
// copies of some Layer methods, because the parameter wrapper code
|
// copies of some Layer methods, because the parameter wrapper code
|
||||||
// gets confused about getting a Layer::Support instead of a Layer
|
// gets confused about getting a Layer::Support instead of a Layer
|
||||||
int id();
|
int id();
|
||||||
|
void set_id(int id);
|
||||||
Ref<PrintObject> object();
|
Ref<PrintObject> object();
|
||||||
Ref<SupportLayer> upper_layer()
|
Ref<SupportLayer> upper_layer()
|
||||||
%code%{ RETVAL = (SupportLayer*)THIS->upper_layer; %};
|
%code%{ RETVAL = (SupportLayer*)THIS->upper_layer; %};
|
||||||
|
|
|
@ -28,11 +28,19 @@
|
||||||
bool parallel_to(double angle);
|
bool parallel_to(double angle);
|
||||||
bool parallel_to_line(Line* line)
|
bool parallel_to_line(Line* line)
|
||||||
%code{% RETVAL = THIS->parallel_to(*line); %};
|
%code{% RETVAL = THIS->parallel_to(*line); %};
|
||||||
Point* midpoint();
|
Clone<Point> midpoint();
|
||||||
Clone<Point> point_at(double distance);
|
Clone<Point> point_at(double distance);
|
||||||
Polyline* as_polyline()
|
Clone<Point> intersection_infinite(Line* other)
|
||||||
%code{% RETVAL = new Polyline(*THIS); %};
|
%code{%
|
||||||
|
Point p;
|
||||||
|
bool res = THIS->intersection_infinite(*other, &p);
|
||||||
|
if (!res) CONFESS("Intersection failed");
|
||||||
|
RETVAL = p;
|
||||||
|
%};
|
||||||
|
Clone<Polyline> as_polyline()
|
||||||
|
%code{% RETVAL = Polyline(*THIS); %};
|
||||||
Clone<Point> normal();
|
Clone<Point> normal();
|
||||||
|
Clone<Point> vector();
|
||||||
%{
|
%{
|
||||||
|
|
||||||
Line*
|
Line*
|
||||||
|
@ -79,4 +87,5 @@ Line::coincides_with(line_sv)
|
||||||
Ref<Pointf3> b()
|
Ref<Pointf3> b()
|
||||||
%code{% RETVAL = &THIS->b; %};
|
%code{% RETVAL = &THIS->b; %};
|
||||||
Clone<Pointf3> intersect_plane(double z);
|
Clone<Pointf3> intersect_plane(double z);
|
||||||
|
void scale(double factor);
|
||||||
};
|
};
|
||||||
|
|
|
@ -59,19 +59,13 @@
|
||||||
// void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb);
|
// void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb);
|
||||||
bool has_objects_with_no_instances();
|
bool has_objects_with_no_instances();
|
||||||
bool add_default_instances();
|
bool add_default_instances();
|
||||||
BoundingBoxf3* bounding_box()
|
Clone<BoundingBoxf3> bounding_box();
|
||||||
%code%{
|
|
||||||
RETVAL = new BoundingBoxf3();
|
|
||||||
THIS->bounding_box(RETVAL);
|
|
||||||
%};
|
|
||||||
void center_instances_around_point(Pointf* point)
|
void center_instances_around_point(Pointf* point)
|
||||||
%code%{ THIS->center_instances_around_point(*point); %};
|
%code%{ THIS->center_instances_around_point(*point); %};
|
||||||
void align_instances_to_origin();
|
void align_instances_to_origin();
|
||||||
void translate(double x, double y, double z);
|
void translate(double x, double y, double z);
|
||||||
TriangleMesh* mesh()
|
Clone<TriangleMesh> mesh();
|
||||||
%code%{ RETVAL = new TriangleMesh(); THIS->mesh(RETVAL); %};
|
Clone<TriangleMesh> raw_mesh();
|
||||||
TriangleMesh* raw_mesh()
|
|
||||||
%code%{ RETVAL = new TriangleMesh(); THIS->raw_mesh(RETVAL); %};
|
|
||||||
// void split_meshes();
|
// void split_meshes();
|
||||||
// std::string get_material_name(t_model_material_id material_id);
|
// std::string get_material_name(t_model_material_id material_id);
|
||||||
|
|
||||||
|
@ -119,20 +113,10 @@ ModelMaterial::attributes()
|
||||||
|
|
||||||
void invalidate_bounding_box();
|
void invalidate_bounding_box();
|
||||||
void update_bounding_box();
|
void update_bounding_box();
|
||||||
TriangleMesh* mesh()
|
Clone<TriangleMesh> mesh();
|
||||||
%code%{ RETVAL = new TriangleMesh(); THIS->mesh(RETVAL); %};
|
Clone<TriangleMesh> raw_mesh();
|
||||||
TriangleMesh* raw_mesh()
|
Clone<BoundingBoxf3> raw_bounding_box();
|
||||||
%code%{ RETVAL = new TriangleMesh(); THIS->raw_mesh(RETVAL); %};
|
Clone<BoundingBoxf3> instance_bounding_box(int idx);
|
||||||
BoundingBoxf3* raw_bounding_box()
|
|
||||||
%code%{
|
|
||||||
RETVAL = new BoundingBoxf3();
|
|
||||||
THIS->raw_bounding_box(RETVAL);
|
|
||||||
%};
|
|
||||||
BoundingBoxf3* instance_bounding_box(int idx)
|
|
||||||
%code%{
|
|
||||||
RETVAL = new BoundingBoxf3();
|
|
||||||
THIS->instance_bounding_box(idx, RETVAL);
|
|
||||||
%};
|
|
||||||
|
|
||||||
Ref<BoundingBoxf3> _bounding_box(BoundingBoxf3* new_bbox = NULL)
|
Ref<BoundingBoxf3> _bounding_box(BoundingBoxf3* new_bbox = NULL)
|
||||||
%code{%
|
%code{%
|
||||||
|
@ -147,11 +131,7 @@ ModelMaterial::attributes()
|
||||||
|
|
||||||
RETVAL = &THIS->_bounding_box;
|
RETVAL = &THIS->_bounding_box;
|
||||||
%};
|
%};
|
||||||
BoundingBoxf3* bounding_box()
|
Clone<BoundingBoxf3> bounding_box();
|
||||||
%code%{
|
|
||||||
RETVAL = new BoundingBoxf3();
|
|
||||||
THIS->bounding_box(RETVAL);
|
|
||||||
%};
|
|
||||||
|
|
||||||
%name{_add_volume} Ref<ModelVolume> add_volume(TriangleMesh* mesh)
|
%name{_add_volume} Ref<ModelVolume> add_volume(TriangleMesh* mesh)
|
||||||
%code%{ RETVAL = THIS->add_volume(*mesh); %};
|
%code%{ RETVAL = THIS->add_volume(*mesh); %};
|
||||||
|
|
|
@ -10,6 +10,6 @@
|
||||||
~MotionPlanner();
|
~MotionPlanner();
|
||||||
|
|
||||||
int islands_count();
|
int islands_count();
|
||||||
Polyline* shortest_path(Point* from, Point* to)
|
Clone<Polyline> shortest_path(Point* from, Point* to)
|
||||||
%code%{ RETVAL = new Polyline(); THIS->shortest_path(*from, *to, RETVAL); %};
|
%code%{ RETVAL = THIS->shortest_path(*from, *to); %};
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,9 +22,13 @@
|
||||||
%code{% RETVAL = THIS->x; %};
|
%code{% RETVAL = THIS->x; %};
|
||||||
long y()
|
long y()
|
||||||
%code{% RETVAL = THIS->y; %};
|
%code{% RETVAL = THIS->y; %};
|
||||||
|
void set_x(long val)
|
||||||
|
%code{% THIS->x = val; %};
|
||||||
|
void set_y(long val)
|
||||||
|
%code{% THIS->y = val; %};
|
||||||
int nearest_point_index(Points points);
|
int nearest_point_index(Points points);
|
||||||
Point* nearest_point(Points points)
|
Clone<Point> nearest_point(Points points)
|
||||||
%code{% RETVAL = new Point(); THIS->nearest_point(points, RETVAL); %};
|
%code{% Point p; THIS->nearest_point(points, &p); RETVAL = p; %};
|
||||||
double distance_to(Point* point)
|
double distance_to(Point* point)
|
||||||
%code{% RETVAL = THIS->distance_to(*point); %};
|
%code{% RETVAL = THIS->distance_to(*point); %};
|
||||||
double distance_to_line(Line* line)
|
double distance_to_line(Line* line)
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue