Merge branch 'master' into sender

Conflicts:
	Build.PL
This commit is contained in:
Alessandro Ranellucci 2015-02-01 14:07:32 +01:00
commit 3ae6f2630e
106 changed files with 2262 additions and 994 deletions

View file

@ -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';

View file

@ -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:

View file

@ -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 {};

View file

@ -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"

View file

@ -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 ],

View file

@ -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)};
} }

View file

@ -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,

View file

@ -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 = ();

View file

@ -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);

View 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) {

View file

@ -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";

View 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;

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View 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;

View file

@ -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) {

View file

@ -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');

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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) = @_;

View file

@ -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;
} }

View file

@ -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;

View file

@ -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);
} }
} }
} }

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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:

View file

@ -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

View file

@ -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';
} }

View file

@ -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

View file

@ -29,7 +29,7 @@ use Slic3r::Test;
} }
}); });
ok abs($retracted) < epsilon, 'all retractions are compensated'; ok abs($retracted) < 0.01, 'all retractions are compensated';
} }

View file

@ -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

View file

@ -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);

View file

@ -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';

View file

@ -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);

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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());
} }

View file

@ -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;
}; };
}; };

View file

@ -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;
} }

View file

@ -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

View file

@ -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;
}; };

View file

@ -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

View file

@ -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;
}; };
} }

View file

@ -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");

View file

@ -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;
}; };
} }

View file

@ -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);
} }

View file

@ -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);
} }

View 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 &copy,
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
}

View 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 &copy,
GLVertexArray* qverts, GLVertexArray* tverts);
};
}
#endif

View file

@ -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
{ {

View file

@ -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:

View file

@ -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)
{ {

View file

@ -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();
}; };

View file

@ -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

View file

@ -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

View file

@ -26,9 +26,10 @@ class Line
void rotate(double angle, const Point &center); void rotate(double angle, const Point &center);
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);

View file

@ -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();
} }

View file

@ -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);

View file

@ -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

View file

@ -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);
}; };
} }

View file

@ -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

View file

@ -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);

View file

@ -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 &center); void rotate(double angle, const Pointf &center);
@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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)
{ {

View file

@ -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:

View file

@ -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);
} }
} }

View file

@ -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;

View file

@ -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

View file

@ -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';
}
} }
{ {

View file

@ -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__

View file

@ -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__

View file

@ -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; %};

View file

@ -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
%} %}
}; };

View file

@ -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

View file

@ -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*

View file

@ -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
View 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);
%}

View file

@ -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
%} %}

View file

@ -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; %};

View file

@ -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);
}; };

View file

@ -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); %};

View file

@ -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); %};
}; };

View file

@ -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