Merge branch 'master' into sender
This commit is contained in:
commit
d2172b4383
47 changed files with 860 additions and 429 deletions
|
@ -2,9 +2,9 @@ language: perl
|
||||||
install: true
|
install: true
|
||||||
script: perl ./Build.PL
|
script: perl ./Build.PL
|
||||||
perl:
|
perl:
|
||||||
- "5.12"
|
|
||||||
- "5.14"
|
- "5.14"
|
||||||
- "5.18"
|
- "5.18"
|
||||||
|
- "5.20"
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
|
|
|
@ -8,7 +8,7 @@ Slic3r [](
|
||||||
Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for
|
Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for
|
||||||
3D printers. It's compatible with any modern printer based on the RepRap toolchain,
|
3D printers. It's compatible with any modern printer based on the RepRap toolchain,
|
||||||
including all those based on the Marlin, Sprinter and Repetier firmware. It also works
|
including all those based on the Marlin, Sprinter and Repetier firmware. It also works
|
||||||
with Mach3 and LinuxCNC controllers.
|
with Mach3, LinuxCNC and Machinekit controllers.
|
||||||
|
|
||||||
See the [project homepage](http://slic3r.org/) at slic3r.org and the
|
See the [project homepage](http://slic3r.org/) at slic3r.org and the
|
||||||
[manual](http://manual.slic3r.org/) for more information.
|
[manual](http://manual.slic3r.org/) for more information.
|
||||||
|
@ -30,7 +30,7 @@ Key features are:
|
||||||
* **multi-platform** (Linux/Mac/Win) and packaged as standalone-app with no dependencies required
|
* **multi-platform** (Linux/Mac/Win) and packaged as standalone-app with no dependencies required
|
||||||
* complete **command-line interface** to use it with no GUI
|
* complete **command-line interface** to use it with no GUI
|
||||||
* multi-material **(multiple extruders)** object printing
|
* multi-material **(multiple extruders)** object printing
|
||||||
* multiple G-code flavors supported (RepRap, Makerbot, Mach3 etc.)
|
* multiple G-code flavors supported (RepRap, Makerbot, Mach3, Machinekit etc.)
|
||||||
* ability to plate **multiple objects having distinct print settings**
|
* ability to plate **multiple objects having distinct print settings**
|
||||||
* **multithread** processing
|
* **multithread** processing
|
||||||
* **STL auto-repair** (tolerance for broken models)
|
* **STL auto-repair** (tolerance for broken models)
|
||||||
|
@ -109,6 +109,8 @@ The author of the Silk icon set is Mark James.
|
||||||
-j, --threads <num> Number of threads to use (1+, default: 2)
|
-j, --threads <num> Number of threads to use (1+, default: 2)
|
||||||
|
|
||||||
GUI options:
|
GUI options:
|
||||||
|
--gui Forces the GUI launch instead of command line slicing (if you
|
||||||
|
supply a model file, it will be loaded into the plater)
|
||||||
--no-plater Disable the plater tab
|
--no-plater Disable the plater tab
|
||||||
--gui-mode Overrides the configured mode (simple/expert)
|
--gui-mode Overrides the configured mode (simple/expert)
|
||||||
--autosave <file> Automatically export current configuration to the specified file
|
--autosave <file> Automatically export current configuration to the specified file
|
||||||
|
@ -130,7 +132,7 @@ The author of the Silk icon set is Mark James.
|
||||||
(default: 100,100)
|
(default: 100,100)
|
||||||
--z-offset Additional height in mm to add to vertical coordinates
|
--z-offset Additional height in mm to add to vertical coordinates
|
||||||
(+/-, default: 0)
|
(+/-, default: 0)
|
||||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/no-extrusion,
|
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/no-extrusion,
|
||||||
default: reprap)
|
default: reprap)
|
||||||
--use-relative-e-distances Enable this to get relative E values (default: no)
|
--use-relative-e-distances Enable this to get relative E values (default: no)
|
||||||
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
||||||
|
|
|
@ -243,7 +243,7 @@ sub validate {
|
||||||
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
||||||
|
|
||||||
die "--use-firmware-retraction is only supported by Marlin firmware\n"
|
die "--use-firmware-retraction is only supported by Marlin firmware\n"
|
||||||
if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap';
|
if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit';
|
||||||
|
|
||||||
die "--use-firmware-retraction is not compatible with --wipe\n"
|
die "--use-firmware-retraction is not compatible with --wipe\n"
|
||||||
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
|
if $self->use_firmware_retraction && first {$_} @{$self->wipe};
|
||||||
|
|
|
@ -49,9 +49,10 @@ sub make_fill {
|
||||||
|
|
||||||
Slic3r::debugf "Filling layer %d:\n", $layerm->id;
|
Slic3r::debugf "Filling layer %d:\n", $layerm->id;
|
||||||
|
|
||||||
my $fill_density = $layerm->config->fill_density;
|
my $fill_density = $layerm->config->fill_density;
|
||||||
my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL);
|
my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL);
|
||||||
my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL);
|
my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL);
|
||||||
|
my $top_solid_infill_flow = $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL);
|
||||||
|
|
||||||
my @surfaces = ();
|
my @surfaces = ();
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ sub make_fill {
|
||||||
if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->id == 0)) {
|
if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->id == 0)) {
|
||||||
$is_solid[$i] = 1;
|
$is_solid[$i] = 1;
|
||||||
$fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP)
|
$fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP)
|
||||||
? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width
|
? $top_solid_infill_flow->width
|
||||||
: $solid_infill_flow->width;
|
: $solid_infill_flow->width;
|
||||||
$pattern[$i] = $groups[$i][0]->is_external
|
$pattern[$i] = $groups[$i][0]->is_external
|
||||||
? $layerm->config->external_fill_pattern
|
? $layerm->config->external_fill_pattern
|
||||||
|
|
|
@ -584,11 +584,14 @@ sub wipe {
|
||||||
$gcode .= $gcodegen->writer->extrude_to_xy(
|
$gcode .= $gcodegen->writer->extrude_to_xy(
|
||||||
$gcodegen->point_to_gcode($line->b),
|
$gcodegen->point_to_gcode($line->b),
|
||||||
-$dE,
|
-$dE,
|
||||||
'retract' . ($gcodegen->enable_cooling_markers ? ';_WIPE' : ''),
|
'wipe and retract' . ($gcodegen->enable_cooling_markers ? ';_WIPE' : ''),
|
||||||
);
|
);
|
||||||
$retracted += $dE;
|
$retracted += $dE;
|
||||||
}
|
}
|
||||||
$gcodegen->writer->extruder->set_retracted($gcodegen->writer->extruder->retracted + $retracted);
|
$gcodegen->writer->extruder->set_retracted($gcodegen->writer->extruder->retracted + $retracted);
|
||||||
|
|
||||||
|
# prevent wiping again on same path
|
||||||
|
$self->path(undef);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $gcode;
|
return $gcode;
|
||||||
|
|
|
@ -34,7 +34,7 @@ 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
|
||||||
:filedialog);
|
:filedialog);
|
||||||
use Wx::Event qw(EVT_IDLE);
|
use Wx::Event qw(EVT_IDLE EVT_COMMAND);
|
||||||
use base 'Wx::App';
|
use base 'Wx::App';
|
||||||
|
|
||||||
use constant FILE_WILDCARDS => {
|
use constant FILE_WILDCARDS => {
|
||||||
|
@ -70,6 +70,8 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||||
$medium_font->SetPointSize(12);
|
$medium_font->SetPointSize(12);
|
||||||
our $grey = Wx::Colour->new(200,200,200);
|
our $grey = Wx::Colour->new(200,200,200);
|
||||||
|
|
||||||
|
our $VERSION_CHECK_EVENT : shared = Wx::NewEventType;
|
||||||
|
|
||||||
sub OnInit {
|
sub OnInit {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
@ -143,6 +145,25 @@ sub OnInit {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
EVT_COMMAND($self, -1, $VERSION_CHECK_EVENT, sub {
|
||||||
|
my ($self, $event) = @_;
|
||||||
|
my ($success, $response, $manual_check) = @{$event->GetData};
|
||||||
|
|
||||||
|
if ($success) {
|
||||||
|
if ($response =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) {
|
||||||
|
my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?",
|
||||||
|
'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal;
|
||||||
|
Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES;
|
||||||
|
} else {
|
||||||
|
Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $manual_check;
|
||||||
|
}
|
||||||
|
$Settings->{_}{last_version_check} = time();
|
||||||
|
$self->save_settings;
|
||||||
|
} else {
|
||||||
|
Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $manual_check;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +260,7 @@ sub have_version_check {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_version {
|
sub check_version {
|
||||||
my ($self, %p) = @_;
|
my ($self, $manual_check) = @_;
|
||||||
|
|
||||||
Slic3r::debugf "Checking for updates...\n";
|
Slic3r::debugf "Checking for updates...\n";
|
||||||
|
|
||||||
|
@ -248,19 +269,9 @@ sub check_version {
|
||||||
my $ua = LWP::UserAgent->new;
|
my $ua = LWP::UserAgent->new;
|
||||||
$ua->timeout(10);
|
$ua->timeout(10);
|
||||||
my $response = $ua->get('http://slic3r.org/updatecheck');
|
my $response = $ua->get('http://slic3r.org/updatecheck');
|
||||||
if ($response->is_success) {
|
Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $VERSION_CHECK_EVENT,
|
||||||
if ($response->decoded_content =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) {
|
threads::shared::shared_clone([ $response->is_success, $response->decoded_content, $manual_check ])));
|
||||||
my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?",
|
|
||||||
'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal;
|
|
||||||
Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES;
|
|
||||||
} else {
|
|
||||||
Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $p{manual};
|
|
||||||
}
|
|
||||||
$Settings->{_}{last_version_check} = time();
|
|
||||||
$self->save_settings;
|
|
||||||
} else {
|
|
||||||
Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $p{manual};
|
|
||||||
}
|
|
||||||
Slic3r::thread_cleanup();
|
Slic3r::thread_cleanup();
|
||||||
})->detach;
|
})->detach;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||||
enable_cutting
|
enable_cutting
|
||||||
enable_picking
|
enable_picking
|
||||||
enable_moving
|
enable_moving
|
||||||
|
on_viewport_changed
|
||||||
on_hover
|
on_hover
|
||||||
on_select
|
on_select
|
||||||
on_double_click
|
on_double_click
|
||||||
|
@ -108,6 +109,7 @@ sub new {
|
||||||
-($pos->y - $size->y/2) * ($zoom) / $self->_zoom,
|
-($pos->y - $size->y/2) * ($zoom) / $self->_zoom,
|
||||||
0,
|
0,
|
||||||
) if 0;
|
) if 0;
|
||||||
|
$self->on_viewport_changed->() if $self->on_viewport_changed;
|
||||||
$self->_dirty(1);
|
$self->_dirty(1);
|
||||||
$self->Refresh;
|
$self->Refresh;
|
||||||
});
|
});
|
||||||
|
@ -207,6 +209,7 @@ sub mouse_event {
|
||||||
);
|
);
|
||||||
$self->_quat(mulquats($self->_quat, \@quat));
|
$self->_quat(mulquats($self->_quat, \@quat));
|
||||||
}
|
}
|
||||||
|
$self->on_viewport_changed->() if $self->on_viewport_changed;
|
||||||
$self->Refresh;
|
$self->Refresh;
|
||||||
}
|
}
|
||||||
$self->_drag_start_pos($pos);
|
$self->_drag_start_pos($pos);
|
||||||
|
@ -220,6 +223,7 @@ sub mouse_event {
|
||||||
$self->_camera_target->translate(
|
$self->_camera_target->translate(
|
||||||
@{$orig->vector_to($cur_pos)->negative},
|
@{$orig->vector_to($cur_pos)->negative},
|
||||||
);
|
);
|
||||||
|
$self->on_viewport_changed->() if $self->on_viewport_changed;
|
||||||
$self->Refresh;
|
$self->Refresh;
|
||||||
}
|
}
|
||||||
$self->_drag_start_xy($pos);
|
$self->_drag_start_xy($pos);
|
||||||
|
@ -256,6 +260,17 @@ sub reset_objects {
|
||||||
$self->_dirty(1);
|
$self->_dirty(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub set_viewport_from_scene {
|
||||||
|
my ($self, $scene) = @_;
|
||||||
|
|
||||||
|
$self->_sphi($scene->_sphi);
|
||||||
|
$self->_stheta($scene->_stheta);
|
||||||
|
$self->_camera_target($scene->_camera_target);
|
||||||
|
$self->_zoom($scene->_zoom);
|
||||||
|
$self->_quat($scene->_quat);
|
||||||
|
$self->_dirty(1);
|
||||||
|
}
|
||||||
|
|
||||||
sub zoom_to_bounding_box {
|
sub zoom_to_bounding_box {
|
||||||
my ($self, $bb) = @_;
|
my ($self, $bb) = @_;
|
||||||
|
|
||||||
|
@ -267,6 +282,8 @@ sub zoom_to_bounding_box {
|
||||||
|
|
||||||
# center view around bounding box center
|
# center view around bounding box center
|
||||||
$self->_camera_target($bb->center);
|
$self->_camera_target($bb->center);
|
||||||
|
|
||||||
|
$self->on_viewport_changed->() if $self->on_viewport_changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub zoom_to_bed {
|
sub zoom_to_bed {
|
||||||
|
@ -347,25 +364,24 @@ sub set_bed_shape {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my @lines = ();
|
my @polylines = ();
|
||||||
for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) {
|
for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) {
|
||||||
push @lines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]);
|
push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]);
|
||||||
}
|
}
|
||||||
for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) {
|
for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) {
|
||||||
push @lines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]);
|
push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]);
|
||||||
}
|
}
|
||||||
# clip with a slightly grown expolygon because our lines lay on the contours and
|
# clip with a slightly grown expolygon because our lines lay on the contours and
|
||||||
# may get erroneously clipped
|
# may get erroneously clipped
|
||||||
@lines = @{intersection_pl(\@lines, [ @{$expolygon->offset(+scaled_epsilon)} ])};
|
my @lines = map Slic3r::Line->new(@$_[0,-1]),
|
||||||
|
@{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])};
|
||||||
|
|
||||||
# append bed contours
|
# append bed contours
|
||||||
foreach my $line (map @{$_->lines}, @$expolygon) {
|
push @lines, map @{$_->lines}, @$expolygon;
|
||||||
push @lines, $line->as_polyline;
|
|
||||||
}
|
|
||||||
|
|
||||||
my @points = ();
|
my @points = ();
|
||||||
foreach my $polyline (@lines) {
|
foreach my $line (@lines) {
|
||||||
push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$polyline; #))
|
push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line; #))
|
||||||
}
|
}
|
||||||
$self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points));
|
$self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points));
|
||||||
}
|
}
|
||||||
|
@ -572,9 +588,10 @@ sub Resize {
|
||||||
|
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
my $depth = 10 * max(@{ $self->max_bounding_box->size });
|
||||||
glOrtho(
|
glOrtho(
|
||||||
-$x/2, $x/2, -$y/2, $y/2,
|
-$x/2, $x/2, -$y/2, $y/2,
|
||||||
-200, 10 * max(@{ $self->max_bounding_box->size }),
|
-$depth, 2*$depth,
|
||||||
);
|
);
|
||||||
|
|
||||||
glMatrixMode(GL_MODELVIEW);
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
|
|
@ -307,7 +307,7 @@ sub _repaint_canvas {
|
||||||
@polylines = @{intersection_pl(\@polylines, [$bed_polygon])};
|
@polylines = @{intersection_pl(\@polylines, [$bed_polygon])};
|
||||||
|
|
||||||
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID));
|
$dc->SetPen(Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID));
|
||||||
$dc->DrawLine(map @{$to_pixel->([map unscale($_), @$_])}, @$_) for @polylines;
|
$dc->DrawLine(map @{$to_pixel->([map unscale($_), @$_])}, @$_[0,-1]) for @polylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
# draw bed contour
|
# draw bed contour
|
||||||
|
|
|
@ -255,7 +255,7 @@ sub _init_menubar {
|
||||||
Wx::LaunchDefaultBrowser('http://slic3r.org/');
|
Wx::LaunchDefaultBrowser('http://slic3r.org/');
|
||||||
});
|
});
|
||||||
my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
|
my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub {
|
||||||
wxTheApp->check_version(manual => 1);
|
wxTheApp->check_version(1);
|
||||||
});
|
});
|
||||||
$versioncheck->Enable(wxTheApp->have_version_check);
|
$versioncheck->Enable(wxTheApp->have_version_check);
|
||||||
$self->_append_menu_item($helpMenu, "Slic3r &Manual", 'Open the Slic3r manual in your browser', sub {
|
$self->_append_menu_item($helpMenu, "Slic3r &Manual", 'Open the Slic3r manual in your browser', sub {
|
||||||
|
@ -678,7 +678,7 @@ sub config {
|
||||||
} else {
|
} else {
|
||||||
my $extruders_count = $self->{options_tabs}{printer}{extruders_count};
|
my $extruders_count = $self->{options_tabs}{printer}{extruders_count};
|
||||||
$config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count))
|
$config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count))
|
||||||
for qw(perimeter infill support_material support_material_interface);
|
for qw(perimeter infill solid_infill support_material support_material_interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|
|
@ -96,6 +96,9 @@ sub new {
|
||||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
$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_right_click(sub { $on_right_click->($self->{canvas3D}, @_); });
|
||||||
$self->{canvas3D}->set_on_instances_moved($on_instances_moved);
|
$self->{canvas3D}->set_on_instances_moved($on_instances_moved);
|
||||||
|
$self->{canvas3D}->on_viewport_changed(sub {
|
||||||
|
$self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize 2D preview canvas
|
# Initialize 2D preview canvas
|
||||||
|
@ -109,6 +112,9 @@ sub new {
|
||||||
# Initialize 3D toolpaths preview
|
# Initialize 3D toolpaths preview
|
||||||
if ($Slic3r::GUI::have_OpenGL) {
|
if ($Slic3r::GUI::have_OpenGL) {
|
||||||
$self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print});
|
$self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print});
|
||||||
|
$self->{preview3D}->canvas->on_viewport_changed(sub {
|
||||||
|
$self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas);
|
||||||
|
});
|
||||||
$self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview');
|
$self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview');
|
||||||
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
|
$self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1;
|
||||||
}
|
}
|
||||||
|
@ -988,9 +994,13 @@ sub pause_background_process {
|
||||||
|
|
||||||
if ($self->{process_thread} || $self->{export_thread}) {
|
if ($self->{process_thread} || $self->{export_thread}) {
|
||||||
Slic3r::pause_all_threads();
|
Slic3r::pause_all_threads();
|
||||||
|
return 1;
|
||||||
} elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) {
|
} elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) {
|
||||||
$self->{apply_config_timer}->Stop;
|
$self->{apply_config_timer}->Stop;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub resume_background_process {
|
sub resume_background_process {
|
||||||
|
@ -1312,9 +1322,14 @@ sub update {
|
||||||
$self->{model}->center_instances_around_point($self->bed_centerf);
|
$self->{model}->center_instances_around_point($self->bed_centerf);
|
||||||
}
|
}
|
||||||
|
|
||||||
$self->pause_background_process;
|
my $running = $self->pause_background_process;
|
||||||
my $invalidated = $self->{print}->reload_model_instances();
|
my $invalidated = $self->{print}->reload_model_instances();
|
||||||
if ($invalidated) {
|
|
||||||
|
# The mere fact that no steps were invalidated when reloading model instances
|
||||||
|
# doesn't mean that all steps were done: for example, validation might have
|
||||||
|
# failed upon previous instance move, so we have no running thread and no steps
|
||||||
|
# are invalidated on this move, thus we need to schedule a new run.
|
||||||
|
if ($invalidated || !$running) {
|
||||||
$self->schedule_background_process;
|
$self->schedule_background_process;
|
||||||
} else {
|
} else {
|
||||||
$self->resume_background_process;
|
$self->resume_background_process;
|
||||||
|
@ -1323,12 +1338,6 @@ sub update {
|
||||||
$self->refresh_canvases;
|
$self->refresh_canvases;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub on_model_instances_changed {
|
|
||||||
my ($self) = @_;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
sub on_extruders_change {
|
sub on_extruders_change {
|
||||||
my ($self, $num_extruders) = @_;
|
my ($self, $num_extruders) = @_;
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ sub update_bed_size {
|
||||||
push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]);
|
push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]);
|
||||||
}
|
}
|
||||||
@polylines = @{intersection_pl(\@polylines, [$polygon])};
|
@polylines = @{intersection_pl(\@polylines, [$polygon])};
|
||||||
$self->{grid} = [ map $self->scaled_points_to_pixel(\@$_, 1), @polylines ];
|
$self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -291,7 +291,7 @@ sub Render {
|
||||||
$brim_drawn = 1;
|
$brim_drawn = 1;
|
||||||
}
|
}
|
||||||
if ($self->print->step_done(STEP_SKIRT)
|
if ($self->print->step_done(STEP_SKIRT)
|
||||||
&& ($self->print->config->skirt_height == -1 || $self->print->config->skirt_height > $layer->id)
|
&& ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id)
|
||||||
&& !$skirt_drawn) {
|
&& !$skirt_drawn) {
|
||||||
$self->color([0, 0, 0]);
|
$self->color([0, 0, 0]);
|
||||||
$self->_draw(undef, $print_z, $_) for @{$self->print->skirt};
|
$self->_draw(undef, $print_z, $_) for @{$self->print->skirt};
|
||||||
|
|
|
@ -37,9 +37,9 @@ sub new {
|
||||||
|
|
||||||
# buttons
|
# buttons
|
||||||
$self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG),
|
$self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG),
|
||||||
wxDefaultPosition, [16,16], wxBORDER_NONE);
|
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||||
$self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG),
|
$self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG),
|
||||||
wxDefaultPosition, [16,16], wxBORDER_NONE);
|
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
|
||||||
$self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title));
|
$self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title));
|
||||||
$self->{btn_delete_preset}->SetToolTipString("Delete this preset");
|
$self->{btn_delete_preset}->SetToolTipString("Delete this preset");
|
||||||
$self->{btn_delete_preset}->Disable;
|
$self->{btn_delete_preset}->Disable;
|
||||||
|
@ -745,11 +745,13 @@ sub _update {
|
||||||
perimeter_speed small_perimeter_speed external_perimeter_speed);
|
perimeter_speed small_perimeter_speed external_perimeter_speed);
|
||||||
|
|
||||||
my $have_infill = $config->fill_density > 0;
|
my $have_infill = $config->fill_density > 0;
|
||||||
|
# infill_extruder uses the same logic as in Print::extruders()
|
||||||
$self->get_field($_)->toggle($have_infill)
|
$self->get_field($_)->toggle($have_infill)
|
||||||
for qw(fill_pattern infill_every_layers infill_only_where_needed solid_infill_every_layers
|
for qw(fill_pattern infill_every_layers infill_only_where_needed solid_infill_every_layers
|
||||||
solid_infill_below_area infill_extruder);
|
solid_infill_below_area infill_extruder);
|
||||||
|
|
||||||
my $have_solid_infill = ($config->top_solid_layers > 0) || ($config->bottom_solid_layers > 0);
|
my $have_solid_infill = ($config->top_solid_layers > 0) || ($config->bottom_solid_layers > 0);
|
||||||
|
# solid_infill_extruder uses the same logic as in Print::extruders()
|
||||||
$self->get_field($_)->toggle($have_solid_infill)
|
$self->get_field($_)->toggle($have_solid_infill)
|
||||||
for qw(external_fill_pattern infill_first solid_infill_extruder solid_infill_extrusion_width
|
for qw(external_fill_pattern infill_first solid_infill_extruder solid_infill_extrusion_width
|
||||||
solid_infill_speed);
|
solid_infill_speed);
|
||||||
|
@ -772,6 +774,7 @@ sub _update {
|
||||||
for qw(skirt_distance skirt_height);
|
for qw(skirt_distance skirt_height);
|
||||||
|
|
||||||
my $have_brim = $config->brim_width > 0;
|
my $have_brim = $config->brim_width > 0;
|
||||||
|
# perimeter_extruder uses the same logic as in Print::extruders()
|
||||||
$self->get_field('perimeter_extruder')->toggle($have_perimeters || $have_brim);
|
$self->get_field('perimeter_extruder')->toggle($have_perimeters || $have_brim);
|
||||||
|
|
||||||
my $have_support_material = $config->support_material || $config->raft_layers > 0;
|
my $have_support_material = $config->support_material || $config->raft_layers > 0;
|
||||||
|
|
|
@ -286,7 +286,7 @@ sub process {
|
||||||
# where 0.5*$pwidth < thickness < $pwidth, infill with width = 0.5*$pwidth
|
# where 0.5*$pwidth < thickness < $pwidth, infill with width = 0.5*$pwidth
|
||||||
my @gap_sizes = (
|
my @gap_sizes = (
|
||||||
[ $pwidth, 2*$pspacing, unscale 1.5*$pwidth ],
|
[ $pwidth, 2*$pspacing, unscale 1.5*$pwidth ],
|
||||||
[ 0.5*$pwidth, $pwidth, unscale 0.5*$pwidth ],
|
[ 0.1*$pwidth, $pwidth, unscale 0.5*$pwidth ],
|
||||||
);
|
);
|
||||||
foreach my $gap_size (@gap_sizes) {
|
foreach my $gap_size (@gap_sizes) {
|
||||||
my @gap_fill = $self->_fill_gaps(@$gap_size, \@gaps);
|
my @gap_fill = $self->_fill_gaps(@$gap_size, \@gaps);
|
||||||
|
@ -311,12 +311,22 @@ sub process {
|
||||||
# we offset by half the perimeter spacing (to get to the actual infill boundary)
|
# we offset by half the perimeter spacing (to get to the actual infill boundary)
|
||||||
# and then we offset back and forth by half the infill spacing to only consider the
|
# and then we offset back and forth by half the infill spacing to only consider the
|
||||||
# non-collapsing regions
|
# non-collapsing regions
|
||||||
|
my $inset = 0;
|
||||||
|
if ($loop_number == 0) {
|
||||||
|
# one loop
|
||||||
|
$inset += $ext_pspacing/2;
|
||||||
|
} elsif ($loop_number > 0) {
|
||||||
|
# two or more loops
|
||||||
|
$inset += $pspacing/2;
|
||||||
|
}
|
||||||
|
$inset -= $self->config->get_abs_value_over('infill_overlap', $pwidth);
|
||||||
|
|
||||||
my $min_perimeter_infill_spacing = $ispacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE);
|
my $min_perimeter_infill_spacing = $ispacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE);
|
||||||
$self->fill_surfaces->append($_)
|
$self->fill_surfaces->append($_)
|
||||||
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 - $self->config->get_abs_value_over('infill_overlap', $pwidth) + $min_perimeter_infill_spacing/2),
|
-$inset -$min_perimeter_infill_spacing/2,
|
||||||
+$min_perimeter_infill_spacing/2,
|
+$min_perimeter_infill_spacing/2,
|
||||||
)};
|
)};
|
||||||
}
|
}
|
||||||
|
@ -384,8 +394,8 @@ sub _traverse_loops {
|
||||||
push @paths, Slic3r::ExtrusionPath->new(
|
push @paths, Slic3r::ExtrusionPath->new(
|
||||||
polyline => $loop->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 => ($is_external ? $self->_ext_mm3_per_mm : $self->_mm3_per_mm),
|
||||||
width => $self->perimeter_flow->width,
|
width => ($is_external ? $self->ext_perimeter_flow->width : $self->perimeter_flow->width),
|
||||||
height => $self->layer_height,
|
height => $self->layer_height,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -441,11 +451,14 @@ sub _traverse_loops {
|
||||||
sub _fill_gaps {
|
sub _fill_gaps {
|
||||||
my ($self, $min, $max, $w, $gaps) = @_;
|
my ($self, $min, $max, $w, $gaps) = @_;
|
||||||
|
|
||||||
|
$min *= (1 - &Slic3r::INSET_OVERLAP_TOLERANCE);
|
||||||
|
|
||||||
my $this = diff_ex(
|
my $this = diff_ex(
|
||||||
offset2([ map @$_, @$gaps ], -$min/2, +$min/2),
|
offset2([ map @$_, @$gaps ], -$min/2, +$min/2),
|
||||||
offset2([ map @$_, @$gaps ], -$max/2, +$max/2),
|
offset2([ map @$_, @$gaps ], -$max/2, +$max/2),
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
my @polylines = map @{$_->medial_axis($max, $min/2)}, @$this;
|
my @polylines = map @{$_->medial_axis($max, $min/2)}, @$this;
|
||||||
return if !@polylines;
|
return if !@polylines;
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,8 @@ sub export_gcode {
|
||||||
$self->config->setenv;
|
$self->config->setenv;
|
||||||
for my $script (@{$self->config->post_process}) {
|
for my $script (@{$self->config->post_process}) {
|
||||||
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
|
Slic3r::debugf " '%s' '%s'\n", $script, $output_file;
|
||||||
if (!-x $script) {
|
# -x doesn't return true on Windows except for .exe files
|
||||||
|
if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) {
|
||||||
die "The configured post-processing script is not executable: check permissions. ($script)\n";
|
die "The configured post-processing script is not executable: check permissions. ($script)\n";
|
||||||
}
|
}
|
||||||
system($script, $output_file);
|
system($script, $output_file);
|
||||||
|
@ -101,9 +102,6 @@ sub export_svg {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my %params = @_;
|
my %params = @_;
|
||||||
|
|
||||||
# is this needed?
|
|
||||||
$self->init_extruders;
|
|
||||||
|
|
||||||
$_->slice for @{$self->objects};
|
$_->slice for @{$self->objects};
|
||||||
|
|
||||||
my $fh = $params{output_fh};
|
my $fh = $params{output_fh};
|
||||||
|
@ -208,8 +206,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 || $self->config->skirt_height == 0)
|
if (!$self->has_skirt) {
|
||||||
&& (!$self->config->ooze_prevention || @{$self->extruders} == 1)) {
|
|
||||||
$self->set_step_done(STEP_SKIRT);
|
$self->set_step_done(STEP_SKIRT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +216,7 @@ sub make_skirt {
|
||||||
# The skirt_height option from config is expressed in layers, but our
|
# The skirt_height option from config is expressed in layers, but our
|
||||||
# object might have different layer heights, so we need to find the print_z
|
# object might have different layer heights, so we need to find the print_z
|
||||||
# of the highest layer involved.
|
# of the highest layer involved.
|
||||||
# Note that unless skirt_height == -1 (which means it's printed on all layers)
|
# Note that unless has_infinite_skirt() == true
|
||||||
# the actual skirt might not reach this $skirt_height_z value since the print
|
# the actual skirt might not reach this $skirt_height_z value since the print
|
||||||
# order of objects on each layer is not guaranteed and will not generally
|
# order of objects on each layer is not guaranteed and will not generally
|
||||||
# include the thickest object first. It is just guaranteed that a skirt is
|
# include the thickest object first. It is just guaranteed that a skirt is
|
||||||
|
@ -227,10 +224,9 @@ sub make_skirt {
|
||||||
# $skirt_height_z in this case is the highest possible skirt height for safety.
|
# $skirt_height_z in this case is the highest possible skirt height for safety.
|
||||||
my $skirt_height_z = -1;
|
my $skirt_height_z = -1;
|
||||||
foreach my $object (@{$self->objects}) {
|
foreach my $object (@{$self->objects}) {
|
||||||
my $skirt_height = ($self->config->skirt_height == -1 || $self->config->ooze_prevention)
|
my $skirt_height = $self->has_infinite_skirt
|
||||||
? scalar(@{$object->layers})
|
? scalar(@{$object->layers})
|
||||||
: min($self->config->skirt_height, scalar(@{$object->layers}));
|
: min($self->config->skirt_height, scalar(@{$object->layers}));
|
||||||
|
|
||||||
my $highest_layer = $object->get_layer($skirt_height - 1);
|
my $highest_layer = $object->get_layer($skirt_height - 1);
|
||||||
$skirt_height_z = max($skirt_height_z, $highest_layer->print_z);
|
$skirt_height_z = max($skirt_height_z, $highest_layer->print_z);
|
||||||
}
|
}
|
||||||
|
@ -278,10 +274,13 @@ sub make_skirt {
|
||||||
my @extruders_e_per_mm = ();
|
my @extruders_e_per_mm = ();
|
||||||
my $extruder_idx = 0;
|
my $extruder_idx = 0;
|
||||||
|
|
||||||
|
my $skirts = $self->config->skirts;
|
||||||
|
$skirts ||= 1 if $self->has_infinite_skirt;
|
||||||
|
|
||||||
# draw outlines from outside to inside
|
# draw outlines from outside to inside
|
||||||
# loop while we have less skirts than required or any extruder hasn't reached the min length if any
|
# loop while we have less skirts than required or any extruder hasn't reached the min length if any
|
||||||
my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
|
my $distance = scale max($self->config->skirt_distance, $self->config->brim_width);
|
||||||
for (my $i = $self->config->skirts; $i > 0; $i--) {
|
for (my $i = $skirts; $i > 0; $i--) {
|
||||||
$distance += scale $spacing;
|
$distance += scale $spacing;
|
||||||
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0];
|
||||||
$self->skirt->append(Slic3r::ExtrusionLoop->new_from_paths(
|
$self->skirt->append(Slic3r::ExtrusionLoop->new_from_paths(
|
||||||
|
@ -428,10 +427,10 @@ sub expanded_output_filepath {
|
||||||
|
|
||||||
my $filename = my $filename_base = basename($input_file);
|
my $filename = my $filename_base = basename($input_file);
|
||||||
$filename_base =~ s/\.[^.]+$//; # without suffix
|
$filename_base =~ s/\.[^.]+$//; # without suffix
|
||||||
my $extra = {
|
|
||||||
input_filename => $filename,
|
# set filename in placeholder parser so that it's available also in custom G-code
|
||||||
input_filename_base => $filename_base,
|
$self->placeholder_parser->set(input_filename => $filename);
|
||||||
};
|
$self->placeholder_parser->set(input_filename_base => $filename_base);
|
||||||
|
|
||||||
if ($path && -d $path) {
|
if ($path && -d $path) {
|
||||||
# if output path is an existing directory, we take that and append
|
# if output path is an existing directory, we take that and append
|
||||||
|
@ -447,7 +446,7 @@ sub expanded_output_filepath {
|
||||||
|
|
||||||
# make sure we use an up-to-date timestamp
|
# make sure we use an up-to-date timestamp
|
||||||
$self->placeholder_parser->update_timestamp;
|
$self->placeholder_parser->update_timestamp;
|
||||||
return $self->placeholder_parser->process($path, $extra);
|
return $self->placeholder_parser->process($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
# This method assigns extruders to the volumes having a material
|
# This method assigns extruders to the volumes having a material
|
||||||
|
|
|
@ -195,12 +195,14 @@ sub export {
|
||||||
# no collision happens hopefully.
|
# no collision happens hopefully.
|
||||||
if ($finished_objects > 0) {
|
if ($finished_objects > 0) {
|
||||||
$gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
|
$gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y));
|
||||||
|
$gcodegen->enable_cooling_markers(0); # we're not filtering these moves through CoolingBuffer
|
||||||
print $fh $gcodegen->retract;
|
print $fh $gcodegen->retract;
|
||||||
print $fh $gcodegen->travel_to(
|
print $fh $gcodegen->travel_to(
|
||||||
Slic3r::Point->new(0,0),
|
Slic3r::Point->new(0,0),
|
||||||
undef,
|
undef,
|
||||||
'move to origin position for next object',
|
'move to origin position for next object',
|
||||||
);
|
);
|
||||||
|
$gcodegen->enable_cooling_markers(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers};
|
my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers};
|
||||||
|
@ -217,6 +219,7 @@ sub export {
|
||||||
}
|
}
|
||||||
$self->flush_filters;
|
$self->flush_filters;
|
||||||
$finished_objects++;
|
$finished_objects++;
|
||||||
|
$self->_second_layer_things_done(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,6 +252,7 @@ sub export {
|
||||||
print $fh $gcodegen->writer->set_fan(0);
|
print $fh $gcodegen->writer->set_fan(0);
|
||||||
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode);
|
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode);
|
||||||
print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100%
|
print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1); # 100%
|
||||||
|
print $fh $gcodegen->writer->postamble;
|
||||||
|
|
||||||
# get filament stats
|
# get filament stats
|
||||||
$self->print->clear_filament_stats;
|
$self->print->clear_filament_stats;
|
||||||
|
@ -299,7 +303,7 @@ sub process_layer {
|
||||||
if (defined $self->_spiral_vase) {
|
if (defined $self->_spiral_vase) {
|
||||||
$self->_spiral_vase->enable(
|
$self->_spiral_vase->enable(
|
||||||
($layer->id > 0 || $self->print->config->brim_width == 0)
|
($layer->id > 0 || $self->print->config->brim_width == 0)
|
||||||
&& ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1)
|
&& ($layer->id >= $self->print->config->skirt_height && !$self->print->has_infinite_skirt)
|
||||||
&& !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions})
|
&& !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions})
|
||||||
&& !defined(first { @{$_->perimeters} > 1 } @{$layer->regions})
|
&& !defined(first { @{$_->perimeters} > 1 } @{$layer->regions})
|
||||||
&& !defined(first { @{$_->fills} > 0 } @{$layer->regions})
|
&& !defined(first { @{$_->fills} > 0 } @{$layer->regions})
|
||||||
|
@ -332,14 +336,15 @@ sub process_layer {
|
||||||
}) . "\n" if $self->print->config->layer_gcode;
|
}) . "\n" if $self->print->config->layer_gcode;
|
||||||
|
|
||||||
# extrude skirt
|
# extrude skirt
|
||||||
if (((values %{$self->_skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1)
|
if (((values %{$self->_skirt_done}) < $self->print->config->skirt_height || $self->print->has_infinite_skirt)
|
||||||
&& !$self->_skirt_done->{$layer->print_z}) {
|
&& !$self->_skirt_done->{$layer->print_z}
|
||||||
|
&& !$layer->isa('Slic3r::Layer::Support')) {
|
||||||
$self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0));
|
$self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0));
|
||||||
$self->_gcodegen->avoid_crossing_perimeters->use_external_mp(1);
|
$self->_gcodegen->avoid_crossing_perimeters->use_external_mp(1);
|
||||||
my @extruder_ids = map { $_->id } @{$self->_gcodegen->writer->extruders};
|
my @extruder_ids = map { $_->id } @{$self->_gcodegen->writer->extruders};
|
||||||
$gcode .= $self->_gcodegen->set_extruder($extruder_ids[0]);
|
$gcode .= $self->_gcodegen->set_extruder($extruder_ids[0]);
|
||||||
# skip skirt if we have a large brim
|
# skip skirt if we have a large brim
|
||||||
if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) {
|
if ($layer->id < $self->print->config->skirt_height || $self->print->has_infinite_skirt) {
|
||||||
my $skirt_flow = $self->print->skirt_flow;
|
my $skirt_flow = $self->print->skirt_flow;
|
||||||
|
|
||||||
# distribute skirt loops across all extruders
|
# distribute skirt loops across all extruders
|
||||||
|
|
|
@ -6,7 +6,7 @@ use List::Util qw(min max sum first);
|
||||||
use Slic3r::Flow ':roles';
|
use Slic3r::Flow ':roles';
|
||||||
use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path);
|
use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path);
|
||||||
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
|
use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex
|
||||||
offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER);
|
offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER);
|
||||||
use Slic3r::Print::State ':steps';
|
use Slic3r::Print::State ':steps';
|
||||||
use Slic3r::Surface ':types';
|
use Slic3r::Surface ':types';
|
||||||
|
|
||||||
|
@ -344,7 +344,6 @@ sub make_perimeters {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
# prerequisites
|
# prerequisites
|
||||||
$self->print->init_extruders;
|
|
||||||
$self->slice;
|
$self->slice;
|
||||||
|
|
||||||
return if $self->step_done(STEP_PERIMETERS);
|
return if $self->step_done(STEP_PERIMETERS);
|
||||||
|
@ -369,51 +368,59 @@ sub make_perimeters {
|
||||||
my $region = $self->print->regions->[$region_id];
|
my $region = $self->print->regions->[$region_id];
|
||||||
my $region_perimeters = $region->config->perimeters;
|
my $region_perimeters = $region->config->perimeters;
|
||||||
|
|
||||||
if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) {
|
next if !$region->config->extra_perimeters;
|
||||||
for my $i (0 .. ($self->layer_count - 2)) {
|
next if $region_perimeters == 0;
|
||||||
my $layerm = $self->get_layer($i)->regions->[$region_id];
|
next if $region->config->fill_density == 0;
|
||||||
my $upper_layerm = $self->get_layer($i+1)->regions->[$region_id];
|
|
||||||
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
for my $i (0 .. ($self->layer_count - 2)) {
|
||||||
my $ext_perimeter_spacing = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_spacing;
|
my $layerm = $self->get_layer($i)->get_region($region_id);
|
||||||
|
my $upper_layerm = $self->get_layer($i+1)->get_region($region_id);
|
||||||
my $overlap = $perimeter_spacing; # one perimeter
|
|
||||||
|
my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing;
|
||||||
my $diff = diff(
|
my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER);
|
||||||
offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($ext_perimeter_spacing + ($region_perimeters-1) * $perimeter_spacing)),
|
my $ext_perimeter_width = $ext_perimeter_flow->scaled_width;
|
||||||
offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap),
|
my $ext_perimeter_spacing = $ext_perimeter_flow->scaled_spacing;
|
||||||
);
|
|
||||||
next if !@$diff;
|
foreach my $slice (@{$layerm->slices}) {
|
||||||
# if we need more perimeters, $diff should contain a narrow region that we can collapse
|
while (1) {
|
||||||
|
# compute the total thickness of perimeters
|
||||||
# we use a higher miterLimit here to handle areas with acute angles
|
my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2
|
||||||
# in those cases, the default miterLimit would cut the corner and we'd
|
+ ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing;
|
||||||
# get a triangle that would trigger a non-needed extra perimeter
|
|
||||||
$diff = diff(
|
# define a critical area where we don't want the upper slice to fall into
|
||||||
$diff,
|
# (it should either lay over our perimeters or outside this area)
|
||||||
offset2($diff, -$perimeter_spacing, +$perimeter_spacing, CLIPPER_OFFSET_SCALE, JT_MITER, 5),
|
my $critical_area_depth = $perimeter_spacing*1.5;
|
||||||
1,
|
my $critical_area = diff(
|
||||||
);
|
offset($slice->expolygon->arrayref, -$perimeters_thickness),
|
||||||
next if !@$diff;
|
offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)),
|
||||||
# diff contains the collapsed area
|
);
|
||||||
|
|
||||||
foreach my $slice (@{$layerm->slices}) {
|
# check whether a portion of the upper slices falls inside the critical area
|
||||||
my $extra_perimeters = 0;
|
my $intersection = intersection_ppl(
|
||||||
CYCLE: while (1) {
|
[ map $_->p, @{$upper_layerm->slices} ],
|
||||||
# compute polygons representing the thickness of the hypotetical new internal perimeter
|
$critical_area,
|
||||||
# of our slice
|
);
|
||||||
$extra_perimeters++;
|
|
||||||
my $hypothetical_perimeter = diff(
|
# only add an additional loop if at least 30% of the slice loop would benefit from it
|
||||||
offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters-1))),
|
my $total_loop_length = sum(map $_->length, map $_->p, @{$upper_layerm->slices}) // 0;
|
||||||
offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters))),
|
my $total_intersection_length = sum(map $_->length, @$intersection) // 0;
|
||||||
|
last unless $total_intersection_length > $total_loop_length*0.3;
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
require "Slic3r/SVG.pm";
|
||||||
|
Slic3r::SVG::output(
|
||||||
|
"extra.svg",
|
||||||
|
no_arrows => 1,
|
||||||
|
expolygons => union_ex($critical_area),
|
||||||
|
polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ],
|
||||||
);
|
);
|
||||||
last CYCLE if !@$hypothetical_perimeter; # no extra perimeter is possible
|
|
||||||
|
|
||||||
# only add the perimeter if there's an intersection with the collapsed area
|
|
||||||
last CYCLE if !@{ intersection($diff, $hypothetical_perimeter) };
|
|
||||||
Slic3r::debugf " adding one more perimeter at layer %d\n", $layerm->id;
|
|
||||||
$slice->extra_perimeters($extra_perimeters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$slice->extra_perimeters($slice->extra_perimeters + 1);
|
||||||
}
|
}
|
||||||
|
Slic3r::debugf " adding %d more perimeter(s) at layer %d\n",
|
||||||
|
$slice->extra_perimeters, $layerm->id
|
||||||
|
if $slice->extra_perimeters > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -524,7 +531,6 @@ sub generate_support_material {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
|
||||||
# prerequisites
|
# prerequisites
|
||||||
$self->print->init_extruders;
|
|
||||||
$self->slice;
|
$self->slice;
|
||||||
|
|
||||||
return if $self->step_done(STEP_SUPPORTMATERIAL);
|
return if $self->step_done(STEP_SUPPORTMATERIAL);
|
||||||
|
@ -547,7 +553,7 @@ sub _support_material {
|
||||||
my ($self) = @_;
|
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->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width),
|
||||||
role => FLOW_ROLE_SUPPORT_MATERIAL,
|
role => FLOW_ROLE_SUPPORT_MATERIAL,
|
||||||
nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ]
|
nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ]
|
||||||
// $self->print->config->nozzle_diameter->[0],
|
// $self->print->config->nozzle_diameter->[0],
|
||||||
|
@ -650,7 +656,7 @@ sub detect_surfaces_type {
|
||||||
|
|
||||||
# if we have raft layers, consider bottom layer as a bridge
|
# if we have raft layers, consider bottom layer as a bridge
|
||||||
# just like any other bottom surface lying on the void
|
# just like any other bottom surface lying on the void
|
||||||
if ($self->config->raft_layers > 0) {
|
if ($self->config->raft_layers > 0 && $self->config->support_material_contact_distance > 0) {
|
||||||
$_->surface_type(S_TYPE_BOTTOMBRIDGE) for @bottom;
|
$_->surface_type(S_TYPE_BOTTOMBRIDGE) for @bottom;
|
||||||
} else {
|
} else {
|
||||||
$_->surface_type(S_TYPE_BOTTOM) for @bottom;
|
$_->surface_type(S_TYPE_BOTTOM) for @bottom;
|
||||||
|
@ -714,61 +720,95 @@ sub clip_fill_surfaces {
|
||||||
# We only want infill under ceilings; this is almost like an
|
# We only want infill under ceilings; this is almost like an
|
||||||
# internal support material.
|
# internal support material.
|
||||||
|
|
||||||
my $additional_margin = scale 3*0;
|
# proceed top-down skipping bottom layer
|
||||||
|
my $upper_internal = [];
|
||||||
my $overhangs = []; # arrayref of polygons
|
for my $layer_id (reverse 1..($self->layer_count - 1)) {
|
||||||
for my $layer_id (reverse 0..($self->layer_count - 1)) {
|
my $layer = $self->get_layer($layer_id);
|
||||||
my $layer = $self->get_layer($layer_id);
|
my $lower_layer = $self->get_layer($layer_id-1);
|
||||||
my @layer_internal = (); # arrayref of Surface objects
|
|
||||||
my @new_internal = (); # arrayref of Surface objects
|
|
||||||
|
|
||||||
# clip this layer's internal surfaces to @overhangs
|
# detect things that we need to support
|
||||||
foreach my $layerm (@{$layer->regions}) {
|
my $overhangs = []; # Polygons
|
||||||
|
|
||||||
|
# we need to support any solid surface
|
||||||
|
push @$overhangs, map $_->p,
|
||||||
|
grep $_->is_solid, map @{$_->fill_surfaces}, @{$layer->regions};
|
||||||
|
|
||||||
|
# we also need to support perimeters when there's at least one full
|
||||||
|
# unsupported loop
|
||||||
|
{
|
||||||
|
# get perimeters area as the difference between slices and fill_surfaces
|
||||||
|
my $perimeters = diff(
|
||||||
|
[ map @$_, @{$layer->slices} ],
|
||||||
|
[ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ],
|
||||||
|
);
|
||||||
|
|
||||||
|
# only consider the area that is not supported by lower perimeters
|
||||||
|
$perimeters = intersection(
|
||||||
|
$perimeters,
|
||||||
|
[ map $_->p, map @{$_->fill_surfaces}, @{$lower_layer->regions} ],
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
# only consider perimeter areas that are at least one extrusion width thick
|
||||||
|
my $pw = min(map $_->flow(FLOW_ROLE_PERIMETER)->scaled_width, @{$layer->regions});
|
||||||
|
$perimeters = offset2($perimeters, -$pw, +$pw);
|
||||||
|
|
||||||
|
# append such thick perimeters to the areas that need support
|
||||||
|
push @$overhangs, @$perimeters;
|
||||||
|
}
|
||||||
|
|
||||||
|
# find new internal infill
|
||||||
|
$upper_internal = my $new_internal = intersection(
|
||||||
|
[
|
||||||
|
@$overhangs,
|
||||||
|
@$upper_internal,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
# our current internal fill boundaries
|
||||||
|
map $_->p,
|
||||||
|
grep $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALVOID,
|
||||||
|
map @{$_->fill_surfaces}, @{$lower_layer->regions}
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
# apply new internal infill to regions
|
||||||
|
foreach my $layerm (@{$lower_layer->regions}) {
|
||||||
my (@internal, @other) = ();
|
my (@internal, @other) = ();
|
||||||
foreach my $surface (map $_->clone, @{$layerm->fill_surfaces}) {
|
foreach my $surface (map $_->clone, @{$layerm->fill_surfaces}) {
|
||||||
if ($surface->surface_type == S_TYPE_INTERNAL) {
|
if ($surface->surface_type == S_TYPE_INTERNAL || $surface->surface_type == S_TYPE_INTERNALVOID) {
|
||||||
push @internal, $surface;
|
push @internal, $surface;
|
||||||
} else {
|
} else {
|
||||||
push @other, $surface;
|
push @other, $surface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# keep all the original internal surfaces to detect overhangs in this layer
|
my @new = map Slic3r::Surface->new(
|
||||||
push @layer_internal, @internal;
|
|
||||||
|
|
||||||
push @new_internal, my @new = map Slic3r::Surface->new(
|
|
||||||
expolygon => $_,
|
expolygon => $_,
|
||||||
surface_type => S_TYPE_INTERNAL,
|
surface_type => S_TYPE_INTERNAL,
|
||||||
),
|
),
|
||||||
@{intersection_ex(
|
@{intersection_ex(
|
||||||
[ map $_->p, @internal ],
|
[ map $_->p, @internal ],
|
||||||
$overhangs,
|
$new_internal,
|
||||||
|
1,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
push @new, map Slic3r::Surface->new(
|
push @other, map Slic3r::Surface->new(
|
||||||
expolygon => $_,
|
expolygon => $_,
|
||||||
surface_type => S_TYPE_INTERNALVOID,
|
surface_type => S_TYPE_INTERNALVOID,
|
||||||
),
|
),
|
||||||
@{diff_ex(
|
@{diff_ex(
|
||||||
[ map $_->p, @internal ],
|
[ map $_->p, @internal ],
|
||||||
$overhangs,
|
$new_internal,
|
||||||
|
1,
|
||||||
)};
|
)};
|
||||||
|
|
||||||
|
# If there are voids it means that our internal infill is not adjacent to
|
||||||
|
# perimeters. In this case it would be nice to add a loop around infill to
|
||||||
|
# make it more robust and nicer. TODO.
|
||||||
|
|
||||||
$layerm->fill_surfaces->clear;
|
$layerm->fill_surfaces->clear;
|
||||||
$layerm->fill_surfaces->append($_) for (@new, @other);
|
$layerm->fill_surfaces->append($_) for (@new, @other);
|
||||||
}
|
}
|
||||||
|
|
||||||
# get this layer's overhangs defined as the full slice minus the internal infill
|
|
||||||
# (thus we also consider perimeters)
|
|
||||||
if ($layer_id > 0) {
|
|
||||||
my $solid = diff(
|
|
||||||
[ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ],
|
|
||||||
[ map $_->p, @layer_internal ],
|
|
||||||
);
|
|
||||||
$overhangs = offset($solid, +$additional_margin);
|
|
||||||
|
|
||||||
push @$overhangs, map $_->p, @new_internal; # propagate upper overhangs
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use warnings;
|
||||||
|
|
||||||
require Exporter;
|
require Exporter;
|
||||||
our @ISA = qw(Exporter);
|
our @ISA = qw(Exporter);
|
||||||
our @EXPORT_OK = qw(STEP_INIT_EXTRUDERS STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL
|
our @EXPORT_OK = qw(STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL
|
||||||
STEP_INFILL STEP_SUPPORTMATERIAL STEP_SKIRT STEP_BRIM);
|
STEP_INFILL STEP_SUPPORTMATERIAL STEP_SKIRT STEP_BRIM);
|
||||||
our %EXPORT_TAGS = (steps => \@EXPORT_OK);
|
our %EXPORT_TAGS = (steps => \@EXPORT_OK);
|
||||||
|
|
||||||
|
|
|
@ -723,6 +723,11 @@ sub generate_toolpaths {
|
||||||
if (@$base) {
|
if (@$base) {
|
||||||
my $filler = $fillers{support};
|
my $filler = $fillers{support};
|
||||||
$filler->angle($angles[ ($layer_id) % @angles ]);
|
$filler->angle($angles[ ($layer_id) % @angles ]);
|
||||||
|
|
||||||
|
# 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 $density = $support_density;
|
my $density = $support_density;
|
||||||
my $base_flow = $_flow;
|
my $base_flow = $_flow;
|
||||||
|
|
||||||
|
@ -737,6 +742,10 @@ sub generate_toolpaths {
|
||||||
$filler->angle($self->object_config->support_material_angle + 90);
|
$filler->angle($self->object_config->support_material_angle + 90);
|
||||||
$density = 0.5;
|
$density = 0.5;
|
||||||
$base_flow = $self->first_layer_flow;
|
$base_flow = $self->first_layer_flow;
|
||||||
|
|
||||||
|
# use the proper spacing for first layer as we don't need to align
|
||||||
|
# its pattern to the other layers
|
||||||
|
$filler->spacing($base_flow->spacing);
|
||||||
} else {
|
} else {
|
||||||
# draw a perimeter all around support infill
|
# draw a perimeter all around support infill
|
||||||
# TODO: use brim ordering algorithm
|
# TODO: use brim ordering algorithm
|
||||||
|
@ -753,10 +762,6 @@ sub generate_toolpaths {
|
||||||
$to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing);
|
$to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_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;
|
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(
|
||||||
|
|
|
@ -90,7 +90,7 @@ $config->apply($cli_config);
|
||||||
|
|
||||||
# launch GUI
|
# launch GUI
|
||||||
my $gui;
|
my $gui;
|
||||||
if (!@ARGV && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
||||||
{
|
{
|
||||||
no warnings 'once';
|
no warnings 'once';
|
||||||
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir});
|
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir});
|
||||||
|
@ -102,6 +102,9 @@ if (!@ARGV && !$opt{save} && eval "require Slic3r::GUI; 1") {
|
||||||
setlocale(LC_NUMERIC, 'C');
|
setlocale(LC_NUMERIC, 'C');
|
||||||
$gui->{mainframe}->load_config_file($_) for @{$opt{load}};
|
$gui->{mainframe}->load_config_file($_) for @{$opt{load}};
|
||||||
$gui->{mainframe}->load_config($cli_config);
|
$gui->{mainframe}->load_config($cli_config);
|
||||||
|
foreach my $input_file (@ARGV) {
|
||||||
|
$gui->{mainframe}{plater}->load_file($input_file) unless $opt{no_plater};
|
||||||
|
}
|
||||||
$gui->MainLoop;
|
$gui->MainLoop;
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
@ -261,6 +264,8 @@ Usage: slic3r.pl [ OPTIONS ] [ file.stl ] [ file2.stl ] ...
|
||||||
|
|
||||||
$j
|
$j
|
||||||
GUI options:
|
GUI options:
|
||||||
|
--gui Forces the GUI launch instead of command line slicing (if you
|
||||||
|
supply a model file, it will be loaded into the plater)
|
||||||
--no-plater Disable the plater tab
|
--no-plater Disable the plater tab
|
||||||
--gui-mode Overrides the configured mode (simple/expert)
|
--gui-mode Overrides the configured mode (simple/expert)
|
||||||
--autosave <file> Automatically export current configuration to the specified file
|
--autosave <file> Automatically export current configuration to the specified file
|
||||||
|
@ -282,7 +287,7 @@ $j
|
||||||
(default: 100,100)
|
(default: 100,100)
|
||||||
--z-offset Additional height in mm to add to vertical coordinates
|
--z-offset Additional height in mm to add to vertical coordinates
|
||||||
(+/-, default: $config->{z_offset})
|
(+/-, default: $config->{z_offset})
|
||||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/no-extrusion,
|
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/no-extrusion,
|
||||||
default: $config->{gcode_flavor})
|
default: $config->{gcode_flavor})
|
||||||
--use-relative-e-distances Enable this to get relative E values (default: no)
|
--use-relative-e-distances Enable this to get relative E values (default: no)
|
||||||
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
--use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no)
|
||||||
|
|
|
@ -124,7 +124,6 @@ if (0) {
|
||||||
|
|
||||||
# copy of Print::export_gcode() up to the point
|
# copy of Print::export_gcode() up to the point
|
||||||
# after fill surfaces are combined
|
# after fill surfaces are combined
|
||||||
$self->init_extruders;
|
|
||||||
$_->slice for @{$self->objects};
|
$_->slice for @{$self->objects};
|
||||||
$_->make_perimeters for @{$self->objects};
|
$_->make_perimeters for @{$self->objects};
|
||||||
$_->detect_surfaces_type for @{$self->objects};
|
$_->detect_surfaces_type for @{$self->objects};
|
||||||
|
|
21
t/fill.t
21
t/fill.t
|
@ -12,7 +12,7 @@ BEGIN {
|
||||||
use List::Util qw(first sum);
|
use List::Util qw(first sum);
|
||||||
use Slic3r;
|
use Slic3r;
|
||||||
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
|
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
|
||||||
use Slic3r::Geometry::Clipper qw(union diff_ex offset);
|
use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex);
|
||||||
use Slic3r::Surface qw(:types);
|
use Slic3r::Surface qw(:types);
|
||||||
use Slic3r::Test;
|
use Slic3r::Test;
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
||||||
|
|
||||||
{
|
{
|
||||||
my $print = Slic3r::Print->new;
|
my $print = Slic3r::Print->new;
|
||||||
$print->init_extruders;
|
|
||||||
my $filler = Slic3r::Fill::Rectilinear->new(
|
my $filler = Slic3r::Fill::Rectilinear->new(
|
||||||
print => $print,
|
print => $print,
|
||||||
bounding_box => Slic3r::Geometry::BoundingBox->new_from_points([ Slic3r::Point->new(0, 0), Slic3r::Point->new(10, 10) ]),
|
bounding_box => Slic3r::Geometry::BoundingBox->new_from_points([ Slic3r::Point->new(0, 0), Slic3r::Point->new(10, 10) ]),
|
||||||
|
@ -249,18 +248,27 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||||
{
|
{
|
||||||
my $config = Slic3r::Config->new_from_defaults;
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
$config->set('skirts', 0);
|
$config->set('skirts', 0);
|
||||||
$config->set('perimeters', 0);
|
$config->set('perimeters', 1);
|
||||||
$config->set('fill_density', 0);
|
$config->set('fill_density', 0);
|
||||||
$config->set('top_solid_layers', 0);
|
$config->set('top_solid_layers', 0);
|
||||||
$config->set('bottom_solid_layers', 0);
|
$config->set('bottom_solid_layers', 0);
|
||||||
$config->set('solid_infill_below_area', 20000000);
|
$config->set('solid_infill_below_area', 20000000);
|
||||||
$config->set('solid_infill_every_layers', 2);
|
$config->set('solid_infill_every_layers', 2);
|
||||||
|
$config->set('perimeter_speed', 99);
|
||||||
|
$config->set('external_perimeter_speed', 99);
|
||||||
|
$config->set('cooling', 0);
|
||||||
|
$config->set('first_layer_speed', '100%');
|
||||||
|
|
||||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||||
my %layers_with_extrusion = ();
|
my %layers_with_extrusion = ();
|
||||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||||
my ($self, $cmd, $args, $info) = @_;
|
my ($self, $cmd, $args, $info) = @_;
|
||||||
$layers_with_extrusion{$self->Z} = 1 if $info->{extruding};
|
|
||||||
|
if ($cmd eq 'G1' && $info->{dist_XY} > 0 && $info->{extruding}) {
|
||||||
|
if (($args->{F} // $self->F) != $config->perimeter_speed*60) {
|
||||||
|
$layers_with_extrusion{$self->Z} = ($args->{F} // $self->F);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ok !%layers_with_extrusion,
|
ok !%layers_with_extrusion,
|
||||||
|
@ -276,6 +284,7 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||||
$config->set('first_layer_height', 0.2);
|
$config->set('first_layer_height', 0.2);
|
||||||
$config->set('nozzle_diameter', [0.35]);
|
$config->set('nozzle_diameter', [0.35]);
|
||||||
$config->set('infill_extruder', 2);
|
$config->set('infill_extruder', 2);
|
||||||
|
$config->set('solid_infill_extruder', 2);
|
||||||
$config->set('infill_extrusion_width', 0.52);
|
$config->set('infill_extrusion_width', 0.52);
|
||||||
$config->set('solid_infill_extrusion_width', 0.52);
|
$config->set('solid_infill_extrusion_width', 0.52);
|
||||||
$config->set('first_layer_extrusion_width', 0);
|
$config->set('first_layer_extrusion_width', 0);
|
||||||
|
@ -302,7 +311,9 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||||
my $grow_d = scale($config->infill_extrusion_width)/2;
|
my $grow_d = scale($config->infill_extrusion_width)/2;
|
||||||
my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]);
|
my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]);
|
||||||
my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]);
|
my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]);
|
||||||
my $diff = [ grep { $_->area > 2*(($grow_d*2)**2) } @{diff_ex($layer0_infill, $layer1_infill)} ];
|
my $diff = diff($layer0_infill, $layer1_infill);
|
||||||
|
$diff = offset2_ex($diff, -$grow_d, +$grow_d);
|
||||||
|
$diff = [ grep { $_->area > 2*(($grow_d*2)**2) } @$diff ];
|
||||||
is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0';
|
is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
t/gcode.t
18
t/gcode.t
|
@ -1,4 +1,4 @@
|
||||||
use Test::More tests => 20;
|
use Test::More tests => 22;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
@ -86,6 +86,7 @@ use Slic3r::Test;
|
||||||
# - no hard-coded "E" are generated
|
# - no hard-coded "E" are generated
|
||||||
# - Z moves are correctly generated for both objects
|
# - Z moves are correctly generated for both objects
|
||||||
# - no travel moves go outside skirt
|
# - no travel moves go outside skirt
|
||||||
|
# - temperatures are set correctly
|
||||||
my $config = Slic3r::Config->new_from_defaults;
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
$config->set('gcode_comments', 1);
|
$config->set('gcode_comments', 1);
|
||||||
$config->set('complete_objects', 1);
|
$config->set('complete_objects', 1);
|
||||||
|
@ -93,11 +94,14 @@ use Slic3r::Test;
|
||||||
$config->set('start_gcode', ''); # prevent any default extra Z move
|
$config->set('start_gcode', ''); # prevent any default extra Z move
|
||||||
$config->set('layer_height', 0.4);
|
$config->set('layer_height', 0.4);
|
||||||
$config->set('first_layer_height', 0.4);
|
$config->set('first_layer_height', 0.4);
|
||||||
|
$config->set('temperature', [200]);
|
||||||
|
$config->set('first_layer_temperature', [210]);
|
||||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
|
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 2);
|
||||||
ok my $gcode = Slic3r::Test::gcode($print), "complete_objects";
|
ok my $gcode = Slic3r::Test::gcode($print), "complete_objects";
|
||||||
my @z_moves = ();
|
my @z_moves = ();
|
||||||
my @travel_moves = (); # array of scaled points
|
my @travel_moves = (); # array of scaled points
|
||||||
my @extrusions = (); # array of scaled points
|
my @extrusions = (); # array of scaled points
|
||||||
|
my @temps = ();
|
||||||
Slic3r::GCode::Reader->new->parse($gcode, sub {
|
Slic3r::GCode::Reader->new->parse($gcode, sub {
|
||||||
my ($self, $cmd, $args, $info) = @_;
|
my ($self, $cmd, $args, $info) = @_;
|
||||||
fail 'unexpected E argument' if defined $args->{E};
|
fail 'unexpected E argument' if defined $args->{E};
|
||||||
|
@ -112,6 +116,8 @@ use Slic3r::Test;
|
||||||
push @travel_moves, Slic3r::Point->new_scale($info->{new_X}, $info->{new_Y})
|
push @travel_moves, Slic3r::Point->new_scale($info->{new_X}, $info->{new_Y})
|
||||||
if @extrusions; # skip initial travel move to first skirt point
|
if @extrusions; # skip initial travel move to first skirt point
|
||||||
}
|
}
|
||||||
|
} elsif ($cmd eq 'M104' || $cmd eq 'M109') {
|
||||||
|
push @temps, $args->{S} if !@temps || $args->{S} != $temps[-1];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
my $layer_count = 20/0.4; # cube is 20mm tall
|
my $layer_count = 20/0.4; # cube is 20mm tall
|
||||||
|
@ -120,6 +126,8 @@ use Slic3r::Test;
|
||||||
|
|
||||||
my $convex_hull = convex_hull(\@extrusions);
|
my $convex_hull = convex_hull(\@extrusions);
|
||||||
ok !(defined first { !$convex_hull->contains_point($_) } @travel_moves), 'all travel moves happen within skirt';
|
ok !(defined first { !$convex_hull->contains_point($_) } @travel_moves), 'all travel moves happen within skirt';
|
||||||
|
|
||||||
|
is_deeply \@temps, [210, 200, 210, 200, 0], 'expected temperature changes';
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -184,4 +192,12 @@ use Slic3r::Test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
|
$config->set('start_gcode', 'START:[input_filename]');
|
||||||
|
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||||
|
my $gcode = Slic3r::Test::gcode($print);
|
||||||
|
like $gcode, qr/START:20mm_cube/, '[input_filename] is also available in custom G-code';
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -72,6 +72,9 @@ use Slic3r::Test;
|
||||||
$config->set('bottom_solid_layers', 0);
|
$config->set('bottom_solid_layers', 0);
|
||||||
ok $test->(), "no shells are applied when both top and bottom are set to zero";
|
ok $test->(), "no shells are applied when both top and bottom are set to zero";
|
||||||
|
|
||||||
|
$config->set('perimeters', 1);
|
||||||
|
$config->set('top_solid_layers', 3);
|
||||||
|
$config->set('bottom_solid_layers', 3);
|
||||||
$config->set('fill_density', 0);
|
$config->set('fill_density', 0);
|
||||||
ok $test->(), "proper number of shells is applied even when fill density is none";
|
ok $test->(), "proper number of shells is applied even when fill density is none";
|
||||||
}
|
}
|
||||||
|
|
53
t/support.t
53
t/support.t
|
@ -1,4 +1,4 @@
|
||||||
use Test::More tests => 20;
|
use Test::More tests => 25;
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ use Slic3r::Test;
|
||||||
|
|
||||||
my $test = sub {
|
my $test = sub {
|
||||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||||
$print->print->init_extruders;
|
|
||||||
my $flow = $print->print->objects->[0]->support_material_flow;
|
my $flow = $print->print->objects->[0]->support_material_flow;
|
||||||
my $support = Slic3r::Print::SupportMaterial->new(
|
my $support = Slic3r::Print::SupportMaterial->new(
|
||||||
object_config => $print->print->objects->[0]->config,
|
object_config => $print->print->objects->[0]->config,
|
||||||
|
@ -180,4 +179,54 @@ use Slic3r::Test;
|
||||||
$test->(70);
|
$test->(70);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
|
$config->set('brim_width', 0);
|
||||||
|
$config->set('skirts', 0);
|
||||||
|
$config->set('support_material', 1);
|
||||||
|
$config->set('top_solid_layers', 0); # so that we don't have the internal bridge over infill
|
||||||
|
$config->set('bridge_speed', 99);
|
||||||
|
$config->set('cooling', 0);
|
||||||
|
$config->set('first_layer_speed', '100%');
|
||||||
|
|
||||||
|
my $test = sub {
|
||||||
|
my $print = Slic3r::Test::init_print('overhang', config => $config);
|
||||||
|
|
||||||
|
my $has_bridge_speed = 0;
|
||||||
|
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||||
|
my ($self, $cmd, $args, $info) = @_;
|
||||||
|
|
||||||
|
if ($info->{extruding}) {
|
||||||
|
if (($args->{F} // $self->F) == $config->bridge_speed*60) {
|
||||||
|
$has_bridge_speed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $has_bridge_speed;
|
||||||
|
};
|
||||||
|
|
||||||
|
$config->set('support_material_contact_distance', 0.2);
|
||||||
|
ok $test->(), 'bridge speed is used when support_material_contact_distance > 0';
|
||||||
|
|
||||||
|
$config->set('support_material_contact_distance', 0);
|
||||||
|
ok !$test->(), 'bridge speed is not used when support_material_contact_distance == 0';
|
||||||
|
|
||||||
|
$config->set('raft_layers', 5);
|
||||||
|
$config->set('support_material_contact_distance', 0.2);
|
||||||
|
ok $test->(), 'bridge speed is used when raft_layers > 0 and support_material_contact_distance > 0';
|
||||||
|
|
||||||
|
$config->set('support_material_contact_distance', 0);
|
||||||
|
ok !$test->(), 'bridge speed is not used when raft_layers > 0 and support_material_contact_distance == 0';
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
my $config = Slic3r::Config->new_from_defaults;
|
||||||
|
$config->set('raft_layers', 3);
|
||||||
|
$config->set('nozzle_diameter', [0.4, 1]);
|
||||||
|
$config->set('first_layer_height', 0.8);
|
||||||
|
$config->set('support_material_extruder', 2);
|
||||||
|
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||||
|
ok Slic3r::Test::gcode($print), 'first_layer_height is validated with support material extruder nozzle diameter when using raft layers';
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
|
@ -22,7 +22,7 @@ _arguments -S \
|
||||||
'*--nozzle-diameter[specify nozzle diameter]:nozzle diameter in mm' \
|
'*--nozzle-diameter[specify nozzle diameter]:nozzle diameter in mm' \
|
||||||
'--print-center[specify print center coordinates]:print center coordinates in mm,mm' \
|
'--print-center[specify print center coordinates]:print center coordinates in mm,mm' \
|
||||||
'--z-offset[specify Z-axis offset]:Z-axis offset in mm' \
|
'--z-offset[specify Z-axis offset]:Z-axis offset in mm' \
|
||||||
'--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerware sailfish mach3 no-extrusion)' \
|
'--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerware sailfish mach3 machinekit no-extrusion)' \
|
||||||
'(--use-relative-e-distances --no-use-relative-e-distances)'--{no-,}use-relative-e-distances'[disable/enable relative E values]' \
|
'(--use-relative-e-distances --no-use-relative-e-distances)'--{no-,}use-relative-e-distances'[disable/enable relative E values]' \
|
||||||
'--extrusion-axis[specify letter associated with the extrusion axis]:extrusion axis letter' \
|
'--extrusion-axis[specify letter associated with the extrusion axis]:extrusion axis letter' \
|
||||||
'(--gcode-arcs --no-gcode-arcs)'--{no-,}gcode-arcs'[disable/enable G2/G3 commands for native arcs]' \
|
'(--gcode-arcs --no-gcode-arcs)'--{no-,}gcode-arcs'[disable/enable G2/G3 commands for native arcs]' \
|
||||||
|
|
|
@ -63,7 +63,7 @@ my $build = Module::Build::WithXSpp->new(
|
||||||
dist_abstract => 'XS code for Slic3r',
|
dist_abstract => 'XS code for Slic3r',
|
||||||
build_requires => {qw(
|
build_requires => {qw(
|
||||||
ExtUtils::ParseXS 3.18
|
ExtUtils::ParseXS 3.18
|
||||||
ExtUtils::Typemap 1.00
|
ExtUtils::Typemaps 1.00
|
||||||
ExtUtils::Typemaps::Default 1.05
|
ExtUtils::Typemaps::Default 1.05
|
||||||
ExtUtils::XSpp 0.17
|
ExtUtils::XSpp 0.17
|
||||||
Module::Build 0.3601
|
Module::Build 0.3601
|
||||||
|
|
|
@ -135,7 +135,21 @@ stl_count_facets(stl_file *stl, char *file) {
|
||||||
/* Otherwise, if the .STL file is ASCII, then do the following */
|
/* Otherwise, if the .STL file is ASCII, then do the following */
|
||||||
else {
|
else {
|
||||||
/* Reopen the file in text mode (for getting correct newlines on Windows) */
|
/* Reopen the file in text mode (for getting correct newlines on Windows) */
|
||||||
freopen(file, "r", stl->fp);
|
// fix to silence a warning about unused return value.
|
||||||
|
// obviously if it fails we have problems....
|
||||||
|
stl->fp = freopen(file, "r", stl->fp);
|
||||||
|
|
||||||
|
// do another null check to be safe
|
||||||
|
if(stl->fp == NULL) {
|
||||||
|
error_msg = (char*)
|
||||||
|
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
|
||||||
|
sprintf(error_msg, "stl_initialize: Couldn't open %s for reading",
|
||||||
|
file);
|
||||||
|
perror(error_msg);
|
||||||
|
free(error_msg);
|
||||||
|
stl->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find the number of facets */
|
/* Find the number of facets */
|
||||||
j = 0;
|
j = 0;
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Version : 6.2.1 *
|
* Version : 6.2.9 *
|
||||||
* Date : 31 October 2014 *
|
* Date : 16 February 2015 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2014 *
|
* Copyright : Angus Johnson 2010-2015 *
|
||||||
* *
|
* *
|
||||||
* License: *
|
* License: *
|
||||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||||
|
@ -381,13 +381,6 @@ Int128 Int128Mul (long64 lhs, long64 rhs)
|
||||||
// Miscellaneous global functions
|
// Miscellaneous global functions
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void Swap(cInt& val1, cInt& val2)
|
|
||||||
{
|
|
||||||
cInt tmp = val1;
|
|
||||||
val1 = val2;
|
|
||||||
val2 = tmp;
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool Orientation(const Path &poly)
|
bool Orientation(const Path &poly)
|
||||||
{
|
{
|
||||||
return Area(poly) >= 0;
|
return Area(poly) >= 0;
|
||||||
|
@ -435,11 +428,11 @@ bool PointIsVertex(const IntPoint &Pt, OutPt *pp)
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
int PointInPolygon (const IntPoint &pt, const Path &path)
|
//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
|
||||||
|
//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
|
||||||
|
int PointInPolygon(const IntPoint &pt, const Path &path)
|
||||||
{
|
{
|
||||||
//returns 0 if false, +1 if true, -1 if pt ON polygon boundary
|
//returns 0 if false, +1 if true, -1 if pt ON polygon boundary
|
||||||
//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos
|
|
||||||
//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf
|
|
||||||
int result = 0;
|
int result = 0;
|
||||||
size_t cnt = path.size();
|
size_t cnt = path.size();
|
||||||
if (cnt < 3) return 0;
|
if (cnt < 3) return 0;
|
||||||
|
@ -758,9 +751,9 @@ inline void ReverseHorizontal(TEdge &e)
|
||||||
//swap horizontal edges' Top and Bottom x's so they follow the natural
|
//swap horizontal edges' Top and Bottom x's so they follow the natural
|
||||||
//progression of the bounds - ie so their xbots will align with the
|
//progression of the bounds - ie so their xbots will align with the
|
||||||
//adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
|
//adjoining lower edge. [Helpful in the ProcessHorizontal() method.]
|
||||||
Swap(e.Top.X, e.Bot.X);
|
std::swap(e.Top.X, e.Bot.X);
|
||||||
#ifdef use_xyz
|
#ifdef use_xyz
|
||||||
Swap(e.Top.Z, e.Bot.Z);
|
std::swap(e.Top.Z, e.Bot.Z);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
@ -866,8 +859,8 @@ bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1,
|
||||||
|
|
||||||
bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
|
bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b)
|
||||||
{
|
{
|
||||||
if (seg1a > seg1b) Swap(seg1a, seg1b);
|
if (seg1a > seg1b) std::swap(seg1a, seg1b);
|
||||||
if (seg2a > seg2b) Swap(seg2a, seg2b);
|
if (seg2a > seg2b) std::swap(seg2a, seg2b);
|
||||||
return (seg1a < seg2b) && (seg2a < seg1b);
|
return (seg1a < seg2b) && (seg2a < seg1b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,16 +969,13 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
|
||||||
EStart = E->Prev;
|
EStart = E->Prev;
|
||||||
else
|
else
|
||||||
EStart = E->Next;
|
EStart = E->Next;
|
||||||
if (EStart->OutIdx != Skip)
|
if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
|
||||||
{
|
|
||||||
if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge
|
|
||||||
{
|
{
|
||||||
if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)
|
if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X)
|
||||||
ReverseHorizontal(*E);
|
ReverseHorizontal(*E);
|
||||||
}
|
}
|
||||||
else if (EStart->Bot.X != E->Bot.X)
|
else if (EStart->Bot.X != E->Bot.X)
|
||||||
ReverseHorizontal(*E);
|
ReverseHorizontal(*E);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EStart = E;
|
EStart = E;
|
||||||
|
@ -1000,11 +990,7 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
|
||||||
//unless a Skip edge is encountered when that becomes the top divide
|
//unless a Skip edge is encountered when that becomes the top divide
|
||||||
Horz = Result;
|
Horz = Result;
|
||||||
while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;
|
while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev;
|
||||||
if (Horz->Prev->Top.X == Result->Next->Top.X)
|
if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
|
||||||
{
|
|
||||||
if (!NextIsForward) Result = Horz->Prev;
|
|
||||||
}
|
|
||||||
else if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev;
|
|
||||||
}
|
}
|
||||||
while (E != Result)
|
while (E != Result)
|
||||||
{
|
{
|
||||||
|
@ -1024,11 +1010,8 @@ TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward)
|
||||||
{
|
{
|
||||||
Horz = Result;
|
Horz = Result;
|
||||||
while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;
|
while (IsHorizontal(*Horz->Next)) Horz = Horz->Next;
|
||||||
if (Horz->Next->Top.X == Result->Prev->Top.X)
|
if (Horz->Next->Top.X == Result->Prev->Top.X ||
|
||||||
{
|
Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
|
||||||
if (!NextIsForward) Result = Horz->Next;
|
|
||||||
}
|
|
||||||
else if (Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (E != Result)
|
while (E != Result)
|
||||||
|
@ -1155,17 +1138,17 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
E->Prev->OutIdx = Skip;
|
E->Prev->OutIdx = Skip;
|
||||||
if (E->Prev->Bot.X < E->Prev->Top.X) ReverseHorizontal(*E->Prev);
|
|
||||||
MinimaList::value_type locMin;
|
MinimaList::value_type locMin;
|
||||||
locMin.Y = E->Bot.Y;
|
locMin.Y = E->Bot.Y;
|
||||||
locMin.LeftBound = 0;
|
locMin.LeftBound = 0;
|
||||||
locMin.RightBound = E;
|
locMin.RightBound = E;
|
||||||
locMin.RightBound->Side = esRight;
|
locMin.RightBound->Side = esRight;
|
||||||
locMin.RightBound->WindDelta = 0;
|
locMin.RightBound->WindDelta = 0;
|
||||||
while (E->Next->OutIdx != Skip)
|
for (;;)
|
||||||
{
|
{
|
||||||
E->NextInLML = E->Next;
|
|
||||||
if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
|
if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E);
|
||||||
|
if (E->Next->OutIdx == Skip) break;
|
||||||
|
E->NextInLML = E->Next;
|
||||||
E = E->Next;
|
E = E->Next;
|
||||||
}
|
}
|
||||||
m_MinimaList.push_back(locMin);
|
m_MinimaList.push_back(locMin);
|
||||||
|
@ -1371,6 +1354,7 @@ void Clipper::Reset()
|
||||||
{
|
{
|
||||||
ClipperBase::Reset();
|
ClipperBase::Reset();
|
||||||
m_Scanbeam = ScanbeamList();
|
m_Scanbeam = ScanbeamList();
|
||||||
|
m_Maxima = MaximaList();
|
||||||
m_ActiveEdges = 0;
|
m_ActiveEdges = 0;
|
||||||
m_SortedEdges = 0;
|
m_SortedEdges = 0;
|
||||||
for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
|
for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm)
|
||||||
|
@ -1378,12 +1362,24 @@ void Clipper::Reset()
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType)
|
||||||
|
{
|
||||||
|
return Execute(clipType, solution, fillType, fillType);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType)
|
||||||
|
{
|
||||||
|
return Execute(clipType, polytree, fillType, fillType);
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
bool Clipper::Execute(ClipType clipType, Paths &solution,
|
bool Clipper::Execute(ClipType clipType, Paths &solution,
|
||||||
PolyFillType subjFillType, PolyFillType clipFillType)
|
PolyFillType subjFillType, PolyFillType clipFillType)
|
||||||
{
|
{
|
||||||
if( m_ExecuteLocked ) return false;
|
if( m_ExecuteLocked ) return false;
|
||||||
if (m_HasOpenPaths)
|
if (m_HasOpenPaths)
|
||||||
throw clipperException("Error: PolyTree struct is need for open path clipping.");
|
throw clipperException("Error: PolyTree struct is needed for open path clipping.");
|
||||||
m_ExecuteLocked = true;
|
m_ExecuteLocked = true;
|
||||||
solution.resize(0);
|
solution.resize(0);
|
||||||
m_SubjFillType = subjFillType;
|
m_SubjFillType = subjFillType;
|
||||||
|
@ -1439,9 +1435,9 @@ bool Clipper::ExecuteInternal()
|
||||||
cInt botY = PopScanbeam();
|
cInt botY = PopScanbeam();
|
||||||
do {
|
do {
|
||||||
InsertLocalMinimaIntoAEL(botY);
|
InsertLocalMinimaIntoAEL(botY);
|
||||||
ClearGhostJoins();
|
ProcessHorizontals();
|
||||||
ProcessHorizontals(false);
|
ClearGhostJoins();
|
||||||
if (m_Scanbeam.empty()) break;
|
if (m_Scanbeam.empty()) break;
|
||||||
cInt topY = PopScanbeam();
|
cInt topY = PopScanbeam();
|
||||||
succeeded = ProcessIntersections(topY);
|
succeeded = ProcessIntersections(topY);
|
||||||
if (!succeeded) break;
|
if (!succeeded) break;
|
||||||
|
@ -1471,7 +1467,10 @@ bool Clipper::ExecuteInternal()
|
||||||
for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
|
for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i)
|
||||||
{
|
{
|
||||||
OutRec *outRec = m_PolyOuts[i];
|
OutRec *outRec = m_PolyOuts[i];
|
||||||
if (outRec->Pts && !outRec->IsOpen)
|
if (!outRec->Pts) continue;
|
||||||
|
if (outRec->IsOpen)
|
||||||
|
FixupOutPolyline(*outRec);
|
||||||
|
else
|
||||||
FixupOutPolygon(*outRec);
|
FixupOutPolygon(*outRec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1486,17 +1485,16 @@ bool Clipper::ExecuteInternal()
|
||||||
|
|
||||||
void Clipper::InsertScanbeam(const cInt Y)
|
void Clipper::InsertScanbeam(const cInt Y)
|
||||||
{
|
{
|
||||||
//if (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) return;// avoid duplicates.
|
m_Scanbeam.push(Y);
|
||||||
m_Scanbeam.push(Y);
|
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
cInt Clipper::PopScanbeam()
|
cInt Clipper::PopScanbeam()
|
||||||
{
|
{
|
||||||
const cInt Y = m_Scanbeam.top();
|
const cInt Y = m_Scanbeam.top();
|
||||||
m_Scanbeam.pop();
|
m_Scanbeam.pop();
|
||||||
while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.
|
while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates.
|
||||||
return Y;
|
return Y;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -2356,7 +2354,6 @@ OutRec* Clipper::CreateOutRec()
|
||||||
|
|
||||||
OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
|
OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
|
||||||
{
|
{
|
||||||
bool ToFront = (e->Side == esLeft);
|
|
||||||
if( e->OutIdx < 0 )
|
if( e->OutIdx < 0 )
|
||||||
{
|
{
|
||||||
OutRec *outRec = CreateOutRec();
|
OutRec *outRec = CreateOutRec();
|
||||||
|
@ -2377,7 +2374,8 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
|
||||||
//OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
|
//OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most'
|
||||||
OutPt* op = outRec->Pts;
|
OutPt* op = outRec->Pts;
|
||||||
|
|
||||||
if (ToFront && (pt == op->Pt)) return op;
|
bool ToFront = (e->Side == esLeft);
|
||||||
|
if (ToFront && (pt == op->Pt)) return op;
|
||||||
else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
|
else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev;
|
||||||
|
|
||||||
OutPt* newOp = new OutPt;
|
OutPt* newOp = new OutPt;
|
||||||
|
@ -2393,13 +2391,23 @@ OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt)
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void Clipper::ProcessHorizontals(bool IsTopOfScanbeam)
|
OutPt* Clipper::GetLastOutPt(TEdge *e)
|
||||||
|
{
|
||||||
|
OutRec *outRec = m_PolyOuts[e->OutIdx];
|
||||||
|
if (e->Side == esLeft)
|
||||||
|
return outRec->Pts;
|
||||||
|
else
|
||||||
|
return outRec->Pts->Prev;
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Clipper::ProcessHorizontals()
|
||||||
{
|
{
|
||||||
TEdge* horzEdge = m_SortedEdges;
|
TEdge* horzEdge = m_SortedEdges;
|
||||||
while(horzEdge)
|
while(horzEdge)
|
||||||
{
|
{
|
||||||
DeleteFromSEL(horzEdge);
|
DeleteFromSEL(horzEdge);
|
||||||
ProcessHorizontal(horzEdge, IsTopOfScanbeam);
|
ProcessHorizontal(horzEdge);
|
||||||
horzEdge = m_SortedEdges;
|
horzEdge = m_SortedEdges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2564,10 +2572,11 @@ void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right)
|
||||||
* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
|
* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. *
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
|
void Clipper::ProcessHorizontal(TEdge *horzEdge)
|
||||||
{
|
{
|
||||||
Direction dir;
|
Direction dir;
|
||||||
cInt horzLeft, horzRight;
|
cInt horzLeft, horzRight;
|
||||||
|
bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen);
|
||||||
|
|
||||||
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
|
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
|
||||||
|
|
||||||
|
@ -2577,50 +2586,100 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
|
||||||
if (!eLastHorz->NextInLML)
|
if (!eLastHorz->NextInLML)
|
||||||
eMaxPair = GetMaximaPair(eLastHorz);
|
eMaxPair = GetMaximaPair(eLastHorz);
|
||||||
|
|
||||||
for (;;)
|
MaximaList::const_iterator maxIt;
|
||||||
|
MaximaList::const_reverse_iterator maxRit;
|
||||||
|
if (m_Maxima.size() > 0)
|
||||||
{
|
{
|
||||||
|
//get the first maxima in range (X) ...
|
||||||
|
if (dir == dLeftToRight)
|
||||||
|
{
|
||||||
|
maxIt = m_Maxima.begin();
|
||||||
|
while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++;
|
||||||
|
if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X)
|
||||||
|
maxIt = m_Maxima.end();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
maxRit = m_Maxima.rbegin();
|
||||||
|
while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++;
|
||||||
|
if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X)
|
||||||
|
maxRit = m_Maxima.rend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutPt* op1 = 0;
|
||||||
|
|
||||||
|
for (;;) //loop through consec. horizontal edges
|
||||||
|
{
|
||||||
|
|
||||||
bool IsLastHorz = (horzEdge == eLastHorz);
|
bool IsLastHorz = (horzEdge == eLastHorz);
|
||||||
TEdge* e = GetNextInAEL(horzEdge, dir);
|
TEdge* e = GetNextInAEL(horzEdge, dir);
|
||||||
while(e)
|
while(e)
|
||||||
{
|
{
|
||||||
//Break if we've got to the end of an intermediate horizontal edge ...
|
|
||||||
//nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
|
|
||||||
if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&
|
|
||||||
e->Dx < horzEdge->NextInLML->Dx) break;
|
|
||||||
|
|
||||||
TEdge* eNext = GetNextInAEL(e, dir); //saves eNext for later
|
//this code block inserts extra coords into horizontal edges (in output
|
||||||
|
//polygons) whereever maxima touch these horizontal edges. This helps
|
||||||
|
//'simplifying' polygons (ie if the Simplify property is set).
|
||||||
|
if (m_Maxima.size() > 0)
|
||||||
|
{
|
||||||
|
if (dir == dLeftToRight)
|
||||||
|
{
|
||||||
|
while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X)
|
||||||
|
{
|
||||||
|
if (horzEdge->OutIdx >= 0 && !IsOpen)
|
||||||
|
AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y));
|
||||||
|
maxIt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X)
|
||||||
|
{
|
||||||
|
if (horzEdge->OutIdx >= 0 && !IsOpen)
|
||||||
|
AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y));
|
||||||
|
maxRit++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if ((dir == dLeftToRight && e->Curr.X <= horzRight) ||
|
if ((dir == dLeftToRight && e->Curr.X > horzRight) ||
|
||||||
(dir == dRightToLeft && e->Curr.X >= horzLeft))
|
(dir == dRightToLeft && e->Curr.X < horzLeft)) break;
|
||||||
{
|
|
||||||
//so far we're still in range of the horizontal Edge but make sure
|
//Also break if we've got to the end of an intermediate horizontal edge ...
|
||||||
|
//nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal.
|
||||||
|
if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML &&
|
||||||
|
e->Dx < horzEdge->NextInLML->Dx) break;
|
||||||
|
|
||||||
|
if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times
|
||||||
|
{
|
||||||
|
op1 = AddOutPt(horzEdge, e->Curr);
|
||||||
|
TEdge* eNextHorz = m_SortedEdges;
|
||||||
|
while (eNextHorz)
|
||||||
|
{
|
||||||
|
if (eNextHorz->OutIdx >= 0 &&
|
||||||
|
HorzSegmentsOverlap(horzEdge->Bot.X,
|
||||||
|
horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
|
||||||
|
{
|
||||||
|
OutPt* op2 = GetLastOutPt(eNextHorz);
|
||||||
|
AddJoin(op2, op1, eNextHorz->Top);
|
||||||
|
}
|
||||||
|
eNextHorz = eNextHorz->NextInSEL;
|
||||||
|
}
|
||||||
|
AddGhostJoin(op1, horzEdge->Bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
//OK, so far we're still in range of the horizontal Edge but make sure
|
||||||
//we're at the last of consec. horizontals when matching with eMaxPair
|
//we're at the last of consec. horizontals when matching with eMaxPair
|
||||||
if(e == eMaxPair && IsLastHorz)
|
if(e == eMaxPair && IsLastHorz)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (horzEdge->OutIdx >= 0)
|
if (horzEdge->OutIdx >= 0)
|
||||||
{
|
|
||||||
OutPt* op1 = AddOutPt(horzEdge, horzEdge->Top);
|
|
||||||
TEdge* eNextHorz = m_SortedEdges;
|
|
||||||
while (eNextHorz)
|
|
||||||
{
|
|
||||||
if (eNextHorz->OutIdx >= 0 &&
|
|
||||||
HorzSegmentsOverlap(horzEdge->Bot.X,
|
|
||||||
horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
|
|
||||||
{
|
|
||||||
OutPt* op2 = AddOutPt(eNextHorz, eNextHorz->Bot);
|
|
||||||
AddJoin(op2, op1, eNextHorz->Top);
|
|
||||||
}
|
|
||||||
eNextHorz = eNextHorz->NextInSEL;
|
|
||||||
}
|
|
||||||
AddGhostJoin(op1, horzEdge->Bot);
|
|
||||||
AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
|
AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top);
|
||||||
}
|
|
||||||
DeleteFromAEL(horzEdge);
|
DeleteFromAEL(horzEdge);
|
||||||
DeleteFromAEL(eMaxPair);
|
DeleteFromAEL(eMaxPair);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if(dir == dLeftToRight)
|
|
||||||
|
if(dir == dLeftToRight)
|
||||||
{
|
{
|
||||||
IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
|
IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
|
||||||
IntersectEdges(horzEdge, e, Pt);
|
IntersectEdges(horzEdge, e, Pt);
|
||||||
|
@ -2630,28 +2689,43 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam)
|
||||||
IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
|
IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y);
|
||||||
IntersectEdges( e, horzEdge, Pt);
|
IntersectEdges( e, horzEdge, Pt);
|
||||||
}
|
}
|
||||||
|
TEdge* eNext = GetNextInAEL(e, dir);
|
||||||
SwapPositionsInAEL( horzEdge, e );
|
SwapPositionsInAEL( horzEdge, e );
|
||||||
}
|
e = eNext;
|
||||||
else if( (dir == dLeftToRight && e->Curr.X >= horzRight) ||
|
} //end while(e)
|
||||||
(dir == dRightToLeft && e->Curr.X <= horzLeft) ) break;
|
|
||||||
e = eNext;
|
//Break out of loop if HorzEdge.NextInLML is not also horizontal ...
|
||||||
} //end while
|
if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break;
|
||||||
|
|
||||||
|
UpdateEdgeIntoAEL(horzEdge);
|
||||||
|
if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
|
||||||
|
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
|
||||||
|
|
||||||
if (horzEdge->NextInLML && IsHorizontal(*horzEdge->NextInLML))
|
|
||||||
{
|
|
||||||
UpdateEdgeIntoAEL(horzEdge);
|
|
||||||
if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot);
|
|
||||||
GetHorzDirection(*horzEdge, dir, horzLeft, horzRight);
|
|
||||||
} else
|
|
||||||
break;
|
|
||||||
} //end for (;;)
|
} //end for (;;)
|
||||||
|
|
||||||
if(horzEdge->NextInLML)
|
if (horzEdge->OutIdx >= 0 && !op1)
|
||||||
|
{
|
||||||
|
op1 = GetLastOutPt(horzEdge);
|
||||||
|
TEdge* eNextHorz = m_SortedEdges;
|
||||||
|
while (eNextHorz)
|
||||||
|
{
|
||||||
|
if (eNextHorz->OutIdx >= 0 &&
|
||||||
|
HorzSegmentsOverlap(horzEdge->Bot.X,
|
||||||
|
horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X))
|
||||||
|
{
|
||||||
|
OutPt* op2 = GetLastOutPt(eNextHorz);
|
||||||
|
AddJoin(op2, op1, eNextHorz->Top);
|
||||||
|
}
|
||||||
|
eNextHorz = eNextHorz->NextInSEL;
|
||||||
|
}
|
||||||
|
AddGhostJoin(op1, horzEdge->Top);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horzEdge->NextInLML)
|
||||||
{
|
{
|
||||||
if(horzEdge->OutIdx >= 0)
|
if(horzEdge->OutIdx >= 0)
|
||||||
{
|
{
|
||||||
OutPt* op1 = AddOutPt( horzEdge, horzEdge->Top);
|
op1 = AddOutPt( horzEdge, horzEdge->Top);
|
||||||
if (isTopOfScanbeam) AddGhostJoin(op1, horzEdge->Bot);
|
|
||||||
UpdateEdgeIntoAEL(horzEdge);
|
UpdateEdgeIntoAEL(horzEdge);
|
||||||
if (horzEdge->WindDelta == 0) return;
|
if (horzEdge->WindDelta == 0) return;
|
||||||
//nb: HorzEdge is no longer horizontal here
|
//nb: HorzEdge is no longer horizontal here
|
||||||
|
@ -2906,6 +2980,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
|
||||||
|
|
||||||
if(IsMaximaEdge)
|
if(IsMaximaEdge)
|
||||||
{
|
{
|
||||||
|
if (m_StrictSimple) m_Maxima.push_back(e->Top.X);
|
||||||
TEdge* ePrev = e->PrevInAEL;
|
TEdge* ePrev = e->PrevInAEL;
|
||||||
DoMaxima(e);
|
DoMaxima(e);
|
||||||
if( !ePrev ) e = m_ActiveEdges;
|
if( !ePrev ) e = m_ActiveEdges;
|
||||||
|
@ -2927,6 +3002,8 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
|
||||||
e->Curr.Y = topY;
|
e->Curr.Y = topY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//When StrictlySimple and 'e' is being touched by another edge, then
|
||||||
|
//make sure both edges have a vertex here ...
|
||||||
if (m_StrictSimple)
|
if (m_StrictSimple)
|
||||||
{
|
{
|
||||||
TEdge* ePrev = e->PrevInAEL;
|
TEdge* ePrev = e->PrevInAEL;
|
||||||
|
@ -2948,7 +3025,9 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
|
||||||
}
|
}
|
||||||
|
|
||||||
//3. Process horizontals at the Top of the scanbeam ...
|
//3. Process horizontals at the Top of the scanbeam ...
|
||||||
ProcessHorizontals(true);
|
m_Maxima.sort();
|
||||||
|
ProcessHorizontals();
|
||||||
|
m_Maxima.clear();
|
||||||
|
|
||||||
//4. Promote intermediate vertices ...
|
//4. Promote intermediate vertices ...
|
||||||
e = m_ActiveEdges;
|
e = m_ActiveEdges;
|
||||||
|
@ -2988,44 +3067,71 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY)
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void Clipper::FixupOutPolygon(OutRec &outrec)
|
void Clipper::FixupOutPolyline(OutRec &outrec)
|
||||||
{
|
{
|
||||||
//FixupOutPolygon() - removes duplicate points and simplifies consecutive
|
|
||||||
//parallel edges by removing the middle vertex.
|
|
||||||
OutPt *lastOK = 0;
|
|
||||||
outrec.BottomPt = 0;
|
|
||||||
OutPt *pp = outrec.Pts;
|
OutPt *pp = outrec.Pts;
|
||||||
|
OutPt *lastPP = pp->Prev;
|
||||||
for (;;)
|
while (pp != lastPP)
|
||||||
{
|
{
|
||||||
if (pp->Prev == pp || pp->Prev == pp->Next )
|
pp = pp->Next;
|
||||||
|
if (pp->Pt == pp->Prev->Pt)
|
||||||
{
|
{
|
||||||
DisposeOutPts(pp);
|
if (pp == lastPP) lastPP = pp->Prev;
|
||||||
outrec.Pts = 0;
|
OutPt *tmpPP = pp->Prev;
|
||||||
return;
|
tmpPP->Next = pp->Next;
|
||||||
}
|
pp->Next->Prev = tmpPP;
|
||||||
|
delete pp;
|
||||||
//test for duplicate points and collinear edges ...
|
pp = tmpPP;
|
||||||
if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
|
|
||||||
(SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
|
|
||||||
(!m_PreserveCollinear ||
|
|
||||||
!Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
|
|
||||||
{
|
|
||||||
lastOK = 0;
|
|
||||||
OutPt *tmp = pp;
|
|
||||||
pp->Prev->Next = pp->Next;
|
|
||||||
pp->Next->Prev = pp->Prev;
|
|
||||||
pp = pp->Prev;
|
|
||||||
delete tmp;
|
|
||||||
}
|
|
||||||
else if (pp == lastOK) break;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!lastOK) lastOK = pp;
|
|
||||||
pp = pp->Next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outrec.Pts = pp;
|
|
||||||
|
if (pp == pp->Prev)
|
||||||
|
{
|
||||||
|
DisposeOutPts(pp);
|
||||||
|
outrec.Pts = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void Clipper::FixupOutPolygon(OutRec &outrec)
|
||||||
|
{
|
||||||
|
//FixupOutPolygon() - removes duplicate points and simplifies consecutive
|
||||||
|
//parallel edges by removing the middle vertex.
|
||||||
|
OutPt *lastOK = 0;
|
||||||
|
outrec.BottomPt = 0;
|
||||||
|
OutPt *pp = outrec.Pts;
|
||||||
|
bool preserveCol = m_PreserveCollinear || m_StrictSimple;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (pp->Prev == pp || pp->Prev == pp->Next)
|
||||||
|
{
|
||||||
|
DisposeOutPts(pp);
|
||||||
|
outrec.Pts = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//test for duplicate points and collinear edges ...
|
||||||
|
if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) ||
|
||||||
|
(SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) &&
|
||||||
|
(!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt))))
|
||||||
|
{
|
||||||
|
lastOK = 0;
|
||||||
|
OutPt *tmp = pp;
|
||||||
|
pp->Prev->Next = pp->Next;
|
||||||
|
pp->Next->Prev = pp->Prev;
|
||||||
|
pp = pp->Prev;
|
||||||
|
delete tmp;
|
||||||
|
}
|
||||||
|
else if (pp == lastOK) break;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!lastOK) lastOK = pp;
|
||||||
|
pp = pp->Next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outrec.Pts = pp;
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -3309,7 +3415,7 @@ bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2)
|
||||||
OutPt *op2 = j->OutPt2, *op2b;
|
OutPt *op2 = j->OutPt2, *op2b;
|
||||||
|
|
||||||
//There are 3 kinds of joins for output polygons ...
|
//There are 3 kinds of joins for output polygons ...
|
||||||
//1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are a vertices anywhere
|
//1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere
|
||||||
//along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
|
//along (horizontal) collinear edges (& Join.OffPt is on the same horizontal).
|
||||||
//2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
|
//2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same
|
||||||
//location at the Bottom of the overlapping segment (& Join.OffPt is above).
|
//location at the Bottom of the overlapping segment (& Join.OffPt is above).
|
||||||
|
@ -3508,6 +3614,7 @@ void Clipper::JoinCommonEdges()
|
||||||
OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);
|
OutRec *outRec2 = GetOutRec(join->OutPt2->Idx);
|
||||||
|
|
||||||
if (!outRec1->Pts || !outRec2->Pts) continue;
|
if (!outRec1->Pts || !outRec2->Pts) continue;
|
||||||
|
if (outRec1->IsOpen || outRec2->IsOpen) continue;
|
||||||
|
|
||||||
//get the polygon fragment with the correct hole state (FirstLeft)
|
//get the polygon fragment with the correct hole state (FirstLeft)
|
||||||
//before calling JoinPoints() ...
|
//before calling JoinPoints() ...
|
||||||
|
@ -4355,7 +4462,7 @@ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool p
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void TranslatePath(const Path& input, Path& output, IntPoint delta)
|
void TranslatePath(const Path& input, Path& output, const IntPoint delta)
|
||||||
{
|
{
|
||||||
//precondition: input != output
|
//precondition: input != output
|
||||||
output.resize(input.size());
|
output.resize(input.size());
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* *
|
* *
|
||||||
* Author : Angus Johnson *
|
* Author : Angus Johnson *
|
||||||
* Version : 6.2.1 *
|
* Version : 6.2.9 *
|
||||||
* Date : 31 October 2014 *
|
* Date : 16 February 2015 *
|
||||||
* Website : http://www.angusj.com *
|
* Website : http://www.angusj.com *
|
||||||
* Copyright : Angus Johnson 2010-2014 *
|
* Copyright : Angus Johnson 2010-2015 *
|
||||||
* *
|
* *
|
||||||
* License: *
|
* License: *
|
||||||
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
* Use, modification & distribution is subject to Boost Software License Ver 1. *
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
#ifndef clipper_hpp
|
#ifndef clipper_hpp
|
||||||
#define clipper_hpp
|
#define clipper_hpp
|
||||||
|
|
||||||
#define CLIPPER_VERSION "6.2.0"
|
#define CLIPPER_VERSION "6.2.6"
|
||||||
|
|
||||||
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
|
//use_int32: When enabled 32bit ints are used instead of 64bit ints. This
|
||||||
//improve performance but coordinate values are limited to the range +/- 46340
|
//improve performance but coordinate values are limited to the range +/- 46340
|
||||||
|
@ -50,6 +50,7 @@
|
||||||
//#define use_deprecated
|
//#define use_deprecated
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -200,7 +201,6 @@ enum EdgeSide { esLeft = 1, esRight = 2};
|
||||||
struct TEdge;
|
struct TEdge;
|
||||||
struct IntersectNode;
|
struct IntersectNode;
|
||||||
struct LocalMinimum;
|
struct LocalMinimum;
|
||||||
struct Scanbeam;
|
|
||||||
struct OutPt;
|
struct OutPt;
|
||||||
struct OutRec;
|
struct OutRec;
|
||||||
struct Join;
|
struct Join;
|
||||||
|
@ -232,7 +232,6 @@ protected:
|
||||||
void PopLocalMinima();
|
void PopLocalMinima();
|
||||||
virtual void Reset();
|
virtual void Reset();
|
||||||
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
|
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
|
||||||
void DoMinimaLML(TEdge* E1, TEdge* E2, bool IsClosed);
|
|
||||||
TEdge* DescendToMin(TEdge *&E);
|
TEdge* DescendToMin(TEdge *&E);
|
||||||
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
|
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
|
||||||
|
|
||||||
|
@ -253,14 +252,20 @@ public:
|
||||||
Clipper(int initOptions = 0);
|
Clipper(int initOptions = 0);
|
||||||
~Clipper();
|
~Clipper();
|
||||||
bool Execute(ClipType clipType,
|
bool Execute(ClipType clipType,
|
||||||
Paths &solution,
|
Paths &solution,
|
||||||
PolyFillType subjFillType = pftEvenOdd,
|
PolyFillType fillType = pftEvenOdd);
|
||||||
PolyFillType clipFillType = pftEvenOdd);
|
|
||||||
bool Execute(ClipType clipType,
|
bool Execute(ClipType clipType,
|
||||||
PolyTree &polytree,
|
Paths &solution,
|
||||||
PolyFillType subjFillType = pftEvenOdd,
|
PolyFillType subjFillType,
|
||||||
PolyFillType clipFillType = pftEvenOdd);
|
PolyFillType clipFillType);
|
||||||
bool ReverseSolution() {return m_ReverseOutput;};
|
bool Execute(ClipType clipType,
|
||||||
|
PolyTree &polytree,
|
||||||
|
PolyFillType fillType = pftEvenOdd);
|
||||||
|
bool Execute(ClipType clipType,
|
||||||
|
PolyTree &polytree,
|
||||||
|
PolyFillType subjFillType,
|
||||||
|
PolyFillType clipFillType);
|
||||||
|
bool ReverseSolution() { return m_ReverseOutput; };
|
||||||
void ReverseSolution(bool value) {m_ReverseOutput = value;};
|
void ReverseSolution(bool value) {m_ReverseOutput = value;};
|
||||||
bool StrictlySimple() {return m_StrictSimple;};
|
bool StrictlySimple() {return m_StrictSimple;};
|
||||||
void StrictlySimple(bool value) {m_StrictSimple = value;};
|
void StrictlySimple(bool value) {m_StrictSimple = value;};
|
||||||
|
@ -272,13 +277,15 @@ protected:
|
||||||
void Reset();
|
void Reset();
|
||||||
virtual bool ExecuteInternal();
|
virtual bool ExecuteInternal();
|
||||||
private:
|
private:
|
||||||
PolyOutList m_PolyOuts;
|
PolyOutList m_PolyOuts;
|
||||||
JoinList m_Joins;
|
JoinList m_Joins;
|
||||||
JoinList m_GhostJoins;
|
JoinList m_GhostJoins;
|
||||||
IntersectList m_IntersectList;
|
IntersectList m_IntersectList;
|
||||||
ClipType m_ClipType;
|
ClipType m_ClipType;
|
||||||
typedef std::priority_queue<cInt> ScanbeamList;
|
typedef std::priority_queue<cInt> ScanbeamList;
|
||||||
ScanbeamList m_Scanbeam;
|
ScanbeamList m_Scanbeam;
|
||||||
|
typedef std::list<cInt> MaximaList;
|
||||||
|
MaximaList m_Maxima;
|
||||||
TEdge *m_ActiveEdges;
|
TEdge *m_ActiveEdges;
|
||||||
TEdge *m_SortedEdges;
|
TEdge *m_SortedEdges;
|
||||||
bool m_ExecuteLocked;
|
bool m_ExecuteLocked;
|
||||||
|
@ -307,8 +314,8 @@ private:
|
||||||
bool IsTopHorz(const cInt XPos);
|
bool IsTopHorz(const cInt XPos);
|
||||||
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
|
||||||
void DoMaxima(TEdge *e);
|
void DoMaxima(TEdge *e);
|
||||||
void ProcessHorizontals(bool IsTopOfScanbeam);
|
void ProcessHorizontals();
|
||||||
void ProcessHorizontal(TEdge *horzEdge, bool isTopOfScanbeam);
|
void ProcessHorizontal(TEdge *horzEdge);
|
||||||
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||||
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
|
||||||
OutRec* GetOutRec(int idx);
|
OutRec* GetOutRec(int idx);
|
||||||
|
@ -316,6 +323,7 @@ private:
|
||||||
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
|
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
|
||||||
OutRec* CreateOutRec();
|
OutRec* CreateOutRec();
|
||||||
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
|
||||||
|
OutPt* GetLastOutPt(TEdge *e);
|
||||||
void DisposeAllOutRecs();
|
void DisposeAllOutRecs();
|
||||||
void DisposeOutRec(PolyOutList::size_type index);
|
void DisposeOutRec(PolyOutList::size_type index);
|
||||||
bool ProcessIntersections(const cInt topY);
|
bool ProcessIntersections(const cInt topY);
|
||||||
|
@ -328,6 +336,7 @@ private:
|
||||||
void DisposeIntersectNodes();
|
void DisposeIntersectNodes();
|
||||||
bool FixupIntersectionOrder();
|
bool FixupIntersectionOrder();
|
||||||
void FixupOutPolygon(OutRec &outrec);
|
void FixupOutPolygon(OutRec &outrec);
|
||||||
|
void FixupOutPolyline(OutRec &outrec);
|
||||||
bool IsHole(TEdge *e);
|
bool IsHole(TEdge *e);
|
||||||
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
|
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
|
||||||
void FixHoleLinkage(OutRec &outrec);
|
void FixHoleLinkage(OutRec &outrec);
|
||||||
|
|
|
@ -37,14 +37,10 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle
|
||||||
Polygons grown;
|
Polygons grown;
|
||||||
offset((Polygons)this->expolygon, &grown, this->extrusion_width);
|
offset((Polygons)this->expolygon, &grown, this->extrusion_width);
|
||||||
|
|
||||||
// detect what edges lie on lower slices
|
// detect what edges lie on lower slices by turning bridge contour and holes
|
||||||
for (ExPolygons::const_iterator lower = this->lower_slices.expolygons.begin();
|
// into polylines and then clipping them with each lower slice's contour
|
||||||
lower != this->lower_slices.expolygons.end();
|
intersection(grown, this->lower_slices.contours(), &this->_edges);
|
||||||
++lower) {
|
|
||||||
/* turn bridge contour and holes into polylines and then clip them
|
|
||||||
with each lower slice's contour */
|
|
||||||
intersection(grown, lower->contour, &this->_edges);
|
|
||||||
}
|
|
||||||
#ifdef SLIC3R_DEBUG
|
#ifdef SLIC3R_DEBUG
|
||||||
printf(" bridge has %zu support(s)\n", this->_edges.size());
|
printf(" bridge has %zu support(s)\n", this->_edges.size());
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -112,6 +112,16 @@ ExPolygonCollection::lines() const
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Polygons
|
||||||
|
ExPolygonCollection::contours() const
|
||||||
|
{
|
||||||
|
Polygons contours;
|
||||||
|
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
|
||||||
|
contours.push_back(it->contour);
|
||||||
|
}
|
||||||
|
return contours;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection");
|
REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection");
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -30,6 +30,7 @@ class ExPolygonCollection
|
||||||
void simplify(double tolerance);
|
void simplify(double tolerance);
|
||||||
Polygon convex_hull() const;
|
Polygon convex_hull() const;
|
||||||
Lines lines() const;
|
Lines lines() const;
|
||||||
|
Polygons contours() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,15 @@ GCodeWriter::preamble()
|
||||||
return gcode.str();
|
return gcode.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
GCodeWriter::postamble()
|
||||||
|
{
|
||||||
|
std::ostringstream gcode;
|
||||||
|
if (FLAVOR_IS(gcfMachinekit))
|
||||||
|
gcode << "M2 ; end of program\n";
|
||||||
|
return gcode.str();
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool)
|
GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +92,7 @@ GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool)
|
||||||
|
|
||||||
std::ostringstream gcode;
|
std::ostringstream gcode;
|
||||||
gcode << code << " ";
|
gcode << code << " ";
|
||||||
if (FLAVOR_IS(gcfMach3)) {
|
if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) {
|
||||||
gcode << "P";
|
gcode << "P";
|
||||||
} else {
|
} else {
|
||||||
gcode << "S";
|
gcode << "S";
|
||||||
|
@ -118,7 +127,7 @@ GCodeWriter::set_bed_temperature(unsigned int temperature, bool wait)
|
||||||
|
|
||||||
std::ostringstream gcode;
|
std::ostringstream gcode;
|
||||||
gcode << code << " ";
|
gcode << code << " ";
|
||||||
if (FLAVOR_IS(gcfMach3)) {
|
if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) {
|
||||||
gcode << "P";
|
gcode << "P";
|
||||||
} else {
|
} else {
|
||||||
gcode << "S";
|
gcode << "S";
|
||||||
|
@ -153,7 +162,7 @@ GCodeWriter::set_fan(unsigned int speed, bool dont_save)
|
||||||
gcode << "M126";
|
gcode << "M126";
|
||||||
} else {
|
} else {
|
||||||
gcode << "M106 ";
|
gcode << "M106 ";
|
||||||
if (FLAVOR_IS(gcfMach3)) {
|
if (FLAVOR_IS(gcfMach3) || FLAVOR_IS(gcfMachinekit)) {
|
||||||
gcode << "P";
|
gcode << "P";
|
||||||
} else {
|
} else {
|
||||||
gcode << "S";
|
gcode << "S";
|
||||||
|
@ -434,7 +443,10 @@ GCodeWriter::_retract(double length, double restart_extra, const std::string &co
|
||||||
double dE = this->_extruder->retract(length, restart_extra);
|
double dE = this->_extruder->retract(length, restart_extra);
|
||||||
if (dE != 0) {
|
if (dE != 0) {
|
||||||
if (this->config.use_firmware_retraction) {
|
if (this->config.use_firmware_retraction) {
|
||||||
gcode << "G10 ; retract\n";
|
if (FLAVOR_IS(gcfMachinekit))
|
||||||
|
gcode << "G22 ; retract\n";
|
||||||
|
else
|
||||||
|
gcode << "G10 ; retract\n";
|
||||||
} else {
|
} else {
|
||||||
gcode << "G1 " << this->_extrusion_axis << E_NUM(this->_extruder->E)
|
gcode << "G1 " << this->_extrusion_axis << E_NUM(this->_extruder->E)
|
||||||
<< " F" << this->_extruder->retract_speed_mm_min;
|
<< " F" << this->_extruder->retract_speed_mm_min;
|
||||||
|
@ -460,7 +472,10 @@ GCodeWriter::unretract()
|
||||||
double dE = this->_extruder->unretract();
|
double dE = this->_extruder->unretract();
|
||||||
if (dE != 0) {
|
if (dE != 0) {
|
||||||
if (this->config.use_firmware_retraction) {
|
if (this->config.use_firmware_retraction) {
|
||||||
gcode << "G11 ; unretract\n";
|
if (FLAVOR_IS(gcfMachinekit))
|
||||||
|
gcode << "G23 ; unretract\n";
|
||||||
|
else
|
||||||
|
gcode << "G11 ; unretract\n";
|
||||||
gcode << this->reset_e();
|
gcode << this->reset_e();
|
||||||
} else {
|
} else {
|
||||||
// use G1 instead of G0 because G0 will blend the restart with the previous travel move
|
// use G1 instead of G0 because G0 will blend the restart with the previous travel move
|
||||||
|
@ -509,4 +524,4 @@ GCodeWriter::get_position() const
|
||||||
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
REGISTER_CLASS(GCodeWriter, "GCode::Writer");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ class GCodeWriter {
|
||||||
void apply_print_config(const PrintConfig &print_config);
|
void apply_print_config(const PrintConfig &print_config);
|
||||||
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
void set_extruders(const std::vector<unsigned int> &extruder_ids);
|
||||||
std::string preamble();
|
std::string preamble();
|
||||||
|
std::string postamble();
|
||||||
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1);
|
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1);
|
||||||
std::string set_bed_temperature(unsigned int temperature, bool wait = false);
|
std::string set_bed_temperature(unsigned int temperature, bool wait = false);
|
||||||
std::string set_fan(unsigned int speed, bool dont_save = false);
|
std::string set_fan(unsigned int speed, bool dont_save = false);
|
||||||
|
|
|
@ -61,13 +61,18 @@ LayerRegion::prepare_fill_surfaces()
|
||||||
the only meaningful information returned by psPerimeters. */
|
the only meaningful information returned by psPerimeters. */
|
||||||
|
|
||||||
// if no solid layers are requested, turn top/bottom surfaces to internal
|
// if no solid layers are requested, turn top/bottom surfaces to internal
|
||||||
if (this->_region->config.top_solid_layers == 0) {
|
if (this->region()->config.top_solid_layers == 0) {
|
||||||
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
|
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
|
||||||
if (surface->surface_type == stTop)
|
if (surface->surface_type == stTop) {
|
||||||
surface->surface_type = stInternal;
|
if (this->layer()->object()->config.infill_only_where_needed) {
|
||||||
|
surface->surface_type = stInternalVoid;
|
||||||
|
} else {
|
||||||
|
surface->surface_type = stInternal;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this->_region->config.bottom_solid_layers == 0) {
|
if (this->region()->config.bottom_solid_layers == 0) {
|
||||||
for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
|
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)
|
if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge)
|
||||||
surface->surface_type = stInternal;
|
surface->surface_type = stInternal;
|
||||||
|
@ -75,9 +80,9 @@ LayerRegion::prepare_fill_surfaces()
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn too small internal regions into solid regions according to the user setting
|
// turn too small internal regions into solid regions according to the user setting
|
||||||
if (this->_region->config.fill_density.value > 0) {
|
if (this->region()->config.fill_density.value > 0) {
|
||||||
// scaling an area requires two calls!
|
// scaling an area requires two calls!
|
||||||
double min_area = scale_(scale_(this->_region->config.solid_infill_below_area.value));
|
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) {
|
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)
|
if (surface->surface_type == stInternal && surface->area() <= min_area)
|
||||||
surface->surface_type = stInternalSolid;
|
surface->surface_type = stInternalSolid;
|
||||||
|
|
|
@ -166,13 +166,14 @@ Print::invalidate_state_by_config_options(const std::vector<t_config_option_key>
|
||||||
if (*opt_key == "skirts"
|
if (*opt_key == "skirts"
|
||||||
|| *opt_key == "skirt_height"
|
|| *opt_key == "skirt_height"
|
||||||
|| *opt_key == "skirt_distance"
|
|| *opt_key == "skirt_distance"
|
||||||
|| *opt_key == "min_skirt_length") {
|
|| *opt_key == "min_skirt_length"
|
||||||
|
|| *opt_key == "ooze_prevention") {
|
||||||
steps.insert(psSkirt);
|
steps.insert(psSkirt);
|
||||||
} else if (*opt_key == "brim_width") {
|
} else if (*opt_key == "brim_width") {
|
||||||
steps.insert(psBrim);
|
steps.insert(psBrim);
|
||||||
steps.insert(psSkirt);
|
steps.insert(psSkirt);
|
||||||
} else if (*opt_key == "nozzle_diameter") {
|
} else if (*opt_key == "nozzle_diameter") {
|
||||||
steps.insert(psInitExtruders);
|
osteps.insert(posSlice);
|
||||||
} else if (*opt_key == "avoid_crossing_perimeters"
|
} else if (*opt_key == "avoid_crossing_perimeters"
|
||||||
|| *opt_key == "bed_shape"
|
|| *opt_key == "bed_shape"
|
||||||
|| *opt_key == "bed_temperature"
|
|| *opt_key == "bed_temperature"
|
||||||
|
@ -266,11 +267,6 @@ Print::invalidate_step(PrintStep step)
|
||||||
// propagate to dependent steps
|
// propagate to dependent steps
|
||||||
if (step == psSkirt) {
|
if (step == psSkirt) {
|
||||||
this->invalidate_step(psBrim);
|
this->invalidate_step(psBrim);
|
||||||
} else if (step == psInitExtruders) {
|
|
||||||
FOREACH_OBJECT(this, object) {
|
|
||||||
(*object)->invalidate_step(posPerimeters);
|
|
||||||
(*object)->invalidate_step(posSupportMaterial);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return invalidated;
|
return invalidated;
|
||||||
|
@ -309,13 +305,22 @@ Print::extruders() const
|
||||||
std::set<size_t> extruders;
|
std::set<size_t> extruders;
|
||||||
|
|
||||||
FOREACH_REGION(this, region) {
|
FOREACH_REGION(this, region) {
|
||||||
extruders.insert((*region)->config.perimeter_extruder - 1);
|
// these checks reflect the same logic used in the GUI for enabling/disabling
|
||||||
extruders.insert((*region)->config.infill_extruder - 1);
|
// extruder selection fields
|
||||||
extruders.insert((*region)->config.solid_infill_extruder - 1);
|
if ((*region)->config.perimeters.value > 0 || this->config.brim_width.value > 0)
|
||||||
|
extruders.insert((*region)->config.perimeter_extruder - 1);
|
||||||
|
|
||||||
|
if ((*region)->config.fill_density.value > 0)
|
||||||
|
extruders.insert((*region)->config.infill_extruder - 1);
|
||||||
|
|
||||||
|
if ((*region)->config.top_solid_layers.value > 0 || (*region)->config.bottom_solid_layers.value > 0)
|
||||||
|
extruders.insert((*region)->config.solid_infill_extruder - 1);
|
||||||
}
|
}
|
||||||
FOREACH_OBJECT(this, object) {
|
FOREACH_OBJECT(this, object) {
|
||||||
extruders.insert((*object)->config.support_material_extruder - 1);
|
if ((*object)->has_support_material()) {
|
||||||
extruders.insert((*object)->config.support_material_interface_extruder - 1);
|
extruders.insert((*object)->config.support_material_extruder - 1);
|
||||||
|
extruders.insert((*object)->config.support_material_interface_extruder - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extruders;
|
return extruders;
|
||||||
|
@ -534,20 +539,16 @@ Print::apply_config(DynamicPrintConfig config)
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool Print::has_infinite_skirt() const
|
||||||
Print::init_extruders()
|
|
||||||
{
|
{
|
||||||
if (this->state.is_done(psInitExtruders)) return;
|
return (this->config.skirt_height == -1 && this->config.skirts > 0)
|
||||||
this->state.set_done(psInitExtruders);
|
|| (this->config.ooze_prevention && this->extruders().size() > 1);
|
||||||
|
}
|
||||||
// enforce tall skirt if using ooze_prevention
|
|
||||||
// FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings)
|
bool Print::has_skirt() const
|
||||||
if (this->config.ooze_prevention && this->extruders().size() > 1) {
|
{
|
||||||
this->config.skirt_height.value = -1;
|
return (this->config.skirt_height > 0 && this->config.skirts > 0)
|
||||||
if (this->config.skirts == 0) this->config.skirts.value = 1;
|
|| this->has_infinite_skirt();
|
||||||
}
|
|
||||||
|
|
||||||
this->state.set_done(psInitExtruders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -625,17 +626,37 @@ Print::validate() const
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::vector<double> layer_heights;
|
// find the smallest nozzle diameter
|
||||||
|
std::set<size_t> extruders = this->extruders();
|
||||||
|
if (extruders.empty())
|
||||||
|
throw PrintValidationException("The supplied settings will cause an empty print.");
|
||||||
|
|
||||||
|
std::set<double> nozzle_diameters;
|
||||||
|
for (std::set<size_t>::iterator it = extruders.begin(); it != extruders.end(); ++it)
|
||||||
|
nozzle_diameters.insert(this->config.nozzle_diameter.get_at(*it));
|
||||||
|
double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end());
|
||||||
|
|
||||||
FOREACH_OBJECT(this, i_object) {
|
FOREACH_OBJECT(this, i_object) {
|
||||||
PrintObject* object = *i_object;
|
PrintObject* object = *i_object;
|
||||||
layer_heights.push_back(object->config.layer_height);
|
|
||||||
layer_heights.push_back(object->config.get_abs_value("first_layer_height"));
|
// validate first_layer_height
|
||||||
}
|
double first_layer_height = object->config.get_abs_value("first_layer_height");
|
||||||
double max_layer_height = *std::max_element(layer_heights.begin(), layer_heights.end());
|
double first_layer_min_nozzle_diameter;
|
||||||
|
if (object->config.raft_layers > 0) {
|
||||||
std::set<size_t> extruders = this->extruders();
|
// if we have raft layers, only support material extruder is used on first layer
|
||||||
for (std::set<size_t>::iterator it = extruders.begin(); it != extruders.end(); ++it) {
|
size_t first_layer_extruder = object->config.raft_layers == 1
|
||||||
if (max_layer_height > this->config.nozzle_diameter.get_at(*it))
|
? object->config.support_material_interface_extruder-1
|
||||||
|
: object->config.support_material_extruder-1;
|
||||||
|
first_layer_min_nozzle_diameter = this->config.nozzle_diameter.get_at(first_layer_extruder);
|
||||||
|
} else {
|
||||||
|
// if we don't have raft layers, any nozzle diameter is potentially used in first layer
|
||||||
|
first_layer_min_nozzle_diameter = min_nozzle_diameter;
|
||||||
|
}
|
||||||
|
if (first_layer_height > first_layer_min_nozzle_diameter)
|
||||||
|
throw PrintValidationException("First layer height can't be greater than nozzle diameter");
|
||||||
|
|
||||||
|
// validate layer_height
|
||||||
|
if (object->config.layer_height.value > min_nozzle_diameter)
|
||||||
throw PrintValidationException("Layer height can't be greater than nozzle diameter");
|
throw PrintValidationException("Layer height can't be greater than nozzle diameter");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -682,13 +703,15 @@ Print::total_bounding_box() const
|
||||||
Flow brim_flow = this->brim_flow();
|
Flow brim_flow = this->brim_flow();
|
||||||
extra = std::max(extra, this->config.brim_width.value + brim_flow.width/2);
|
extra = std::max(extra, this->config.brim_width.value + brim_flow.width/2);
|
||||||
}
|
}
|
||||||
if (this->config.skirts.value > 0) {
|
if (this->has_skirt()) {
|
||||||
|
int skirts = this->config.skirts.value;
|
||||||
|
if (skirts == 0 && this->has_infinite_skirt()) skirts = 1;
|
||||||
Flow skirt_flow = this->skirt_flow();
|
Flow skirt_flow = this->skirt_flow();
|
||||||
extra = std::max(
|
extra = std::max(
|
||||||
extra,
|
extra,
|
||||||
this->config.brim_width.value
|
this->config.brim_width.value
|
||||||
+ this->config.skirt_distance.value
|
+ this->config.skirt_distance.value
|
||||||
+ this->config.skirts.value * skirt_flow.spacing()
|
+ skirts * skirt_flow.spacing()
|
||||||
+ skirt_flow.width/2
|
+ skirt_flow.width/2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -773,9 +796,7 @@ bool
|
||||||
Print::has_support_material() const
|
Print::has_support_material() const
|
||||||
{
|
{
|
||||||
FOREACH_OBJECT(this, object) {
|
FOREACH_OBJECT(this, object) {
|
||||||
PrintObjectConfig &config = (*object)->config;
|
if ((*object)->has_support_material()) return true;
|
||||||
if (config.support_material || config.raft_layers > 0 || config.support_material_enforce_layers > 0)
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ModelObject;
|
||||||
|
|
||||||
|
|
||||||
enum PrintStep {
|
enum PrintStep {
|
||||||
psInitExtruders, psSkirt, psBrim,
|
psSkirt, psBrim,
|
||||||
};
|
};
|
||||||
enum PrintObjectStep {
|
enum PrintObjectStep {
|
||||||
posSlice, posPerimeters, posPrepareInfill,
|
posSlice, posPerimeters, posPrepareInfill,
|
||||||
|
@ -134,6 +134,7 @@ class PrintObject
|
||||||
bool invalidate_step(PrintObjectStep step);
|
bool invalidate_step(PrintObjectStep step);
|
||||||
bool invalidate_all_steps();
|
bool invalidate_all_steps();
|
||||||
|
|
||||||
|
bool has_support_material() const;
|
||||||
void bridge_over_infill();
|
void bridge_over_infill();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -189,7 +190,8 @@ class Print
|
||||||
|
|
||||||
void add_model_object(ModelObject* model_object, int idx = -1);
|
void add_model_object(ModelObject* model_object, int idx = -1);
|
||||||
bool apply_config(DynamicPrintConfig config);
|
bool apply_config(DynamicPrintConfig config);
|
||||||
void init_extruders();
|
bool has_infinite_skirt() const;
|
||||||
|
bool has_skirt() const;
|
||||||
void validate() const;
|
void validate() const;
|
||||||
BoundingBox bounding_box() const;
|
BoundingBox bounding_box() const;
|
||||||
BoundingBox total_bounding_box() const;
|
BoundingBox total_bounding_box() const;
|
||||||
|
|
|
@ -380,12 +380,14 @@ PrintConfigDef::build_def() {
|
||||||
Options["gcode_flavor"].enum_values.push_back("makerware");
|
Options["gcode_flavor"].enum_values.push_back("makerware");
|
||||||
Options["gcode_flavor"].enum_values.push_back("sailfish");
|
Options["gcode_flavor"].enum_values.push_back("sailfish");
|
||||||
Options["gcode_flavor"].enum_values.push_back("mach3");
|
Options["gcode_flavor"].enum_values.push_back("mach3");
|
||||||
|
Options["gcode_flavor"].enum_values.push_back("machinekit");
|
||||||
Options["gcode_flavor"].enum_values.push_back("no-extrusion");
|
Options["gcode_flavor"].enum_values.push_back("no-extrusion");
|
||||||
Options["gcode_flavor"].enum_labels.push_back("RepRap (Marlin/Sprinter/Repetier)");
|
Options["gcode_flavor"].enum_labels.push_back("RepRap (Marlin/Sprinter/Repetier)");
|
||||||
Options["gcode_flavor"].enum_labels.push_back("Teacup");
|
Options["gcode_flavor"].enum_labels.push_back("Teacup");
|
||||||
Options["gcode_flavor"].enum_labels.push_back("MakerWare (MakerBot)");
|
Options["gcode_flavor"].enum_labels.push_back("MakerWare (MakerBot)");
|
||||||
Options["gcode_flavor"].enum_labels.push_back("Sailfish (MakerBot)");
|
Options["gcode_flavor"].enum_labels.push_back("Sailfish (MakerBot)");
|
||||||
Options["gcode_flavor"].enum_labels.push_back("Mach3/LinuxCNC");
|
Options["gcode_flavor"].enum_labels.push_back("Mach3/LinuxCNC");
|
||||||
|
Options["gcode_flavor"].enum_labels.push_back("Machinekit");
|
||||||
Options["gcode_flavor"].enum_labels.push_back("No extrusion");
|
Options["gcode_flavor"].enum_labels.push_back("No extrusion");
|
||||||
|
|
||||||
Options["infill_acceleration"].type = coFloat;
|
Options["infill_acceleration"].type = coFloat;
|
||||||
|
@ -426,7 +428,7 @@ PrintConfigDef::build_def() {
|
||||||
Options["infill_only_where_needed"].type = coBool;
|
Options["infill_only_where_needed"].type = coBool;
|
||||||
Options["infill_only_where_needed"].label = "Only infill where needed";
|
Options["infill_only_where_needed"].label = "Only infill where needed";
|
||||||
Options["infill_only_where_needed"].category = "Infill";
|
Options["infill_only_where_needed"].category = "Infill";
|
||||||
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). If enabled, slows down the G-code generation due to the multiple checks involved.";
|
||||||
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"].type = coFloatOrPercent;
|
||||||
|
@ -1028,6 +1030,40 @@ PrintConfigDef::build_def() {
|
||||||
|
|
||||||
t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def();
|
t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def();
|
||||||
|
|
||||||
|
void
|
||||||
|
DynamicPrintConfig::normalize() {
|
||||||
|
if (this->has("extruder")) {
|
||||||
|
int extruder = this->option("extruder")->getInt();
|
||||||
|
this->erase("extruder");
|
||||||
|
if (extruder != 0) {
|
||||||
|
if (!this->has("infill_extruder"))
|
||||||
|
this->option("infill_extruder", true)->setInt(extruder);
|
||||||
|
if (!this->has("perimeter_extruder"))
|
||||||
|
this->option("perimeter_extruder", true)->setInt(extruder);
|
||||||
|
if (!this->has("support_material_extruder"))
|
||||||
|
this->option("support_material_extruder", true)->setInt(extruder);
|
||||||
|
if (!this->has("support_material_interface_extruder"))
|
||||||
|
this->option("support_material_interface_extruder", true)->setInt(extruder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->has("solid_infill_extruder") && this->has("infill_extruder"))
|
||||||
|
this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt());
|
||||||
|
|
||||||
|
if (this->has("spiral_vase") && this->opt<ConfigOptionBool>("spiral_vase", true)->value) {
|
||||||
|
{
|
||||||
|
// this should be actually done only on the spiral layers instead of all
|
||||||
|
ConfigOptionBools* opt = this->opt<ConfigOptionBools>("retract_layer_change", true);
|
||||||
|
opt->values.assign(opt->values.size(), false); // set all values to false
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this->opt<ConfigOptionInt>("perimeters", true)->value = 1;
|
||||||
|
this->opt<ConfigOptionInt>("top_solid_layers", true)->value = 0;
|
||||||
|
this->opt<ConfigOptionPercent>("fill_density", true)->value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SLIC3RXS
|
#ifdef SLIC3RXS
|
||||||
REGISTER_CLASS(DynamicPrintConfig, "Config");
|
REGISTER_CLASS(DynamicPrintConfig, "Config");
|
||||||
REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject");
|
REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
enum GCodeFlavor {
|
enum GCodeFlavor {
|
||||||
gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfNoExtrusion,
|
gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum InfillPattern {
|
enum InfillPattern {
|
||||||
|
@ -29,6 +29,7 @@ template<> inline t_config_enum_values ConfigOptionEnum<GCodeFlavor>::get_enum_v
|
||||||
keys_map["makerware"] = gcfMakerWare;
|
keys_map["makerware"] = gcfMakerWare;
|
||||||
keys_map["sailfish"] = gcfSailfish;
|
keys_map["sailfish"] = gcfSailfish;
|
||||||
keys_map["mach3"] = gcfMach3;
|
keys_map["mach3"] = gcfMach3;
|
||||||
|
keys_map["machinekit"] = gcfMachinekit;
|
||||||
keys_map["no-extrusion"] = gcfNoExtrusion;
|
keys_map["no-extrusion"] = gcfNoExtrusion;
|
||||||
return keys_map;
|
return keys_map;
|
||||||
}
|
}
|
||||||
|
@ -78,38 +79,7 @@ class DynamicPrintConfig : public DynamicConfig
|
||||||
this->def = &PrintConfigDef::def;
|
this->def = &PrintConfigDef::def;
|
||||||
};
|
};
|
||||||
|
|
||||||
void normalize() {
|
void normalize();
|
||||||
if (this->has("extruder")) {
|
|
||||||
int extruder = this->option("extruder")->getInt();
|
|
||||||
this->erase("extruder");
|
|
||||||
if (extruder != 0) {
|
|
||||||
if (!this->has("infill_extruder"))
|
|
||||||
this->option("infill_extruder", true)->setInt(extruder);
|
|
||||||
if (!this->has("perimeter_extruder"))
|
|
||||||
this->option("perimeter_extruder", true)->setInt(extruder);
|
|
||||||
if (!this->has("support_material_extruder"))
|
|
||||||
this->option("support_material_extruder", true)->setInt(extruder);
|
|
||||||
if (!this->has("support_material_interface_extruder"))
|
|
||||||
this->option("support_material_interface_extruder", true)->setInt(extruder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->has("solid_infill_extruder") && this->has("infill_extruder"))
|
|
||||||
this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt());
|
|
||||||
|
|
||||||
if (this->has("spiral_vase") && this->opt<ConfigOptionBool>("spiral_vase", true)->value) {
|
|
||||||
{
|
|
||||||
// this should be actually done only on the spiral layers instead of all
|
|
||||||
ConfigOptionBools* opt = this->opt<ConfigOptionBools>("retract_layer_change", true);
|
|
||||||
opt->values.assign(opt->values.size(), false); // set all values to false
|
|
||||||
}
|
|
||||||
{
|
|
||||||
this->opt<ConfigOptionInt>("perimeters", true)->value = 1;
|
|
||||||
this->opt<ConfigOptionInt>("top_solid_layers", true)->value = 0;
|
|
||||||
this->opt<ConfigOptionPercent>("fill_density", true)->value = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class StaticPrintConfig : public virtual StaticConfig
|
class StaticPrintConfig : public virtual StaticConfig
|
||||||
|
@ -410,7 +380,7 @@ class GCodeConfig : public virtual StaticPrintConfig
|
||||||
|
|
||||||
std::string get_extrusion_axis() const
|
std::string get_extrusion_axis() const
|
||||||
{
|
{
|
||||||
if (this->gcode_flavor.value == gcfMach3) {
|
if ((this->gcode_flavor.value == gcfMach3) || (this->gcode_flavor.value == gcfMachinekit)) {
|
||||||
return "A";
|
return "A";
|
||||||
} else if (this->gcode_flavor.value == gcfNoExtrusion) {
|
} else if (this->gcode_flavor.value == gcfNoExtrusion) {
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -333,6 +333,14 @@ PrintObject::invalidate_all_steps()
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PrintObject::has_support_material() const
|
||||||
|
{
|
||||||
|
return this->config.support_material
|
||||||
|
|| this->config.raft_layers > 0
|
||||||
|
|| this->config.support_material_enforce_layers > 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PrintObject::bridge_over_infill()
|
PrintObject::bridge_over_infill()
|
||||||
{
|
{
|
||||||
|
@ -340,7 +348,7 @@ PrintObject::bridge_over_infill()
|
||||||
size_t region_id = region - this->_print->regions.begin();
|
size_t region_id = region - this->_print->regions.begin();
|
||||||
|
|
||||||
double fill_density = (*region)->config.fill_density.value;
|
double fill_density = (*region)->config.fill_density.value;
|
||||||
if (fill_density == 100 || fill_density == 0) continue;
|
if (fill_density == 100) continue;
|
||||||
|
|
||||||
FOREACH_LAYER(this, layer_it) {
|
FOREACH_LAYER(this, layer_it) {
|
||||||
if (layer_it == this->layers.begin()) continue;
|
if (layer_it == this->layers.begin()) continue;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#define SLIC3R_VERSION "1.2.6-dev"
|
#define SLIC3R_VERSION "1.2.7-dev"
|
||||||
|
|
||||||
#define EPSILON 1e-4
|
#define EPSILON 1e-4
|
||||||
#define SCALING_FACTOR 0.000001
|
#define SCALING_FACTOR 0.000001
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
|
use List::Util qw(sum);
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 19;
|
use Test::More tests => 23;
|
||||||
|
|
||||||
my $square = Slic3r::Polygon->new( # ccw
|
my $square = Slic3r::Polygon->new( # ccw
|
||||||
[200, 100],
|
[200, 100],
|
||||||
|
@ -178,8 +179,8 @@ if (0) { # Clipper does not preserve polyline orientation
|
||||||
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
|
# Clipper bug #122
|
||||||
my $subject = [
|
my $subject = [
|
||||||
Slic3r::Polyline->new([1975,1975],[25,1975],[25,25],[1975,25],[1975,1975]),
|
Slic3r::Polyline->new([1975,1975],[25,1975],[25,25],[1975,25],[1975,1975]),
|
||||||
];
|
];
|
||||||
|
@ -188,9 +189,51 @@ if (0) {
|
||||||
Slic3r::Polygon->new([525,525],[525,1475],[1475,1475],[1475,525]),
|
Slic3r::Polygon->new([525,525],[525,1475],[1475,1475],[1475,525]),
|
||||||
];
|
];
|
||||||
my $result = Slic3r::Geometry::Clipper::intersection_pl($subject, $clip);
|
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), 1, 'intersection_pl - result is not empty';
|
||||||
is scalar(@{$result->[0]}), 5, 'intersection_pl - result is not empty';
|
is scalar(@{$result->[0]}), 5, 'intersection_pl - result is not empty';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
# Clipper bug #126
|
||||||
|
my $subject = Slic3r::Polyline->new(
|
||||||
|
[200000,19799999],[200000,200000],[24304692,200000],[15102879,17506106],[13883200,19799999],[200000,19799999],
|
||||||
|
);
|
||||||
|
my $clip = [
|
||||||
|
Slic3r::Polygon->new([15257205,18493894],[14350057,20200000],[-200000,20200000],[-200000,-200000],[25196917,-200000]),
|
||||||
|
];
|
||||||
|
my $result = Slic3r::Geometry::Clipper::intersection_pl([$subject], $clip);
|
||||||
|
is scalar(@$result), 1, 'intersection_pl - result is not empty';
|
||||||
|
is $result->[0]->length, $subject->length, 'intersection_pl - result has same length as subject polyline';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
# Disabled until Clipper bug #127 is fixed
|
||||||
|
my $subject = [
|
||||||
|
Slic3r::Polyline->new([-90000000,-100000000],[-90000000,100000000]), # vertical
|
||||||
|
Slic3r::Polyline->new([-100000000,-10000000],[100000000,-10000000]), # horizontal
|
||||||
|
Slic3r::Polyline->new([-100000000,0],[100000000,0]), # horizontal
|
||||||
|
Slic3r::Polyline->new([-100000000,10000000],[100000000,10000000]), # horizontal
|
||||||
|
];
|
||||||
|
my $clip = Slic3r::Polygon->new( # a circular, convex, polygon
|
||||||
|
[99452190,10452846],[97814760,20791169],[95105652,30901699],[91354546,40673664],[86602540,50000000],
|
||||||
|
[80901699,58778525],[74314483,66913061],[66913061,74314483],[58778525,80901699],[50000000,86602540],
|
||||||
|
[40673664,91354546],[30901699,95105652],[20791169,97814760],[10452846,99452190],[0,100000000],
|
||||||
|
[-10452846,99452190],[-20791169,97814760],[-30901699,95105652],[-40673664,91354546],
|
||||||
|
[-50000000,86602540],[-58778525,80901699],[-66913061,74314483],[-74314483,66913061],
|
||||||
|
[-80901699,58778525],[-86602540,50000000],[-91354546,40673664],[-95105652,30901699],
|
||||||
|
[-97814760,20791169],[-99452190,10452846],[-100000000,0],[-99452190,-10452846],
|
||||||
|
[-97814760,-20791169],[-95105652,-30901699],[-91354546,-40673664],[-86602540,-50000000],
|
||||||
|
[-80901699,-58778525],[-74314483,-66913061],[-66913061,-74314483],[-58778525,-80901699],
|
||||||
|
[-50000000,-86602540],[-40673664,-91354546],[-30901699,-95105652],[-20791169,-97814760],
|
||||||
|
[-10452846,-99452190],[0,-100000000],[10452846,-99452190],[20791169,-97814760],
|
||||||
|
[30901699,-95105652],[40673664,-91354546],[50000000,-86602540],[58778525,-80901699],
|
||||||
|
[66913061,-74314483],[74314483,-66913061],[80901699,-58778525],[86602540,-50000000],
|
||||||
|
[91354546,-40673664],[95105652,-30901699],[97814760,-20791169],[99452190,-10452846],[100000000,0]
|
||||||
|
);
|
||||||
|
my $result = Slic3r::Geometry::Clipper::intersection_pl($subject, [$clip]);
|
||||||
|
is scalar(@$result), scalar(@$subject), 'intersection_pl - expected number of polylines';
|
||||||
|
is sum(map scalar(@$_), @$result), scalar(@$subject)*2,
|
||||||
|
'intersection_pl - expected number of points in polylines';
|
||||||
|
}
|
||||||
|
|
||||||
__END__
|
__END__
|
||||||
|
|
|
@ -4,7 +4,7 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
use Slic3r::XS;
|
use Slic3r::XS;
|
||||||
use Test::More tests => 108;
|
use Test::More tests => 110;
|
||||||
|
|
||||||
foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
|
foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
|
||||||
$config->set('layer_height', 0.3);
|
$config->set('layer_height', 0.3);
|
||||||
|
@ -51,6 +51,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) {
|
||||||
is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum';
|
is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum';
|
||||||
$config->set_deserialize('gcode_flavor', 'mach3');
|
$config->set_deserialize('gcode_flavor', 'mach3');
|
||||||
is $config->get('gcode_flavor'), 'mach3', 'deserialize enum (gcode_flavor)';
|
is $config->get('gcode_flavor'), 'mach3', 'deserialize enum (gcode_flavor)';
|
||||||
|
$config->set_deserialize('gcode_flavor', 'machinekit');
|
||||||
|
is $config->get('gcode_flavor'), 'machinekit', 'deserialize enum (gcode_flavor)';
|
||||||
|
|
||||||
$config->set_deserialize('fill_pattern', 'line');
|
$config->set_deserialize('fill_pattern', 'line');
|
||||||
is $config->get('fill_pattern'), 'line', 'deserialize enum (fill_pattern)';
|
is $config->get('fill_pattern'), 'line', 'deserialize enum (fill_pattern)';
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
%code{% THIS->apply_print_config(*print_config); %};
|
%code{% THIS->apply_print_config(*print_config); %};
|
||||||
void set_extruders(std::vector<unsigned int> extruder_ids);
|
void set_extruders(std::vector<unsigned int> extruder_ids);
|
||||||
std::string preamble();
|
std::string preamble();
|
||||||
|
std::string postamble();
|
||||||
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1);
|
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1);
|
||||||
std::string set_bed_temperature(unsigned int temperature, bool wait = false);
|
std::string set_bed_temperature(unsigned int temperature, bool wait = false);
|
||||||
std::string set_fan(unsigned int speed, bool dont_save = false);
|
std::string set_fan(unsigned int speed, bool dont_save = false);
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
IV
|
IV
|
||||||
_constant()
|
_constant()
|
||||||
ALIAS:
|
ALIAS:
|
||||||
STEP_INIT_EXTRUDERS = psInitExtruders
|
|
||||||
STEP_SLICE = posSlice
|
STEP_SLICE = posSlice
|
||||||
STEP_PERIMETERS = posPerimeters
|
STEP_PERIMETERS = posPerimeters
|
||||||
STEP_PREPARE_INFILL = posPrepareInfill
|
STEP_PREPARE_INFILL = posPrepareInfill
|
||||||
|
@ -199,7 +198,8 @@ _constant()
|
||||||
void add_model_object(ModelObject* model_object, int idx = -1);
|
void add_model_object(ModelObject* model_object, int idx = -1);
|
||||||
bool apply_config(DynamicPrintConfig* config)
|
bool apply_config(DynamicPrintConfig* config)
|
||||||
%code%{ RETVAL = THIS->apply_config(*config); %};
|
%code%{ RETVAL = THIS->apply_config(*config); %};
|
||||||
void init_extruders();
|
bool has_infinite_skirt();
|
||||||
|
bool has_skirt();
|
||||||
void validate()
|
void validate()
|
||||||
%code%{
|
%code%{
|
||||||
try {
|
try {
|
||||||
|
|
Loading…
Add table
Reference in a new issue