Merge branch 'master' into adaptive-slicing

This commit is contained in:
Florens Wasserfall 2017-04-19 09:29:23 +02:00
commit 57826c9262
83 changed files with 1596 additions and 608 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@ xs/assertlib*
local-lib
package/osx/Slic3r*.app
*.dmg
*.swp
*.swo

View File

@ -1,35 +1,50 @@
language: perl
install: export LDLOADLIBS=-lstdc++
script: perl ./Build.PL
perl:
- "5.14"
- "5.18"
- "5.20"
before_install:
- sh package/linux/travis-decrypt-key
install:
- export LDLOADLIBS=-lstdc++
- export BOOST_DIR=$HOME/boost_1_63_0
- export SLIC3R_STATIC=1
- export CXX=g++-4.9
- export CC=g++-4.9
- source $HOME/perl5/perlbrew/etc/bashrc
script:
- bash package/linux/travis-setup.sh
- perlbrew switch slic3r-perl
- perl ./Build.PL
after_success:
- eval $(perl -Mlocal::lib=$TRAVIS_BUILD_DIR/local-lib)
- LD_LIBRARY_PATH=$WXDIR/lib package/linux/make_archive.sh linux-x64
- package/deploy/sftp.sh linux ~/slic3r-upload.rsa *.bz2
branches:
only:
- master
- xsgui
sudo: false
cache:
apt: true
directories:
- local-lib
- "$HOME/cache"
addons:
apt:
sources:
- boost-latest
- ubuntu-toolchain-r-test
packages:
- libboost-thread1.55-dev
- libboost-system1.55-dev
- libboost-filesystem1.55-dev
- liblocal-lib-perl
- g++-4.9
env: CC=g++-4.9
- gcc-4.9
- libgtk2.0-0
- libgtk2.0-dev
ssh_known_hosts: dl.slic3r.org
notifications:
irc:
channels:
- "chat.freenode.net#slic3r"
- chat.freenode.net#slic3r
on_success: change
on_failure: always
use_notice: true
sudo: required
dist: trusty
env:
matrix:
global:
- secure: eEVRZNMv7FM6jrOU9iAFkDhWxFQ1WtHBEaObImcvtFUxy6vWSt3ehFFeTRouj3uHQAnbvUzziDyvPPm8/95alv5g/du8ML6YzzqKBKfazM0xQ7SF6R2DQL8lfFIp+RSV7T02byEP1f1g7Zva7xH9szIlDcSfU0pXW4KWbkBFMd8=
- secure: gj338h+qHGccTD/VQFmEJkqdg2McIe2pO0iZ4Ae9BvY5vxkIML4BpoYZQXQTqiAOETnUjlcknY9lx0hI/PfkDD9MSJc5BC/3fMYRCu3SgAclEwklWf9vvtodUeT69mtnZuw1zze1nTbExuOw2mepbqFjxKKMl+9l5oCz4O54fXU=

View File

@ -85,7 +85,7 @@ The main author of Slic3r is Alessandro Ranellucci (@alexrj, *Sound* in IRC, [@a
Joseph Lenox (@lordofhyphens, *Loh* in IRC) is the current co-maintainer.
Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. Original manual by Gary Hodgson. Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James.
Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. Original manual by Gary Hodgson. Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James.
### How can I invoke Slic3r using the command line?

45
appveyor.yml Normal file
View File

@ -0,0 +1,45 @@
version: 1.3.0-{branch}-{build}
image: WMF 5
init:
- ps:
environment:
LDLOADLIBS: -lstdc++
SLIC3R_STATIC: 1
SLIC3R_VERSION: 1.3.0
BOOST_DIR: C:\dev\boost_1_63_0
WXSHARED: SHARED=0
FORCE_WX_BUILD: 0
FORCE_BOOST_REINSTALL: 0
ENC_SECRET:
secure: QfeTOSKXz1uFCEACqFKLNw==
UPLOAD_USER:
secure: fYPwnI3p6HNR+eMRJR3JfmyNolFn+Uc0MUn2bBXp9uU=
install:
- IF DEFINED ENC_SECRET nuget install secure-file -ExcludeVersion
- IF DEFINED ENC_SECRET secure-file\tools\secure-file -decrypt package/deploy/slic3r-upload.ppk.enc -secret %ENC_SECRET%
- ps: "& package/win/appveyor_preinstall.ps1"
cache:
- C:\Users\appveyor\boost.1.63.0.7z
- C:\Users\appveyor\local-lib.7z
- C:\Strawberry\perl\site
- C:\Users\appveyor\freeglut.7z
- C:\users\appveyor\strawberry.msi
- C:\Users\appveyor\winscp.zip
- C:\Users\appveyor\extra_perl.7z
- C:\Users\appveyor\wxwidgets.7z
build_script:
- ps: "& package/win/appveyor_buildscript.ps1"
test_script:
- ps: "mkdir C:\\Andrés\nwget \"http://www.thingiverse.com/download:73351\" -o\"C:\\Andrés\\5mm.stl\"\necho \"bed_temperature=60\" > C:\\Andrés\\test.ini\n\ncd C:\\projects\\slic3r\nperl slic3r.pl --load \"C:\\Andrés\\test.ini\" \"C:\\Andrés\\5mm.stl\"\n\nif (!(Test-Path \"C:\\Andrés\\5mm.gcode\")) {\necho \"IS IT HERE\"\n}"
artifacts:
- path: .\slic3r*zip
name: slic3r-dev
deploy_script:
- ps: "& package/win/appveyor_deploy.ps1"
on_success:
- ps:
on_failure:
- ps:
on_finish:
- ps:

View File

@ -103,11 +103,14 @@ sub spawn_thread {
my $parent_tid = threads->tid;
lock @threads;
# Set up a default handler for preventing crashes in case signals are received before
# thread sets its handlers.
$SIG{'STOP'} = sub {};
@_ = ();
my $thread = threads->create(sub {
@my_threads = ();
Slic3r::debugf "Starting thread %d (parent: %d)...\n", threads->tid, $parent_tid;
local $SIG{'KILL'} = sub {
Slic3r::debugf "Exiting thread %d...\n", threads->tid;
$parallel_sema->up if $parallel_sema;
@ -119,6 +122,7 @@ sub spawn_thread {
$pause_sema->down;
$pause_sema->up;
};
Slic3r::debugf "Starting thread %d (parent: %d)...\n", threads->tid, $parent_tid;
$cb->();
});
push @my_threads, $thread->tid;

View File

@ -269,6 +269,13 @@ sub validate {
qw(perimeter infill solid_infill top_infill support_material first_layer);
}
# support material
if ($self->support_material) {
die "Value of 0 is illegal. Use some % value instead (e.g. 150%) for auto.\n"
if $self->support_material_threshold =~ /^0+/;
}
# general validation, quick and dirty
foreach my $opt_key (@{$self->get_keys}) {
my $opt = $Options->{$opt_key};

View File

@ -30,13 +30,6 @@ sub offset_ex {
return Slic3r::Geometry::Clipper::offset_ex(\@$self, @_);
}
sub noncollapsing_offset_ex {
my $self = shift;
my ($distance, @params) = @_;
return $self->offset_ex($distance + 1, @params);
}
sub bounding_box {
my $self = shift;
return $self->contour->bounding_box;

View File

@ -3,6 +3,19 @@ use strict;
use warnings;
use utf8;
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
:filedialog :font);
BEGIN {
# Wrap the Wx::_load_plugin() function which doesn't work with non-ASCII paths
no warnings 'redefine';
my $orig = *Wx::_load_plugin{CODE};
*Wx::_load_plugin = sub {
$_[0] = Slic3r::decode_path($_[0]);
$orig->(@_);
};
}
use File::Basename qw(basename);
use FindBin;
use List::Util qw(first any);
@ -42,8 +55,6 @@ use Slic3r::GUI::SLAPrintOptions;
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
our $have_LWP = eval "use LWP::UserAgent; 1";
use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow
:filedialog :font);
use Wx::Event qw(EVT_IDLE EVT_COMMAND);
use base 'Wx::App';

View File

@ -47,11 +47,11 @@ sub new {
'<html>' .
'<body bgcolor="#ffffff" link="#808080">' .
'<font color="#808080">' .
'Copyright &copy; 2011-2016 Alessandro Ranellucci. <br />' .
'Copyright &copy; 2011-2017 Alessandro Ranellucci. <br />' .
'<a href="http://slic3r.org/">Slic3r</a> is licensed under the ' .
'<a href="http://www.gnu.org/licenses/agpl-3.0.html">GNU Affero General Public License, version 3</a>.' .
'<br /><br /><br />' .
'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. ' .
'Contributions by Henrik Brix Andersen, Vojtech Bubnik, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Kliment Yanev and numerous others. ' .
'Manual by Gary Hodgson. Inspired by the RepRap community. <br />' .
'Slic3r logo designed by Corey Daniels, <a href="http://www.famfamfam.com/lab/icons/silk/">Silk Icon Set</a> designed by Mark James. ' .
'</font>' .

View File

@ -99,9 +99,8 @@ sub _init_tabpanel {
});
$panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater");
if (!$self->{no_controller}) {
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller");
}
$panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller")
unless ($Slic3r::GUI::Settings->{_}{no_controller});
}
sub _init_menubar {
@ -259,7 +258,7 @@ sub _init_menubar {
}, undef, 'application_view_tile.png');
$self->_append_menu_item($windowMenu, "&Controller\tCtrl+Y", 'Show the printer controller', sub {
$self->select_tab(1);
}, undef, 'printer_empty.png') if !$self->{no_controller};
}, undef, 'printer_empty.png') unless ($Slic3r::GUI::Settings->{_}{no_controller});
$self->_append_menu_item($windowMenu, "DLP Projector…\tCtrl+P", 'Open projector window for DLP printing', sub {
$self->{plater}->pause_background_process;
Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal;

View File

@ -22,9 +22,7 @@ sub get_value {
sub set_tooltip {
my ($self, $tooltip) = @_;
$self->SetToolTipString($tooltip)
if $tooltip && $self->can('SetToolTipString');
die "Method not implemented";
}
sub toggle {
@ -67,8 +65,14 @@ sub _default_size {
sub _trigger_wxWindow {
my ($self) = @_;
$self->wxWindow->SetToolTipString($self->option->tooltip)
if $self->option->tooltip && $self->wxWindow->can('SetToolTipString');
$self->set_tooltip($self->option->tooltip);
}
sub set_tooltip {
my ($self, $tooltip) = @_;
$self->wxWindow->SetToolTipString($tooltip)
if $self->wxWindow->can('SetToolTipString');
}
sub set_value {

View File

@ -1079,6 +1079,23 @@ sub set_number_of_copies {
}
}
sub center_selected_object_on_bed {
my ($self) = @_;
my ($obj_idx, $object) = $self->selected_object;
return if !defined $obj_idx;
my $model_object = $self->{model}->objects->[$obj_idx];
my $bb = $model_object->bounding_box;
my $size = $bb->size;
my $vector = Slic3r::Pointf->new(
$self->bed_centerf->x - $bb->x_min - $size->x/2,
$self->bed_centerf->y - $bb->y_min - $size->y/2, #//
);
$_->offset->translate(@$vector) for @{$model_object->instances};
$self->refresh_canvases;
}
sub rotate {
my $self = shift;
my ($angle, $axis) = @_;
@ -1388,12 +1405,12 @@ sub async_apply_config {
$self->{preview3D}->reload_print if $self->{preview3D};
$self->{AdaptiveLayersDialog}->reload_preview if $self->{AdaptiveLayersDialog};
if ($invalidated) {
if (!$Slic3r::GUI::Settings->{_}{background_processing}) {
$self->hide_preview;
$self->hide_preview if $invalidated;
return;
}
if ($invalidated) {
# kill current thread if any
$self->stop_background_process;
# remove the sliced statistics box because something changed.
@ -2331,6 +2348,9 @@ sub object_menu {
$self->set_number_of_copies;
}, undef, 'textfield.png');
$menu->AppendSeparator();
$frame->_append_menu_item($menu, "Move to bed center", 'Center object around bed center', sub {
$self->center_selected_object_on_bed;
}, undef, 'arrow_in.png');
$frame->_append_menu_item($menu, "Rotate 45° clockwise", 'Rotate the selected object by 45° clockwise', sub {
$self->rotate(-45);
}, undef, 'arrow_rotate_clockwise.png');
@ -2578,7 +2598,7 @@ sub new {
EVT_BUTTON($self, wxID_OK, sub {
wxTheApp->save_settings;
$self->EndModal(wxID_OK);
$self->Close; # needed on Linux
$self->Destroy;
});
$self->SetSizer($sizer);

View File

@ -9,7 +9,9 @@ use utf8;
use File::Basename qw(basename);
use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL
wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED);
use List::Util qw(max);
use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED EVT_TREE_ITEM_RIGHT_CLICK);
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad rad2deg);
use base 'Wx::Panel';
use constant ICON_OBJECT => 0;
@ -156,6 +158,42 @@ sub new {
return if $self->{disable_tree_sel_changed_event};
$self->selection_changed;
});
EVT_TREE_ITEM_RIGHT_CLICK($self, $tree, sub {
my ($self, $event) = @_;
my $item = $event->GetItem;
my $frame = $self->GetFrame;
my $menu = Wx::Menu->new;
my $scaleToSizeMenu = Wx::Menu->new;
my $scaleToSizeMenuItem = $menu->AppendSubMenu($scaleToSizeMenu, "Scale to size", 'Scale the selected object along a single axis');
wxTheApp->set_menu_item_icon($scaleToSizeMenuItem, 'arrow_out.png');
$frame->_append_menu_item($scaleToSizeMenu, "Uniformly… ", 'Scale the selected object along the XYZ axes', sub {
$self->changescale(undef, 1);
});
$frame->_append_menu_item($scaleToSizeMenu, "Along X axis…", 'Scale the selected object along the X axis', sub {
$self->changescale(X, 1);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Y axis…", 'Scale the selected object along the Y axis', sub {
$self->changescale(Y, 1);
}, undef, 'bullet_green.png');
$frame->_append_menu_item($scaleToSizeMenu, "Along Z axis…", 'Scale the selected object along the Z axis', sub {
$self->changescale(Z, 1);
}, undef, 'bullet_blue.png');
my $rotateMenu = Wx::Menu->new;
my $rotateMenuItem = $menu->AppendSubMenu($rotateMenu, "Rotate", 'Rotate the selected object by an arbitrary angle');
wxTheApp->set_menu_item_icon($rotateMenuItem, 'textfield.png');
$frame->_append_menu_item($rotateMenu, "Around X axis…", 'Rotate the selected object by an arbitrary angle around X axis', sub {
$self->rotate(undef, X);
}, undef, 'bullet_red.png');
$frame->_append_menu_item($rotateMenu, "Around Y axis…", 'Rotate the selected object by an arbitrary angle around Y axis', sub {
$self->rotate(undef, Y);
}, undef, 'bullet_green.png');
$frame->_append_menu_item($rotateMenu, "Around Z axis…", 'Rotate the selected object by an arbitrary angle around Z axis', sub {
$self->rotate(undef, Z);
}, undef, 'bullet_blue.png');
$frame->PopupMenu($menu, $event->GetPoint);
});
EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) });
EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) });
EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) });
@ -450,4 +488,82 @@ sub _update {
$self->{canvas}->Render;
}
sub changescale {
my ($self, $axis, $tosize) = @_;
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
my $object_size = $volume->bounding_box->size;
if (defined $axis) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
my $scale;
if (defined $tosize) {
my $cursize = $object_size->[$axis];
# Wx::GetNumberFromUser() does not support decimal numbers
my $newsize = Wx::GetTextFromUser(
sprintf("Enter the new size for the selected mesh:"),
"Scale along $axis_name",
$cursize, $self);
return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0;
$scale = $newsize / $cursize * 100;
} else {
# Wx::GetNumberFromUser() does not support decimal numbers
$scale = Wx::GetTextFromUser("Enter the scale % for the selected object:",
"Scale along $axis_name", 100, $self);
$scale =~ s/%$//;
return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0;
}
my $versor = [1,1,1];
$versor->[$axis] = $scale/100;
$volume->mesh->scale_xyz(Slic3r::Pointf3->new(@$versor));
} else {
my $scale;
if ($tosize) {
my $cursize = max(@$object_size);
# Wx::GetNumberFromUser() does not support decimal numbers
my $newsize = Wx::GetTextFromUser("Enter the new max size for the selected object:",
"Scale", $cursize, $self);
return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0;
$scale = $newsize / $cursize;
} else {
# max scale factor should be above 2540 to allow importing files exported in inches
# Wx::GetNumberFromUser() does not support decimal numbers
$scale = Wx::GetTextFromUser("Enter the scale % for the selected object:", 'Scale',
100, $self);
return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0;
}
return if !$scale || $scale < 0;
$volume->mesh->scale($scale);
}
$self->_parts_changed;
}
}
sub rotate {
my $self = shift;
my ($angle, $axis) = @_;
# angle is in degrees
my $itemData = $self->get_selection;
if ($itemData && $itemData->{type} eq 'volume') {
my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}];
if (!defined $angle) {
my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z';
my $default = $axis == Z ? 0 : 0;
# Wx::GetNumberFromUser() does not support decimal numbers
$angle = Wx::GetTextFromUser("Enter the rotation angle:", "Rotate around $axis_name axis",
$default, $self);
return if !$angle || $angle !~ /^-?\d*(?:\.\d*)?$/ || $angle == -1;
}
if ($axis == X) { $volume->mesh->rotate_x(deg2rad($angle)); }
if ($axis == Y) { $volume->mesh->rotate_y(deg2rad($angle)); }
if ($axis == Z) { $volume->mesh->rotate_z(deg2rad($angle)); }
$self->_parts_changed;
}
}
sub GetFrame {
my ($self) = @_;
return &Wx::GetTopLevelParent($self);
}
1;

View File

@ -7,8 +7,8 @@ use strict;
use warnings;
use utf8;
use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL);
use Wx::Event qw(EVT_BUTTON);
use Wx qw(:dialog :id :misc :sizer :systemsettings :notebook wxTAB_TRAVERSAL wxTheApp);
use Wx::Event qw(EVT_BUTTON EVT_MENU);
use base 'Wx::Dialog';
sub new {
@ -58,6 +58,17 @@ sub PartSettingsChanged {
my ($self) = @_;
return $self->{parts}->PartSettingsChanged || $self->{layers}->LayersChanged;
}
sub _append_menu_item {
my ($self, $menu, $string, $description, $cb, $id, $icon, $kind) = @_;
$id //= &Wx::NewId();
my $item = $menu->Append($id, $string, $description, $kind);
wxTheApp->set_menu_item_icon($item, $icon);
EVT_MENU($self, $id, $cb);
return $item;
}
package Slic3r::GUI::Plater::ObjectDialog::BaseTab;
use base 'Wx::Panel';

View File

@ -1177,11 +1177,13 @@ sub options {
octoprint_host octoprint_apikey
use_firmware_retraction pressure_advance vibration_limit
use_volumetric_e
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode
start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode between_objects_gcode
notes
nozzle_diameter extruder_offset
retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe
retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below
printer_settings_id
printer_notes
);
}
@ -1377,10 +1379,31 @@ sub build {
$option->height(150);
$optgroup->append_single_option_line($option);
}
{
my $optgroup = $page->new_optgroup('Between objects G-code (for sequential printing)',
label_width => 0,
);
my $option = $optgroup->get_option('between_objects_gcode');
$option->full_width(1);
$option->height(150);
$optgroup->append_single_option_line($option);
}
}
$self->{extruder_pages} = [];
$self->_build_extruder_pages;
{
my $page = $self->add_options_page('Notes', 'note.png');
{
my $optgroup = $page->new_optgroup('Notes',
label_width => 0,
);
my $option = $optgroup->get_option('notes');
$option->full_width(1);
$option->height(250);
$optgroup->append_single_option_line($option);
}
}
$self->_update_serial_ports unless $Slic3r::GUI::Settings->{_}{no_controller};
}

View File

@ -12,6 +12,10 @@ sub new {
$self->config(Slic3r::Config::SLAPrint->new);
$self->config->apply_dynamic(wxTheApp->{mainframe}->{plater}->config);
# Set some defaults
$self->config->set('infill_extrusion_width', 0.5) if $self->config->infill_extrusion_width == 0;
$self->config->set('perimeter_extrusion_width', 1) if $self->config->perimeter_extrusion_width == 0;
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
my $new_optgroup = sub {
my ($title) = @_;

View File

@ -80,22 +80,56 @@ sub export {
my $layer_height = $first_object->config->layer_height;
for my $region_id (0..$#{$self->print->regions}) {
my $region = $self->print->regions->[$region_id];
printf $fh "; external perimeters extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; perimeters extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; infill extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; solid infill extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; top infill extrusion width = %.2fmm\n",
$region->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object)->width;
printf $fh "; support material extrusion width = %.2fmm\n",
$self->objects->[0]->support_material_flow->width
if $self->print->has_support_material;
printf $fh "; first layer extrusion width = %.2fmm\n",
{
my $flow = $region->flow(FLOW_ROLE_EXTERNAL_PERIMETER, $layer_height, 0, 0, -1, $first_object);
my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('external_perimeter_speed');
$vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0;
printf $fh "; external perimeters extrusion width = %.2fmm (%.2fmm^3/s)\n",
$flow->width, $vol_speed;
}
{
my $flow = $region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 0, -1, $first_object);
my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('perimeter_speed');
$vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0;
printf $fh "; perimeters extrusion width = %.2fmm (%.2fmm^3/s)\n",
$flow->width, $vol_speed;
}
{
my $flow = $region->flow(FLOW_ROLE_INFILL, $layer_height, 0, 0, -1, $first_object);
my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('infill_speed');
$vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0;
printf $fh "; infill extrusion width = %.2fmm (%.2fmm^3/s)\n",
$flow->width, $vol_speed;
}
{
my $flow = $region->flow(FLOW_ROLE_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object);
my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('solid_infill_speed');
$vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0;
printf $fh "; solid infill extrusion width = %.2fmm (%.2fmm^3/s)\n",
$flow->width, $vol_speed;
}
{
my $flow = $region->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height, 0, 0, -1, $first_object);
my $vol_speed = $flow->mm3_per_mm * $region->config->get_abs_value('top_solid_infill_speed');
$vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0;
printf $fh "; top infill extrusion width = %.2fmm (%.2fmm^3/s)\n",
$flow->width, $vol_speed;
}
if ($self->print->has_support_material) {
my $object0 = $self->objects->[0];
my $flow = $object0->support_material_flow;
my $vol_speed = $flow->mm3_per_mm * $object0->config->get_abs_value('support_material_speed');
$vol_speed = min($vol_speed, $self->config->max_volumetric_speed) if $self->config->max_volumetric_speed > 0;
printf $fh "; support material extrusion width = %.2fmm (%.2fmm^3/s)\n",
$flow->width, $vol_speed;
}
printf $fh "; first layer extrusion width = %.2fmm (%.2fmm^3/s)\n",
$region->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1, -1, $self->objects->[0])->width
if $region->config->first_layer_extrusion_width;
print $fh "\n";
}
@ -112,12 +146,14 @@ sub export {
}
# set extruder(s) temperature before and after start G-code
$self->_print_first_layer_temperature(0);
$self->_print_first_layer_temperature(0)
if $self->config->start_gcode !~ /M(?:109|104)/i;
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->start_gcode);
foreach my $start_gcode (@{ $self->config->start_filament_gcode }) { # process filament gcode in order
printf $fh "%s\n", $gcodegen->placeholder_parser->process($start_gcode);
}
$self->_print_first_layer_temperature(1);
$self->_print_first_layer_temperature(1)
if $self->config->start_gcode !~ /M(?:109|104)/i;
# set other general things
print $fh $gcodegen->preamble;
@ -212,8 +248,12 @@ sub export {
# is triggered, so machine has more time to reach such temperatures
if ($layer->id == 0 && $finished_objects > 0) {
printf $fh $gcodegen->writer->set_bed_temperature($self->config->first_layer_bed_temperature),
if $self->config->first_layer_bed_temperature && $self->config->has_heatbed;
$self->_print_first_layer_temperature(0);
if $self->config->first_layer_bed_temperature
&& $self->config->has_heatbed
&& $self->config->between_objects_gcode !~ /M(?:190|140)/i;
$self->_print_first_layer_temperature(0)
if $self->config->between_objects_gcode !~ /M(?:109|104)/i;
printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->between_objects_gcode);
}
$self->process_layer($layer, [$copy]);
}
@ -302,7 +342,6 @@ sub export {
sub _print_first_layer_temperature {
my ($self, $wait) = @_;
return if $self->config->start_gcode =~ /M(?:109|104)/i;
for my $t (@{$self->print->extruders}) {
my $temp = $self->config->get_at('first_layer_temperature', $t);
$temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention;

View File

@ -155,12 +155,20 @@ sub detect_surfaces_type {
sub prepare_infill {
my ($self) = @_;
return if $self->step_done(STEP_PREPARE_INFILL);
# This prepare_infill() is not really idempotent.
# TODO: It should clear and regenerate fill_surfaces at every run
# instead of modifying it in place.
$self->invalidate_step(STEP_PERIMETERS);
$self->make_perimeters;
# Do this after invalidating STEP_PERIMETERS because that would re-invalidate STEP_PREPARE_INFILL
$self->set_step_started(STEP_PREPARE_INFILL);
# prerequisites
$self->make_perimeters; # do we need them? TODO: check
$self->detect_surfaces_type;
return if $self->step_done(STEP_PREPARE_INFILL);
$self->set_step_started(STEP_PREPARE_INFILL);
$self->print->status_cb->(30, "Preparing infill");
# decide what surfaces are to be filled

View File

@ -8,7 +8,7 @@ use Slic3r::ExtrusionPath ':roles';
use Slic3r::Flow ':roles';
use Slic3r::Geometry qw(epsilon scale scaled_epsilon PI rad2deg deg2rad convex_hull);
use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2
intersection_pl offset2_ex diff_pl);
intersection_pl offset2_ex diff_pl diff_ex);
use Slic3r::Surface ':types';
has 'print_config' => (is => 'rw', required => 1);
@ -96,7 +96,7 @@ sub contact_area {
# if user specified a custom angle threshold, convert it to radians
my $threshold_rad;
if (!$self->object_config->support_material_threshold =~ /%$/) {
if (!($self->object_config->support_material_threshold =~ /%$/)) {
$threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive
Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad);
}
@ -722,7 +722,7 @@ sub generate_toolpaths {
# interface and contact infill
if (@$interface || @$contact_infill) {
$fillers{interface}->set_angle($interface_angle);
$fillers{interface}->set_angle(deg2rad($interface_angle));
$fillers{interface}->set_min_spacing($_interface_flow->spacing);
# find centerline of the external loop
@ -772,7 +772,7 @@ sub generate_toolpaths {
# support or flange
if (@$base) {
my $filler = $fillers{support};
$filler->set_angle($angles[ ($layer_id) % @angles ]);
$filler->set_angle(deg2rad($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.
@ -782,20 +782,31 @@ sub generate_toolpaths {
my $base_flow = $_flow;
# find centerline of the external loop/extrusions
my $to_infill = offset2_ex($base, +scaled_epsilon, -(scaled_epsilon + $_flow->scaled_width/2));
my $to_infill = offset2($base, +scaled_epsilon, -(scaled_epsilon + $_flow->scaled_width/2));
my @paths = ();
# base flange
if ($layer_id == 0) {
$filler = $fillers{interface};
$filler->set_angle($self->object_config->support_material_angle + 90);
$filler->set_angle(deg2rad($self->object_config->support_material_angle + 90));
$density = 0.5;
$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->set_min_spacing($base_flow->spacing);
# subtract brim so that it goes around the object fully (and support gets its own brim)
if ($self->print_config->brim_width > 0) {
my $d = +scale $self->print_config->brim_width*2;
$to_infill = diff_ex(
$to_infill,
offset($object->get_layer(0)->slices->polygons, $d),
);
} else {
$to_infill = union_ex($to_infill);
}
} else {
# draw a perimeter all around support infill
# TODO: use brim ordering algorithm
@ -806,10 +817,10 @@ sub generate_toolpaths {
mm3_per_mm => $mm3_per_mm,
width => $_flow->width,
height => $layer->height,
), map @$_, @$to_infill;
), @$to_infill;
# TODO: use offset2_ex()
$to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing);
$to_infill = offset_ex($to_infill, -$_flow->scaled_spacing);
}
my $mm3_per_mm = $base_flow->mm3_per_mm;

View File

@ -1,26 +1,23 @@
#include <EXTERN.h> // from the Perl distribution
#include <perl.h> // from the Perl distribution
#ifdef WIN32
// Perl win32 specific includes, found in perl\\lib\\CORE\\win32.h
// Defines the windows specific convenience RunPerl() function,
// which is not available on other operating systems.
#include <win32.h>
// the standard Windows. include
//#include <Windows.h>
#include <wchar.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <wchar.h>
#ifdef WIN32
int main(int argc, char **argv, char **env)
{
// replaces following Windows batch file: @"%~dp0\perl5.24.0.exe"
// "%~dp0\slic3r.pl" --DataDir "C:\Users\Public\Documents\Prusa3D\Slic3r
// settings MK2"%*
// If the Slic3r is installed in a localized directory (containing non-iso
// characters), spaces or semicolons, use short file names.
char exe_path[MAX_PATH] = {0};
char script_path[MAX_PATH];
char gui_flag[6] = {"--gui"};
@ -95,4 +92,25 @@ int main(int argc, char **argv, char **env)
#endif
free(command_line);
}
#else
int main(int argc, char **argv, char **env)
{
PerlInterpreter *my_perl = perl_alloc();
if (my_perl == NULL) {
fprintf(stderr, "Cannot start perl interpreter. Exiting.\n");
return -1;
}
perl_construct(my_perl);
#ifdef FORCE_GUI
char* command_line[] = { "slic3r", "slic3r.pl", "--gui" };
#else
char* command_line[] = { "slic3r", "slic3r.pl" };
#endif
perl_parse(my_perl, NULL, 3, command_line, (char **)NULL);
perl_run(my_perl);
perl_destruct(my_perl);
perl_free(my_perl);
}
#endif

92
package/common/util.sh Normal file
View File

@ -0,0 +1,92 @@
#!/bin/bash
# must be run from the root
function set_version ()
{
SLIC3R_VERSION=$(grep "VERSION" xs/src/libslic3r/libslic3r.h | awk -F\" '{print $2}')
}
# Cache the SHA1 for this build commit.
function get_commit () {
if [ ! -z ${TRAVIS_COMMIT+x} ]; then
# Travis sets the sha1 in TRAVIS_COMMIT
COMMIT_SHA1=$(git rev-parse --short $TRAVIS_COMMIT)
else
# should be able to get it properly
COMMIT_SHA1=$(git rev-parse --short HEAD)
fi
}
function set_build_id ()
{
echo "Setting SLIC3R_BUILD_ID"
if [ $(git describe &>/dev/null) ]; then
SLIC3R_BUILD_ID=$(git describe)
TAGGED=true
else
SLIC3R_BUILD_ID=${SLIC3R_VERSION}-${COMMIT_SHA1}
fi
}
function set_branch ()
{
echo "Setting current_branch"
if [ -z ${TRAVIS_BRANCH} ] && [ -z ${GIT_BRANCH+x} ] && [ -z ${APPVEYOR_REPO_BRANCH+x} ]; then
current_branch=$(git symbolic-ref HEAD | sed 's!refs\/heads\/!!')
else
current_branch="unknown"
if [ ! -z ${GIT_BRANCH+x} ]; then
echo "Setting to GIT_BRANCH"
current_branch=$(echo $GIT_BRANCH | cut -d / -f 2)
fi
if [ ! -z ${APPVEYOR_REPO_BRANCH+x} ]; then
echo "Setting to APPVEYOR_REPO_BRANCH"
current_branch=$APPVEYOR_REPO_BRANCH
fi
if [ ! -z ${TRAVIS_BRANCH} ]; then
if [ "${TRAVIS_BRANCH}" != "false" ]; then
echo "Setting to TRAVIS_BRANCH"
current_branch=$TRAVIS_BRANCH
fi
fi
fi
if [ -z ${current_branch+x} ]; then
current_branch="unknown"
fi
}
function set_app_name ()
{
set_branch
if [ "$current_branch" == "master" ]; then
appname=Slic3r
else
appname=Slic3r-${current_branch}
fi
}
function set_pr_id ()
{
echo "Setting PR_ID if available."
if [ ! -z ${GITHUB_PR_NUMBER+x} ]; then
PR_ID=$GITHUB_PR_NUMBER
fi
if [ ! -z ${APPVEYOR_PULL_REQUEST_NUMBER+x} ]; then
PR_ID=$APPVEYOR_PULL_REQUEST_NUMBER
fi
if [ ! -z ${TRAVIS_PULL_REQUEST_BRANCH+x} ] && [ "${TRAVIS_PULL_REQUEST}" != "false" ] ; then
PR_ID=$TRAVIS_PULL_REQUEST
fi
if [ ! -z ${PR_ID+x} ]; then
echo "Setting PR_ID to $PR_ID."
else
echo "PR_ID remains unset."
fi
}
function install_par ()
{
cpanm -q PAR::Packer
cpanm -q Wx::Perl::Packager
}

View File

@ -5,39 +5,12 @@
# BINTRAY_API_USER - Bintray username.
# Run this from the repository root (required to get slic3r version)
source $(dirname $0)/common/util.sh
SLIC3R_VERSION=$(grep "VERSION" xs/src/libslic3r/libslic3r.h | awk -F\" '{print $2}')
if [ $(git describe &>/dev/null) ]; then
SLIC3R_BUILD_ID=$(git describe)
TAGGED=true
else
SLIC3R_BUILD_ID=${SLIC3R_VERSION}-$(git rev-parse --short HEAD)
fi
if [ -z ${GIT_BRANCH+x} ] && [ -z ${APPVEYOR_REPO_BRANCH+x} ]; then
current_branch=$(git symbolic-ref HEAD | sed 's!refs\/heads\/!!')
else
current_branch="unknown"
if [ ! -z ${GIT_BRANCH+x} ]; then
echo "Setting to GIT_BRANCH"
current_branch=$(echo $GIT_BRANCH | cut -d / -f 2)
fi
if [ ! -z ${APPVEYOR_REPO_BRANCH+x} ]; then
echo "Setting to APPVEYOR_REPO_BRANCH"
current_branch=$APPVEYOR_REPO_BRANCH
fi
fi
if [ -z ${current_branch+x} ]; then
current_branch="unknown"
fi
if [ ! -z ${GITHUB_PR_NUMBER+x} ]; then
PR_ID=$GITHUB_PR_NUMBER
fi
if [ ! -z ${APPVEYOR_PULL_REQUEST_NUMBER+x} ]; then
PR_ID=$APPVEYOR_PULL_REQUEST_NUMBER
fi
get_commit
set_build_id
set_branch
set_pr_id
if [ "$current_branch" == "master" ] && [ -z ${PR_ID} ]; then
# If building master, goes in slic3r_dev or slic3r, depending on whether or not this is a tagged build
if [ -z ${TAGGED+x} ]; then

26
package/deploy/sftp.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
# Prerequisites
# Environment Variables:
# UPLOAD_USER - user to upload to sftp server
# KEY is assumed to be path to a ssh key for UPLOAD_USER
DIR=$1
shift
KEY=$1
shift
FILES=$*
source $(dirname $0)/../common/util.sh
set_pr_id
set_branch
if [ ! -z ${PR_ID+x} ] || [ $current_branch != "master" ]; then
DIR=${DIR}/branches
fi
if [ -s $KEY ]; then
for i in $FILES; do
filepath=$(readlink -f "$i")
echo put $filepath | sftp -i$KEY "${UPLOAD_USER}@dl.slic3r.org:$DIR/"
done
else
echo "$KEY is not available, not deploying."
fi

Binary file not shown.

Binary file not shown.

19
package/deploy/winscp.ps1 Executable file
View File

@ -0,0 +1,19 @@
# Prerequisites
# Environment Variables:
# UPLOAD_USER - user to upload to sftp server
# KEY is assumed to be path to a ssh key for UPLOAD_USER
Param(
[string]$DIR,
[string]$KEY,
[string]$FILE
)
Set-Variable -Name "UUSER" -Value "$env:UPLOAD_USER"
Set-Variable -Name "UPLOAD" -Value "$($FILE | Resolve-Path)"
if (Test-Path $KEY) {
if ($env:APPVEYOR_PULL_REQUEST_NUMBER -Or $env:APPVEYOR_REPO_BRANCH -ne "master" ) {
winscp.com /privatekey=$KEY /command "open sftp://$UUSER@dl.slic3r.org/$DIR/branches -hostkey=*" "put $UPLOAD ./$FILE" "exit"
} else {
winscp.com /privatekey=$KEY /command "open sftp://$UUSER@dl.slic3r.org/$DIR -hostkey=*" "put $UPLOAD ./$FILE" "exit"
}
}

View File

@ -0,0 +1,45 @@
#!/bin/bash
WD=$(dirname $0)
source $(dirname $0)/../common/util.sh
set_version
set_app_name
LOWERAPP=${appname,,}
wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./functions.sh
. ./functions.sh
srcfolder="$WD/${appname}"
if [ ! -e $srcfolder ]; then
echo "Run make_archive first."
exit 1
fi
cd $srcfolder
# make shell exec and call it Slic3r
mkdir -p /usr/{lib,bin}
mv -R Slic3r local-lib var perl-local slic3r.pl /usr/bin
mv -R bin/* /usr/lib
rm -rf bin
get_apprun
# Copy desktop and icon file to application root dir for Apprun to pick them up.
sed -e "s|SLIC3R_VERSION|$SLIC3R_VERSION|" -e"s|APPLICATION_NAME|$appname|" ../slic3r.desktop.in > ../slic3r.desktop
cp ../slic3r.desktop $LOWERAPP.desktop
cp ./var/Slic3r_192px_transparent.png ./slic3r.png
# archive directory has everything we need.
delete_blacklisted
get_desktopintegration $LOWERAPP
GLIBC_NEEDED=$(glibc_needed)
VERSION=git$GIT_REV-glibc$GLIBC_NEEDED
cd ..
mkdir -p out
generate_appimage
transfer ../out/*

View File

@ -0,0 +1,31 @@
src=../common/shell.cpp
# override with environment variable
CXX ?= g++
# Path to perl header files
INCLUDEDIR ?= ${HOME}/perl5/perlbrew/perls/slic3r-perl/lib/5.24.0/x86_64-linux-thread-multi/CORE
# path to library files for perl
LIBDIR ?= ${HOME}/perl5/perlbrew/perls/slic3r-perl/lib/5.24.0/x86_64-linux-thread-multi/CORE
LIBS += -lperl -lpthread -lcrypt
CXXFLAGS += -std=c++11 -static-libgcc -static-libstdc++ -I${INCLUDEDIR}
LDFLAGS += -L${LIBDIR}
.PHONY: all clean
all: Slic3r Slic3r-console
Slic3r: slic3r.o
${CXX} ${LDFLAGS} -o $@ $< ${LIBS}
Slic3r-console: slic3r-console.o
${CXX} ${LDFLAGS} -o $@ $< ${LIBS}
slic3r-console.o: ${src}
${CXX} -c ${CXXFLAGS} -o $@ $<
slic3r.o: ${src}
${CXX} -c -DFORCE_GUI ${CXXFLAGS} -o $@ $<
clean:
rm *.o Slic3r*

View File

@ -0,0 +1,28 @@
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_baseu-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_adv-3.0.so.0
/home/travis/builds/alexrj/Slic3r/local-lib/lib/perl5/x86_64-linux-thread-multi/Alien/wxWidgets/gtk_3_0_2_uni/lib/libwx_gtk2u_core-3.0.so.0
/lib/x86_64-linux-gnu/liblzma.so.5
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libpcre.so.3
/lib/x86_64-linux-gnu/libpng12.so.0
/lib/x86_64-linux-gnu/libresolv.so.2
/lib/x86_64-linux-gnu/libuuid.so.1
/usr/lib/x86_64-linux-gnu/libSM.so.6
/usr/lib/x86_64-linux-gnu/libatk-1.0.so.0
/usr/lib/x86_64-linux-gnu/libdatrie.so.1
/usr/lib/x86_64-linux-gnu/libfreetype.so.6
/usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
/usr/lib/x86_64-linux-gnu/libgraphite2.so.3
/usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0
/usr/lib/x86_64-linux-gnu/libharfbuzz.so.0
/usr/lib/x86_64-linux-gnu/libjbig.so.0
/usr/lib/x86_64-linux-gnu/libjpeg.so.8
/usr/lib/x86_64-linux-gnu/libpango-1.0.so.0
/usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so.0
/usr/lib/x86_64-linux-gnu/libpangoft2-1.0.so.0
/usr/lib/x86_64-linux-gnu/libpixman-1.so.0
/usr/lib/x86_64-linux-gnu/libtiff.so.5

131
package/linux/make_archive.sh Executable file
View File

@ -0,0 +1,131 @@
#!/bin/bash
# Assembles an installation archive from a built copy of Slic3r.
# Requires PAR::Packer to be installed for the version of
# perl copied.
# Adapted from script written by bubnikv for Prusa3D.
# Run from slic3r repo root directory.
if [ "$#" -ne 1 ]; then
echo "Usage: $(basename $0) arch_name"
exit 1;
fi
libdirs=$(find ./local-lib -iname *.so -exec dirname {} \; | sort -u | paste -sd ";" -)
WD=./$(dirname $0)
source $(dirname $0)/../common/util.sh
# Determine if this is a tagged (release) commit.
# Change the build id accordingly.
set_version
get_commit
set_build_id
set_branch
set_app_name
set_pr_id
install_par
# If we're on a branch, add the branch name to the app name.
if [ "$current_branch" == "master" ]; then
if [ ! -z ${PR_ID+x} ]; then
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-PR${PR_ID}.tar.bz2
else
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}.tar.bz2
fi
else
dmgfile=slic3r-${SLIC3R_BUILD_ID}-${1}-${current_branch}.tar.bz2
fi
rm -rf $WD/_tmp
mkdir -p $WD/_tmp
# Set the application folder infomation.
appfolder="$WD/${appname}"
archivefolder=$appfolder
resourcefolder=$appfolder
echo "Appfolder: $appfolder, archivefolder: $archivefolder"
# Our slic3r dir and location of perl
PERL_BIN=$(which perl)
PP_BIN=$(which pp)
SLIC3R_DIR="./"
if [[ -d "${appfolder}" ]]; then
echo "Deleting old working folder: ${appfolder}"
rm -rf ${appfolder}
fi
if [[ -e "${dmgfile}" ]]; then
echo "Deleting old archive: ${dmgfile}"
rm -rf ${dmgfile}
fi
echo "Creating new app folder: $appfolder"
mkdir -p $appfolder
echo "Copying resources..."
cp -rf $SLIC3R_DIR/var $resourcefolder/
echo "Copying Slic3r..."
cp $SLIC3R_DIR/slic3r.pl $archivefolder/slic3r.pl
cp -fRP $SLIC3R_DIR/local-lib $archivefolder/local-lib
cp -fRP $SLIC3R_DIR/lib/* $archivefolder/local-lib/lib/perl5/
mkdir $archivefolder/bin
echo "Installing libraries to $archivefolder/bin ..."
if [ -z ${WXDIR+x} ]; then
for bundle in $(find $archivefolder/local-lib/lib/perl5 -name '*.so' | grep "Wx") $(find $archivefolder/local-lib/lib/perl5 -name '*.so' -type f | grep "wxWidgets"); do
echo "$(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}')"
for dylib in $(LD_LIBRARY_PATH=$libdirs ldd $bundle | grep .so | grep local-lib | awk '{print $3}'); do
install -v $dylib $archivefolder/bin
done
done
else
echo "Copying libraries from $WXDIR/lib to $archivefolder/bin"
for dylib in $(find $WXDIR/lib | grep "so"); do
cp -P -v $dylib $archivefolder/bin
done
fi
echo "Copying startup script..."
cp -f $WD/startup_script.sh $archivefolder/$appname
chmod +x $archivefolder/$appname
echo "Copying perl from $PERL_BIN"
cp -f $PERL_BIN $archivefolder/perl-local
${PP_BIN} wxextension .0 \
-M attributes -M base -M bytes -M B -M POSIX \
-M FindBin -M Unicode::Normalize -M Tie::Handle \
-M Time::Local -M Math::Trig \
-M lib -M overload \
-M warnings -M local::lib \
-M strict -M utf8 -M parent \
-B -p -e "print 123" -o $WD/_tmp/test.par
unzip -qq -o $WD/_tmp/test.par -d $WD/_tmp/
cp -rf $WD/_tmp/lib/* $archivefolder/local-lib/lib/perl5/
cp -rf $WD/_tmp/shlib $archivefolder/
rm -rf $WD/_tmp
for i in $(cat $WD/libpaths.txt); do
install -v $i $archivefolder/bin
done
echo "Cleaning local-lib"
rm -rf $archivefolder/local-lib/bin
rm -rf $archivefolder/local-lib/man
rm -f $archivefolder/local-lib/lib/perl5/Algorithm/*.pl
rm -rf $archivefolder/local-lib/lib/perl5/unicore
rm -rf $archivefolder/local-lib/lib/perl5/App
rm -rf $archivefolder/local-lib/lib/perl5/Devel/CheckLib.pm
rm -rf $archivefolder/local-lib/lib/perl5/ExtUtils
rm -rf $archivefolder/local-lib/lib/perl5/Module/Build*
rm -rf $(pwd)$archivefolder/local-lib/lib/perl5/TAP
rm -rf $(pwd)/$archivefolder/local-lib/lib/perl5/Test*
find $(pwd)/$archivefolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name DocView -or -name STC -or -name IPC \
-or -name AUI -or -name Calendar -or -name DataView \
-or -name DateTime -or -name Media -or -name PerlTest \
-or -name Ribbon \) -exec rm -rf "{}" \;
rm -rf $archivefolder/local-lib/lib/perl5/*/Alien/wxWidgets/*/include
find $archivefolder/local-lib -depth -type d -empty -exec rmdir "{}" \;
tar -C$(pwd)/$(dirname $appfolder) -cjf $(pwd)/$dmgfile "$appname"

View File

@ -1,6 +0,0 @@
#!/bin/bash
# Written by Joseph Lenox
# Licensed under the same license as the rest of Slic3r.
# ------------------------
pp -a "../../utils;utils" -a "../../var;var" -a "../../lib;lib" -a "../../local-lib;local-lib" -a "../../slic3r.pl;slic3r.pl" -M AutoLoader -M B -M Carp -M Class::Accessor -M Class::XSAccessor -M Class::XSAccessor::Heavy -M Config -M Cwd -M Devel::GlobalDestruction -M Digest -M Digest::MD5 -M Digest::SHA -M Digest::base -M DynaLoader -M Errno -M Exporter -M Exporter::Heavy -M Fcntl -M File::Basename -M File::Glob -M File::Spec -M File::Spec::Unix -M File::Spec::Win32 -M FindBin -M HTTP::Config -M HTTP::Date -M HTTP::Headers -M HTTP::Headers::Util -M HTTP::Message -M HTTP::Request -M HTTP::Request::Common -M HTTP::Response -M HTTP::Status -M IO -M IO::Handle -M IO::Select -M LWP -M LWP::MediaTypes -M LWP::MemberMixin -M LWP::Protocol -M LWP::Protocol::http -M LWP::UserAgent -M List::Util -M Math::Trig -M Method::Generate::Accessor -M Method::Generate::BuildAll -M Method::Generate::Constructor -M Module::Runtime -M POSIX -M Pod::Escapes -M Pod::Text -M Pod::Usage -M SelectSaver -M Socket -M Socket6 -M Storable -M Sub::Defer -M Sub::Exporter -M Sub::Exporter::Progressive -M Sub::Name -M Symbol -M Term::Cap -M Text::ParseWords -M Thread -M Thread::Queue -M Thread::Semaphore -M Tie::Handle -M Tie::Hash -M Tie::StdHandle -M Time::Local -M URI -M URI::Escape -M URI::http -M Unicode::Normalize -M XSLoader -B -M lib -p ../../slic3r.pl -o ../../slic3r.par

View File

@ -0,0 +1,13 @@
[Desktop Entry]
Type=Application
Version=SLIC3R_VERSION
Name=APPLICATION_NAME
Comment=Prepare 3D Models for printing
Icon=slic3r
Exec=Slic3r

View File

@ -0,0 +1,5 @@
#!/bin/bash
DIR=$(dirname "$0")
export LD_LIBRARY_PATH=./shlib/x86_64-linux-thread-multi/
exec "$DIR/perl-local" -I"$DIR/local-lib/lib/perl5" "$DIR/slic3r.pl" $@

View File

@ -0,0 +1,7 @@
#!/bin/bash
# Script to only decrypt if it is available
if [ ! -z ${encrypted_daaf322d08bf_key+x} ]; then
openssl aes-256-cbc -K $encrypted_daaf322d08bf_key -iv $encrypted_daaf322d08bf_iv -in $TRAVIS_BUILD_DIR/package/deploy/slic3r-upload.rsa.enc -out ~/slic3r-upload.rsa -d
chmod 600 ~/slic3r-upload.rsa
fi

30
package/linux/travis-setup.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Script to configure travis environment prior to build
WXVERSION=302
CACHE=$HOME/cache
mkdir -p $CACHE
if [ ! -e $CACHE/slic3r-perlbrew-5.24.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/slic3r-perl.524.travis.tar.bz2 => $CACHE/slic3r-perlbrew-5.24.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/slic3r-perl.524.travis.tar.bz2" -o $CACHE/slic3r-perlbrew-5.24.tar.bz2;
fi
if [ ! -e $CACHE/boost-compiled.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/boost_1_63_0.built.gcc-4.9.4-buildserver.tar.bz2 => $CACHE/boost-compiled.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/boost_1_63_0.built.gcc-4.9.4-buildserver.tar.bz2" -o $CACHE/boost-compiled.tar.bz2
fi
if [ ! -e $CACHE/local-lib-wx${WXVERSION}.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/slic3r-dependencies.travis-wx${WXVERSION}.tar.bz2 => $CACHE/local-lib-wx${WXVERSION}.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/slic3r-dependencies.travis-wx${WXVERSION}.tar.bz2" -o $CACHE/local-lib-wx${WXVERSION}.tar.bz2
fi
if [ ! -e $CACHE/wx${WXVERSION}.tar.bz2 ]; then
echo "Downloading http://www.siusgs.com/slic3r/buildserver/wx${WXVERSION}-libs.tar.bz2 => $CACHE/wx${WXVERSION}.tar.bz2"
curl -L "http://www.siusgs.com/slic3r/buildserver/wx${WXVERSION}-libs.tar.bz2" -o $CACHE/wx${WXVERSION}.tar.bz2
fi
tar -C$TRAVIS_BUILD_DIR -xjf $CACHE/local-lib-wx${WXVERSION}.tar.bz2
tar -C$HOME/perl5/perlbrew/perls -xjf $CACHE/slic3r-perlbrew-5.24.tar.bz2
tar -C$HOME -xjf $CACHE/boost-compiled.tar.bz2
tar -C$HOME -xjf $CACHE/wx${WXVERSION}.tar.bz2

View File

@ -133,7 +133,6 @@ find -d $macosfolder/local-lib -type d -path '*/Wx/*' \( -name WebView \
-or -name Ribbon \) -exec rm -rf "{}" \;
find -d $macosfolder/local-lib -name libwx_osx_cocoau_ribbon-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_aui-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_media-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_stc-3.0.0.2.0.dylib -delete
find -d $macosfolder/local-lib -name libwx_osx_cocoau_webview-3.0.0.2.0.dylib -delete
rm -rf $macosfolder/local-lib/lib/perl5/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/include

View File

@ -93,6 +93,8 @@ cat << EOF >> $plistfile
<string>10.7</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
EOF

View File

@ -0,0 +1,21 @@
if (!(Test-Path "C:\users\appveyor\local-lib.7z")) {
wget "http://www.siusgs.com/slic3r/buildserver/win/slic3r-perl-dependencies-5.24.0-win-seh-gcc6.3.0-x64.7z" -o "C:\users\appveyor\local-lib.7z" | Write-Output
}
if (Test-Path "C:\users\appveyor\local-lib.7z") {
cmd /c "7z x C:\Users\appveyor\local-lib.7z -oC:\projects\slic3r" -y | Write-Output
rm -r 'C:\projects\slic3r\local-lib\Slic3r*'
}
$env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;" + $env:Path
cd C:\projects\slic3r
rm -r 'C:\Program Files (x86)\Microsoft Vis*\bin' -Force
Add-AppveyorCompilationMessage -Message "Building Slic3r XS"
perl Build.pl
if (-NOT ($LASTEXITCODE -eq 0)) {
Add-AppveyorCompilationMessage -Message "XS Failed to Build" -Category Error
}
Add-AppveyorCompilationMessage -Message "Making ZIP package"
cd package/win
./compile_wrapper.ps1 524| Write-Output
./package_win32.ps1 524| Write-Output

View File

@ -0,0 +1,8 @@
Add-AppveyorCompilationMessage -Message "Making ZIP package"
cd package/win
./compile_wrapper.ps1 524 | Write-Output
./package_win32.ps1 524| Write-Output
cd ../../
Add-AppveyorCompilationMessage -Message "Uploading to server."
& ./package/deploy/winscp.ps1 -DIR win -KEY $env:APPVEYOR_BUILD_FOLDER/package/deploy/slic3r-upload.ppk -FILE *.zip *>> ./sftplog.txt

View File

@ -0,0 +1,71 @@
mkdir C:\projects\slic3r\FreeGLUT
if (!(Test-Path "C:\users\appveyor\freeglut.7z"))
{
wget "http://www.siusgs.com/slic3r/buildserver/win/freeglut-mingw-3.0.0.win64.7z" -o C:\users\appveyor\freeglut.7z
}
cmd /c "7z x C:\Users\appveyor\freeglut.7z -oC:\projects\slic3r\FreeGLUT"
if (!(Test-Path "C:\users\appveyor\strawberry.msi")) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=slic3r-perl-5.24.1.4-64bit.msi" -o "C:\users\appveyor\strawberry.msi" | Write-Output
}
if (!(Test-Path "C:\users\appveyor\extra_perl.7z")) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=Strawberry-6.3.0-seg-archive.7z" -o "C:\users\appveyor\extra_perl.7z" | Write-Output
}
msiexec.exe /i "C:\users\appveyor\strawberry.msi" /quiet
cmd /c "7z x -aoa C:\Users\appveyor\extra_perl.7z -oC:\"
if (!(Test-Path "C:\users\appveyor\winscp.zip")) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=WinSCP-5.9.4-Portable.zip" -o "C:\users\appveyor\winscp.zip" | Write-Output
}
cmd /c "7z x C:\Users\appveyor\winscp.zip -oC:\Strawberry\c\bin"
rm -r C:\min* -Force
rm -r C:\msys64\mingw* -Force
rm -r C:\cygwin* -Force
rm -r C:\Perl -Force
$PERLDIR = 'C:\Strawberry'
$env:Path = "C:\Strawberry\c\bin;C:\Strawberry\perl\bin;C:\Strawberry\perl\vendor\bin;" + $env:Path
if(Test-Path -Path 'C:\Strawberry' ) {
copy C:\Strawberry\c\bin\gcc.exe C:\Strawberry\c\bin\cc.exe
cmd /c mklink /D C:\Perl C:\Strawberry\perl
mkdir C:\dev
if (!(Test-Path "C:\users\appveyor\boost.1.63.0.7z") -Or $env:FORCE_BOOST_REINSTALL -eq 1) {
wget "https://bintray.com/lordofhyphens/Slic3r/download_file?file_path=boost_1_63_0-x64-gcc-6.3.0-seh.7z" -O "C:\users\appveyor\boost.1.63.0.7z" | Write-Output
}
Add-AppveyorCompilationMessage -Message "Extracting cached archive."
cmd /c "7z x C:\Users\appveyor\boost.1.63.0.7z -oC:\dev"
mkdir C:\dev\CitrusPerl
cmd /C mklink /D C:\dev\CitrusPerl\mingw32 C:\Strawberry\c
cd C:\projects\slic3r
cpanm ExtUtils::Typemaps::Basic
cpanm ExtUtils::Typemaps::Default
cpanm local::lib
Add-AppveyorCompilationMessage -Message "Finished install script."
rm -r 'C:\Program Files\Git\usr\bin' -Force
} else {
Add-AppveyorCompilationMessage -Message "No strawberry perl!"
}
Add-AppveyorCompilationMessage -Message "Installing wxWidgets (xsgui dependency))"
if ($env:FORCE_WX_BUILD -eq 1) {
rm "C:\Users\appveyor\wxwidgets.7z" -Force
}
if (!(Test-Path "C:\Users\appveyor\wxwidgets.7z")) {
Add-AppveyorCompilationMessage -Message "Compiling wxWidgets"
git clone https://github.com/wxWidgets/wxWidgets -b "v3.1.0" -q C:\dev\wxWidgets
cd C:\dev\wxwidgets
cp .\include\wx\msw\setup0.h include/wx/msw/setup.h
cd build\msw
mingw32-make -f makefile.gcc CXXFLAGS="-std=gnu++11" BUILD=release VENDOR=Slic3r
cd C:\dev
7z a C:\Users\appveyor\wxwidgets.7z wxwidgets
cd C:\projects\slic3r
} else {
Add-AppveyorCompilationMessage -Message "Extracting prebuilt wxWidgets."
7z x "C:\Users\appveyor\wxwidgets.7z" -oC:\dev
}

View File

@ -1 +0,0 @@
@perl5.24.0.exe slic3r.pl %*

View File

@ -4,14 +4,27 @@ if ($args[0])
$perlver = $args[0]
} else
{
$perlver = 518
$perlver = 524
}
$perllib = "-lperl$perlver"
$shell_loc = "..\common\shell.cpp"
# Build the resource file (used to load icon, etc)
windres slic3r.rc -O coff -o slic3r.res
g++ -c -I'C:\strawberry\perl\lib\CORE\' shell.cpp -o slic3r.o
g++ -c -I'C:\strawberry\perl\lib\CORE\' -DFORCE_GUI shell.cpp -o slic3r-gui.o
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r-console.exe | Write-Host
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r.exe | Write-Host
# Compile an object file that does not have gui forced.
g++ -c -I'C:\strawberry\perl\lib\CORE\' $shell_loc -o slic3r.o
# Compile an object file with --gui automatically passed as an argument
g++ -c -I'C:\strawberry\perl\lib\CORE\' -DFORCE_GUI $shell_loc -o slic3r-gui.o
# Build the EXE for the unforced version as slic3r-console
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r.o slic3r.res -o slic3r-console.exe | Write-Host
# Build the EXE for the forced GUI
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -mwindows -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r.exe | Write-Host
# Build an extra copy of the GUI version that creates a console window
g++ -static-libgcc -static-libstdc++ -L'C:\strawberry\c\lib' -L'C:\strawberry\perl\bin' -L'C:\strawberry\perl\lib\CORE\' $perllib slic3r-gui.o slic3r.res -o slic3r-debug-console.exe | Write-Host

View File

@ -1,7 +1,7 @@
# Written by Joseph Lenox
# Licensed under the same license as the rest of Slic3r.
# ------------------------
# You need to have Strawberry Perl 5.24.0.1 installed for this to work,
# You need to have Strawberry Perl 5.24.0.1 (or slic3r-perl) installed for this to work,
param (
[switch]$exe = $false
)
@ -44,7 +44,8 @@ cpanm "PAR::Packer"
pp `
-a "slic3r.exe;slic3r.exe" `
-a "slic3r.exe;slic3r-console.exe" `
-a "slic3r-console.exe;slic3r-console.exe" `
-a "slic3r-debug-console.exe;slic3r-debug-console.exe" `
-a "../../lib;lib" `
-a "../../local-lib;local-lib" `
-a "../../slic3r.pl;slic3r.pl" `

View File

@ -51,6 +51,10 @@ my %cli_options = ();
'duplicate-grid=s' => \$opt{duplicate_grid},
'print-center=s' => \$opt{print_center},
'dont-arrange' => \$opt{dont_arrange},
# legacy options, ignored
'no-plater' => \$opt{no_plater},
'gui-mode=s' => \$opt{gui_mode},
);
foreach my $opt_key (keys %{$Slic3r::Config::Options}) {
my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next;
@ -60,6 +64,9 @@ my %cli_options = ();
@ARGV = grep !/^-psn_\d/, @ARGV if $^O eq 'darwin';
GetOptions(%options) or usage(1);
warn "--no-plater option is deprecated; ignoring\n" if $opt{no_plater};
warn "--gui-mode option is deprecated (Slic3r now has only Expert Mode); ignoring\n" if $opt{gui_mode};
}
# load configuration files

92
t/adaptive_width.t Normal file
View File

@ -0,0 +1,92 @@
use Test::More;
use strict;
use warnings;
plan tests => 32;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first);
use Slic3r;
use Slic3r::Geometry qw(X Y scale epsilon);
use Slic3r::Surface ':types';
sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
{
my $test = sub {
my ($expolygon, $fw, $expected, $descr) = @_;
my $flow = Slic3r::Flow->new(
nozzle_diameter => 0.5,
height => 0.4,
width => $fw,
);
my $slices = Slic3r::Surface::Collection->new;
$slices->append(Slic3r::Surface->new(
surface_type => S_TYPE_INTERNAL,
expolygon => $expolygon,
));
my $config = Slic3r::Config::Full->new;
my $loops = Slic3r::ExtrusionPath::Collection->new;
my $gap_fill = Slic3r::ExtrusionPath::Collection->new;
my $fill_surfaces = Slic3r::Surface::Collection->new;
my $pg = Slic3r::Layer::PerimeterGenerator->new(
$slices, $flow->height, $flow,
$config, $config, $config,
$loops, $gap_fill, $fill_surfaces,
);
$pg->process;
$loops = $loops->flatten;
my @single = grep $_->isa('Slic3r::ExtrusionPath'), @$loops;
my @loops = grep $_->isa('Slic3r::ExtrusionLoop'), @$loops;
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output(
"output.svg",
expolygons => [$expolygon],
polylines => [ map $_->isa('Slic3r::ExtrusionPath') ? $_->polyline : $_->polygon->split_at_first_point, @$loops ],
red_polylines => [ map $_->polyline, @$gap_fill ],
);
}
is scalar(@single), $expected->{single} // 0, "expected number of single lines ($descr)";
is scalar(@loops), $expected->{loops} // 0, "expected number of loops ($descr)";
is scalar(@$gap_fill), $expected->{gaps} // 0, "expected number of gap fills ($descr)";
if ($expected->{single}) {
ok abs($loops->[0]->width - $expected->{width}) < epsilon, "single line covers the full width ($descr)";
}
if ($expected->{loops}) {
my $loop_width = $loops[0][0]->width;
my $gap_fill_width = @$gap_fill ? $gap_fill->[0]->width : 0;
ok $loop_width * $expected->{loops} * 2 + $gap_fill_width > $expected->{width} - epsilon,
"loop total width + gap fill covers the full width ($descr)";
}
};
my $fw = 0.7;
my $test_rect = sub {
my ($width, $expected) = @_;
my $e = Slic3r::ExPolygon->new([ scale_points [0,0], [100,0], [100,$width], [0,$width] ]);
$expected->{width} = $width;
$test->($e, $fw, $expected, $width);
};
$test_rect->($fw * 1, { single => 1, gaps => 0 });
$test_rect->($fw * 1.3, { single => 1, gaps => 0 });
$test_rect->($fw * 1.5, { single => 1, gaps => 0 });
$test_rect->($fw * 2, { loops => 1, gaps => 0 });
$test_rect->($fw * 2.5, { loops => 1, gaps => 1 });
$test_rect->($fw * 3, { loops => 1, gaps => 1 });
$test_rect->($fw * 3.5, { loops => 2, gaps => 0 });
$test_rect->($fw * 4, { loops => 2, gaps => 1 });
}
__END__

View File

@ -1,4 +1,4 @@
use Test::More tests => 16;
use Test::More tests => 18;
use strict;
use warnings;
@ -82,8 +82,25 @@ use Slic3r::Test;
ok check_angle($lower, $bridge, 45, undef, $bridge->area/2), 'correct bridge angle for square overhang with L-shaped anchors';
}
{
# GH #2477: This test case failed when we computed coverage by summing length of centerlines
# instead of summing their covered area.
my $bridge = Slic3r::ExPolygon->new(
Slic3r::Polygon->new([30299990,14299990],[1500010,14299990],[1500010,1500010],[30299990,1500010]),
);
my $lower = [
Slic3r::ExPolygon->new(
Slic3r::Polygon->new([31800000,15800000],[0,15800000],[0,0],[31800000,0]),
Slic3r::Polygon->new([1499999,1500000],[1499999,14300000],[30300000,14300000],[30300000,1500000]),
),
];
ok check_angle($lower, $bridge, 90, undef, $bridge->area, 500000),
'correct bridge angle for rectangle';
}
sub check_angle {
my ($lower, $bridge, $expected, $tolerance, $expected_coverage) = @_;
my ($lower, $bridge, $expected, $tolerance, $expected_coverage, $extrusion_width) = @_;
if (ref($lower) eq 'ARRAY') {
$lower = Slic3r::ExPolygon::Collection->new(@$lower);
@ -91,8 +108,9 @@ sub check_angle {
$expected_coverage //= -1;
$expected_coverage = $bridge->area if $expected_coverage == -1;
$extrusion_width //= scale 0.5;
my $bd = Slic3r::BridgeDetector->new($bridge, $lower, scale 0.5);
my $bd = Slic3r::BridgeDetector->new($bridge, $lower, $extrusion_width);
$tolerance //= rad2deg($bd->resolution) + epsilon;
$bd->detect_angle;

View File

@ -1,4 +1,4 @@
use Test::More tests => 15;
use Test::More tests => 16;
use strict;
use warnings;
@ -132,4 +132,13 @@ use Slic3r::Test;
'layer_num grows continously'; # i.e. no duplicates or regressions
}
{
my $config = Slic3r::Config->new_from_defaults;
$config->set('complete_objects', 1);
$config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_');
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3);
my $gcode = Slic3r::Test::gcode($print);
is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly';
}
__END__

View File

@ -1,93 +0,0 @@
use Test::More;
use strict;
use warnings;
plan skip_all => 'variable-width paths are currently disabled';
plan tests => 20;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first);
use Slic3r;
use Slic3r::Geometry qw(X Y scale epsilon);
use Slic3r::Surface ':types';
sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
{
my $square = Slic3r::ExPolygon->new([
scale_points [0,0], [10,0], [10,10], [0,10],
]);
my @offsets = @{$square->noncollapsing_offset_ex(- scale 5)};
is scalar @offsets, 1, 'non-collapsing offset';
}
{
local $Slic3r::Config = Slic3r::Config->new(
perimeters => 3,
);
my $w = 0.7;
my $perimeter_flow = Slic3r::Flow->new(
nozzle_diameter => 0.5,
layer_height => 0.4,
width => $w,
);
my $print = Slic3r::Print->new;
my $region = Slic3r::Print::Region->new(
print => $print,
flows => { perimeter => $perimeter_flow },
);
push @{$print->regions}, $region;
my $object = Slic3r::Print::Object->new(
print => $print,
size => [1,1],
);
my $make_layer = sub {
my ($width) = @_;
my $layer = Slic3r::Layer->new(
object => $object,
id => 1,
slices => [
Slic3r::Surface->new(
surface_type => S_TYPE_INTERNAL,
expolygon => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,$width], [0,$width] ]),
),
],
thin_walls => [],
);
my $layerm = $layer->region(0);
$layer->make_perimeters;
return $layerm;
};
my %widths = (
1 * $w => { perimeters => 1, gaps => 0 },
1.3 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 0.2 * $w)->spacing },
1.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 0.5 * $w)->spacing },
2 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->spacing },
2.5 * $w => { perimeters => 1, gaps => 1, gap_flow_spacing => $perimeter_flow->clone(width => 1.5 * $w)->spacing },
3 * $w => { perimeters => 2, gaps => 0 },
4 * $w => { perimeters => 2, gaps => 1, gap_flow_spacing => $perimeter_flow->spacing },
);
foreach my $width (sort keys %widths) {
my $layerm = $make_layer->($width);
is scalar @{$layerm->perimeters}, $widths{$width}{perimeters}, 'right number of perimeters';
is scalar @{$layerm->thin_fills} ? 1 : 0, $widths{$width}{gaps},
($widths{$width}{gaps} ? 'gaps were filled' : 'no gaps detected'); # TODO: we should check the exact number of gaps, but we need a better medial axis algorithm
my @gaps = map $_, @{$layerm->thin_fills};
if (@gaps) {
ok +(!first { abs($_->flow_spacing - $widths{$width}{gap_flow_spacing}) > epsilon } @gaps),
'flow spacing was dynamically adjusted';
}
}
}
__END__

View File

@ -22,7 +22,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
{
my $print = Slic3r::Print->new;
my $surface_width = 250;
my $distance = Slic3r::Filler::adjust_solid_spacing($surface_width, 47);
my $distance = Slic3r::Flow::solid_spacing($surface_width, 47);
is $distance, 50, 'adjusted solid distance';
is $surface_width % $distance, 0, 'adjusted solid distance';
}

View File

@ -2,7 +2,6 @@ use Test::More;
use strict;
use warnings;
plan skip_all => 'temporarily disabled';
plan tests => 4;
BEGIN {
@ -12,46 +11,41 @@ BEGIN {
}
use Slic3r;
use Slic3r::Geometry qw(Z epsilon scale);
use Slic3r::Test;
{
# We only need to slice at one height, so we'll build a non-manifold mesh
# that still produces complete loops at that height. Triangular walls are
# enough for this purpose.
# Basically we want to check what happens when three concentric loops happen
# We want to check what happens when three concentric loops happen
# to be at the same height, the two external ones being ccw and the other being
# a hole, thus cw.
my (@vertices, @facets) = ();
Slic3r::Test::add_facet($_, \@vertices, \@facets) for
# external surface below the slicing Z
[ [0,0,0], [20,0,10], [0,0,10] ],
[ [20,0,0], [20,20,10], [20,0,10] ],
[ [20,20,0], [0,20,10], [20,20,10] ],
[ [0,20,0], [0,0,10], [0,20,10] ],
# a hole, thus cw. So we create three cubes, centered around origin, the internal
# one having reversed normals.
my $mesh1 = Slic3r::Test::mesh('20mm_cube');
# external insetted surface above the slicing Z
[ [2,2,10], [18,2,10], [2,2,20] ],
[ [18,2,10], [18,18,10], [18,2,20] ],
[ [18,18,10], [2,18,10], [18,18,20] ],
[ [2,18,10], [2,2,10], [2,18,20] ],
# center around origin
my $bb = $mesh1->bounding_box;
$mesh1->translate(
-($bb->x_min + $bb->size->x/2),
-($bb->y_min + $bb->size->y/2), #//
-($bb->z_min + $bb->size->z/2),
);
# insetted hole below the slicing Z
[ [15,5,0], [5,5,10], [15,5,10] ],
[ [15,15,0], [15,5,10], [15,15,10] ],
[ [5,15,0], [15,15,10], [5,15,10] ],
[ [5,5,0], [5,15,10], [5,5,10] ];
my $mesh2 = $mesh1->clone;
$mesh2->scale(1.2);
my $mesh = Slic3r::TriangleMesh->new;
$mesh->ReadFromPerl(\@vertices, \@facets);
$mesh->analyze;
my @lines = map $mesh->intersect_facet($_, 10), 0..$#facets;
my $loops = Slic3r::TriangleMesh::make_loops(\@lines);
is scalar(@$loops), 3, 'correct number of loops detected';
is scalar(grep $_->is_counter_clockwise, @$loops), 2, 'correct number of ccw loops detected';
my $mesh3 = $mesh2->clone;
$mesh3->scale(1.2);
my @surfaces = Slic3r::Layer::Region::_merge_loops($loops, 0);
is scalar(@surfaces), 1, 'one surface detected';
is scalar(@{$surfaces[0]->expolygon})-1, 1, 'surface has one hole';
$mesh1->reverse_normals;
ok $mesh1->volume < 0, 'reverse_normals';
my $all_meshes = Slic3r::TriangleMesh->new;
$all_meshes->merge($_) for $mesh1, $mesh2, $mesh3;
my $loops = $all_meshes->slice_at(Z, 0);
is scalar(@$loops), 1, 'one expolygon returned';
is scalar(@{$loops->[0]->holes}), 1, 'expolygon has one hole';
ok abs(-$loops->[0]->holes->[0]->area - scale($bb->size->x)*scale($bb->size->y)) < epsilon, #))
'hole has expected size';
}
__END__

View File

@ -16,7 +16,7 @@ while (<>) {
if (/^G1.*? F([0-9.]+)/) {
$F = $1;
}
if (/^G1 X([0-9.]+) Y([0-9.]+).*? E([0-9.]+)/) {
if (/^G1 X([0-9.-]+) Y([0-9.-]+).*? E([0-9.-]+)/) {
my ($x, $y, $e) = ($1, $2, $3);
my $e_length = $e - $E;
if ($e_length > 0 && defined $X && defined $Y) {

BIN
var/arrow_in.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 600 B

View File

@ -5,24 +5,6 @@
namespace Slic3r {
class BridgeDirectionComparator {
public:
std::map<double,double> dir_coverage; // angle => score
BridgeDirectionComparator(double _extrusion_width)
: extrusion_width(_extrusion_width)
{};
// the best direction is the one causing most lines to be bridged (thus most coverage)
bool operator() (double a, double b) {
// Initial sort by coverage only - comparator must obey strict weak ordering
return (this->dir_coverage[a] > this->dir_coverage[b]);
};
private:
double extrusion_width;
};
BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices,
coord_t _extrusion_width)
: expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width),
@ -44,56 +26,56 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle
// safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
this->_anchors = intersection_ex(grown, this->lower_slices, true);
/*
if (0) {
require "Slic3r/SVG.pm";
Slic3r::SVG::output("bridge.svg",
expolygons => [ $self->expolygon ],
red_expolygons => $self->lower_slices,
polylines => $self->_edges,
);
#if 0
{
SVG svg("bridge.svg");
svg.draw(this->expolygon);
svg.draw(this->lower_slices, "red");
svg.draw(this->_anchors, "yellow");
svg.draw(this->_edges, "black", scale_(0.2));
svg.Close();
}
*/
#endif
}
bool
BridgeDetector::detect_angle()
{
// Do nothing if the bridging region is completely in the air
// and there are no anchors available at the layer below.
if (this->_edges.empty() || this->_anchors.empty()) return false;
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
we'll use this one to clip our test lines and be sure that their endpoints
are inside the anchors and not on their contours leading to false negatives. */
Polygons clip_area = offset(this->expolygon, +this->extrusion_width/2);
const Polygons clip_area = offset(this->expolygon, +this->extrusion_width/2);
/* we'll now try several directions using a rudimentary visibility check:
bridge in several directions and then sum the length of lines having both
endpoints within anchors */
// generate the list of candidate angles
std::vector<BridgeDirection> candidates;
{
// we test angles according to configured resolution
std::vector<double> angles;
for (int i = 0; i <= PI/this->resolution; ++i)
angles.push_back(i * this->resolution);
// we also test angles of each bridge contour
{
Polygons pp = this->expolygon;
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
Lines lines = p->lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
angles.push_back(line->direction());
}
}
for (const Polygon &p : (Polygons)this->expolygon)
for (const Line &line : p.lines())
angles.push_back(line.direction());
/* we also test angles of each open supporting edge
(this finds the optimal angle for C-shaped supports) */
for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) {
if (edge->first_point().coincides_with(edge->last_point())) continue;
angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
for (const Polyline &edge : this->_edges) {
if (edge.first_point().coincides_with(edge.last_point())) continue;
angles.push_back(Line(edge.first_point(), edge.last_point()).direction());
}
// remove duplicates
double min_resolution = PI/180.0; // 1 degree
constexpr double min_resolution = PI/180.0; // 1 degree
std::sort(angles.begin(), angles.end());
for (size_t i = 1; i < angles.size(); ++i) {
if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) {
@ -106,81 +88,67 @@ BridgeDetector::detect_angle()
if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
angles.pop_back();
BridgeDirectionComparator bdcomp(this->extrusion_width);
std::map<double,double> dir_avg_length;
double line_increment = this->extrusion_width;
for (auto angle : angles)
candidates.push_back(BridgeDirection(angle));
}
const coord_t line_increment = this->extrusion_width;
bool have_coverage = false;
for (std::vector<double>::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) {
for (BridgeDirection &candidate : candidates) {
Polygons my_clip_area = clip_area;
ExPolygons my_anchors = this->_anchors;
// rotate everything - the center point doesn't matter
for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it)
it->rotate(-*angle, Point(0,0));
for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
it->rotate(-*angle, Point(0,0));
for (Polygon &p : my_clip_area)
p.rotate(-candidate.angle, Point(0,0));
for (ExPolygon &e : my_anchors)
e.rotate(-candidate.angle, Point(0,0));
// generate lines in this direction
BoundingBox bb;
for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
bb.merge((Points)*it);
for (const ExPolygon &e : my_anchors)
bb.merge(e.bounding_box());
Lines lines;
for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment)
lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y)));
Lines clipped_lines = intersection_ln(lines, my_clip_area);
const Lines clipped_lines = intersection_ln(lines, my_clip_area);
// remove any line not having both endpoints within anchors
for (size_t i = 0; i < clipped_lines.size(); ++i) {
Line &line = clipped_lines[i];
for (const Line &line : clipped_lines) {
// skip any line not having both endpoints within anchors
if (!Slic3r::Geometry::contains(my_anchors, line.a)
|| !Slic3r::Geometry::contains(my_anchors, line.b)) {
clipped_lines.erase(clipped_lines.begin() + i);
--i;
|| !Slic3r::Geometry::contains(my_anchors, line.b))
continue;
candidate.max_length = std::max(candidate.max_length, line.length());
// Calculate coverage as actual covered area, because length of centerlines
// is not accurate enough when such lines are slightly skewed and not parallel
// to the sides; calculating area will compute them as triangles.
// TODO: use a faster algorithm for computing covered area by using a sweep line
// instead of intersecting many lines.
candidate.coverage += Slic3r::Geometry::area(intersection(
my_clip_area,
offset((Polyline)line, +this->extrusion_width/2)
));
}
}
std::vector<double> lengths;
double total_length = 0;
for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) {
double len = line->length();
lengths.push_back(len);
total_length += len;
}
if (total_length) have_coverage = true;
// sum length of bridged lines
bdcomp.dir_coverage[*angle] = total_length;
/* The following produces more correct results in some cases and more broken in others.
TODO: investigate, as it looks more reliable than line clipping. */
// $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
// max length of bridged lines
dir_avg_length[*angle] = !lengths.empty()
? *std::max_element(lengths.begin(), lengths.end())
: 0;
if (candidate.coverage > 0) have_coverage = true;
}
// if no direction produced coverage, then there's no bridge direction
if (!have_coverage) return false;
// sort directions by coverage - most coverage first
std::sort(angles.begin(), angles.end(), bdcomp);
this->angle = angles.front();
std::sort(candidates.begin(), candidates.end());
// if any other direction is within extrusion width of coverage, prefer it if shorter
// TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred?
double most_coverage_angle = this->angle;
for (std::vector<double>::const_iterator angle = angles.begin() + 1;
angle != angles.end() && bdcomp.dir_coverage[most_coverage_angle] - bdcomp.dir_coverage[*angle] < this->extrusion_width;
++angle
) {
if (dir_avg_length[*angle] < dir_avg_length[this->angle]) {
this->angle = *angle;
}
}
size_t i_best = 0;
for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->extrusion_width; ++ i)
if (candidates[i].max_length < candidates[i_best].max_length)
i_best = i;
this->angle = candidates[i_best].angle;
if (this->angle >= PI) this->angle -= PI;
@ -208,21 +176,21 @@ BridgeDetector::coverage(double angle) const
// Compute trapezoids according to a vertical orientation
Polygons trapezoids;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
it->get_trapezoids2(&trapezoids, PI/2.0);
for (const ExPolygon &e : grown)
e.get_trapezoids2(&trapezoids, PI/2.0);
// get anchors, convert them to Polygons and rotate them too
Polygons anchors;
for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) {
Polygons pp = *anchor;
for (Polygons::iterator p = pp.begin(); p != pp.end(); ++p)
p->rotate(PI/2.0 - angle, Point(0,0));
anchors.insert(anchors.end(), pp.begin(), pp.end());
for (const ExPolygon &anchor : this->_anchors) {
Polygons pp = anchor;
for (Polygon &p : pp)
p.rotate(PI/2.0 - angle, Point(0,0));
append_to(anchors, pp);
}
Polygons covered;
for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Lines supported = intersection_ln(trapezoid->lines(), anchors);
for (const Polygon &trapezoid : trapezoids) {
Lines supported = intersection_ln(trapezoid.lines(), anchors);
// not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i) {
@ -232,17 +200,17 @@ BridgeDetector::coverage(double angle) const
}
}
if (supported.size() >= 2) covered.push_back(*trapezoid);
if (supported.size() >= 2) covered.push_back(trapezoid);
}
// merge trapezoids and rotate them back
Polygons _coverage = union_(covered);
for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p)
p->rotate(-(PI/2.0 - angle), Point(0,0));
covered = union_(covered);
for (Polygon &p : covered)
p.rotate(-(PI/2.0 - angle), Point(0,0));
// intersect trapezoids with actual bridge area to remove extra margins
// and append it to result
return intersection(_coverage, this->expolygon);
return intersection(covered, this->expolygon);
/*
if (0) {
@ -272,10 +240,8 @@ BridgeDetector::unsupported_edges(double angle) const
// get bridge edges (both contour and holes)
Polylines bridge_edges;
{
Polygons pp = this->expolygon;
bridge_edges.insert(bridge_edges.end(), pp.begin(), pp.end()); // this uses split_at_first_point()
}
for (const Polygon &p : (Polygons)this->expolygon)
bridge_edges.push_back(p.split_at_first_point());
// get unsupported edges
Polylines _unsupported = diff_pl(
@ -290,13 +256,10 @@ BridgeDetector::unsupported_edges(double angle) const
direction might still benefit from anchors if long enough)
double angle_tolerance = PI / 180.0 * 5.0; */
Polylines unsupported;
for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) {
Lines lines = polyline->lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
if (!Slic3r::Geometry::directions_parallel(line->direction(), angle))
unsupported.push_back(*line);
}
}
for (const Polyline &polyline : _unsupported)
for (const Line &line : polyline.lines())
if (!Slic3r::Geometry::directions_parallel(line.direction(), angle))
unsupported.push_back(line);
return unsupported;
/*

View File

@ -15,7 +15,7 @@ public:
// Lower slices, all regions.
ExPolygonCollection lower_slices;
// Scaled extrusion width of the infill.
double extrusion_width;
coord_t extrusion_width;
// Angle resolution for the brute force search of the best bridging angle.
double resolution;
// The final optimal angle.
@ -31,6 +31,19 @@ private:
Polylines _edges;
// Closed polygons representing the supporting areas.
ExPolygons _anchors;
class BridgeDirection {
public:
BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {}
// the best direction is the one causing most lines to be bridged (thus most coverage)
bool operator<(const BridgeDirection &other) const {
// Initial sort by coverage only - comparator must obey strict weak ordering
return this->coverage > other.coverage;
};
double angle;
double coverage;
double max_length;
};
};
}

View File

@ -2,6 +2,7 @@
#define slic3r_ExPolygon_hpp_
#include "libslic3r.h"
#include "BoundingBox.hpp"
#include "Polygon.hpp"
#include "Polyline.hpp"
#include <ostream>
@ -30,6 +31,7 @@ class ExPolygon
bool contains(const Point &point) const;
bool contains_b(const Point &point) const;
bool has_boundary_point(const Point &point) const;
BoundingBox bounding_box() const { return this->contour.bounding_box(); };
void remove_vertical_collinear_points(coord_t tolerance);
void simplify_p(double tolerance, Polygons* polygons) const;
Polygons simplify_p(double tolerance) const;

View File

@ -142,7 +142,6 @@ class ExtrusionLoop : public ExtrusionEntity
void split_at(const Point &point, bool prefer_non_overhang = false);
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
bool has_overhang_point(const Point &point) const;
bool is_perimeter() const {
return this->paths.front().role == erPerimeter

View File

@ -1,3 +1,6 @@
#define DEBUG
#undef NDEBUG
#include <cassert>
#include <math.h>
#include <stdio.h>
@ -69,36 +72,6 @@ Fill::fill_surface(const Surface &surface)
return polylines_out;
}
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase.
coord_t
Fill::adjust_solid_spacing(const coord_t width, const coord_t distance)
{
assert(width >= 0);
assert(distance > 0);
const int number_of_intervals = floor(width / distance);
if (number_of_intervals == 0) return distance;
coord_t distance_new = (width / number_of_intervals);
const coordf_t factor = coordf_t(distance_new) / coordf_t(distance);
assert(factor > 1. - 1e-5);
// How much could the extrusion width be increased? By 20%.
// Because of this limit, this method is not idempotent: each run
// will increment distance by 20%.
const coordf_t factor_max = 1.2;
if (factor > factor_max)
distance_new = floor((double)distance * factor_max + 0.5);
assert((distance_new * number_of_intervals) <= width);
return distance_new;
}
// Returns orientation of the infill and the reference point of the infill pattern.
// For a normal print, the reference point is the center of a bounding box of the STL.
Fill::direction_t

View File

@ -64,7 +64,6 @@ public:
public:
static Fill* new_from_type(const InfillPattern type);
static Fill* new_from_type(const std::string &type);
static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance);
virtual Fill* clone() const = 0;
virtual ~Fill() {};

View File

@ -1,5 +1,6 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Flow.hpp"
#include "../Surface.hpp"
#include "FillConcentric.hpp"
@ -20,7 +21,7 @@ FillConcentric::_fill_surface_single(
if (this->density > 0.9999f && !this->dont_adjust) {
BoundingBox bounding_box = expolygon.contour.bounding_box();
distance = this->adjust_solid_spacing(bounding_box.size().x, distance);
distance = Flow::solid_spacing(bounding_box.size().x, distance);
this->_spacing = unscale(distance);
}

View File

@ -6,6 +6,7 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Flow.hpp"
#include "../PolylineCollection.hpp"
#include "../Surface.hpp"
#include <algorithm>
@ -48,7 +49,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon,
// define flow spacing according to requested density
if (this->density > 0.9999f && !this->dont_adjust) {
line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing);
line_spacing = Flow::solid_spacing(bounding_box.size().x, line_spacing);
this->_spacing = unscale(line_spacing);
} else {
// extend bounding box so that our pattern will be aligned with other layers
@ -339,7 +340,12 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon,
// Get the first lower point.
vertical_t::iterator it = v.begin(); // minimum x,y
IntersectionPoint p = it->second;
assert(p.type == IntersectionPoint::ipTypeLower);
if (p.type != IntersectionPoint::ipTypeLower) {
// Degenerate polygon, this shouldn't happen.
// We used to have an assert here, but let's be tolerant.
grid.erase(p.x);
continue;
}
// Start our polyline.
Polyline polyline;
@ -350,17 +356,32 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon,
// Complete the vertical line by finding the corresponding upper or lower point.
if (p.type == IntersectionPoint::ipTypeUpper) {
// find first point along c.x with y < c.y
assert(it != grid[p.x].begin());
if (it == grid[p.x].begin()) {
// Degenerate polygon, this shouldn't happen.
// We used to have an assert here, but let's be tolerant.
grid.erase(p.x);
break;
}
--it;
} else {
// find first point along c.x with y > c.y
++it;
assert(it != grid[p.x].end());
if (it == grid[p.x].end()) {
// Degenerate polygon, this shouldn't happen.
// We used to have an assert here, but let's be tolerant.
grid.erase(p.x);
break;
}
}
// Append the point to our polyline.
IntersectionPoint b = it->second;
assert(b.type != p.type);
if (b.type == p.type) {
// Degenerate polygon, this shouldn't happen.
// We used to have an assert here, but let's be tolerant.
grid.erase(p.x);
break;
}
polyline.append(b);
polyline.points.back().y += this->endpoints_overlap * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1);
@ -408,8 +429,12 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon,
// If the connection brought us to another x coordinate, we expect the point
// type to be the same.
assert((p.type == b.type && p.x > b.x)
|| (p.type != b.type && p.x == b.x));
if (!(p.type == b.type && p.x > b.x) && !(p.type != b.type && p.x == b.x)) {
// Degenerate polygon, this shouldn't happen.
// We used to have an assert here, but let's be tolerant.
grid.erase(p.x);
break;
}
}
// Yay, we have a polyline!

View File

@ -50,6 +50,11 @@ Flow::spacing() const {
return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing);
}
void
Flow::set_spacing(float spacing) {
this->width = Flow::_width_from_spacing(spacing, this->nozzle_diameter, this->height, this->bridge);
}
/* This method returns the centerline spacing between an extrusion using this
flow and another one using another flow.
this->spacing(other) shall return the same value as other.spacing(*this) */
@ -115,4 +120,37 @@ Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bo
return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0);
}
// Calculate a new spacing to fill width with possibly integer number of lines,
// the first and last line being centered at the interval ends.
// This function possibly increases the spacing, never decreases,
// and for a narrow width the increase in spacing may become severe,
// therefore the adjustment is limited to 20% increase.
template <class T>
T
Flow::solid_spacing(const T total_width, const T spacing)
{
assert(total_width >= 0);
assert(spacing > 0);
const int number_of_intervals = floor(total_width / spacing);
if (number_of_intervals == 0) return spacing;
T spacing_new = (total_width / number_of_intervals);
const double factor = (double)spacing_new / (double)spacing;
assert(factor > 1. - 1e-5);
// How much could the extrusion width be increased? By 20%.
// Because of this limit, this method is not idempotent: each run
// will increment spacing by 20%.
const double factor_max = 1.2;
if (factor > factor_max)
spacing_new = floor((double)spacing * factor_max + 0.5);
assert((spacing_new * number_of_intervals) <= total_width);
return spacing_new;
}
template coord_t Flow::solid_spacing<coord_t>(const coord_t total_width, const coord_t spacing);
template coordf_t Flow::solid_spacing<coordf_t>(const coordf_t total_width, const coordf_t spacing);
}

View File

@ -30,6 +30,13 @@ class Flow
: width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {};
float spacing() const;
float spacing(const Flow &other) const;
void set_spacing(float spacing);
void set_solid_spacing(const coord_t total_width) {
this->set_spacing(Flow::solid_spacing(total_width, this->scaled_spacing()));
};
void set_solid_spacing(const coordf_t total_width) {
this->set_spacing(Flow::solid_spacing(total_width, (coordf_t)this->spacing()));
};
double mm3_per_mm() const;
coord_t scaled_width() const {
return scale_(this->width);
@ -43,12 +50,12 @@ class Flow
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
template <class T> static T solid_spacing(const T total_width, const T spacing);
private:
static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio);
static float _auto_width(FlowRole role, float nozzle_diameter, float height);
static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio);
};
}

View File

@ -296,13 +296,27 @@ template<class T>
bool
contains(const std::vector<T> &vector, const Point &point)
{
for (typename std::vector<T>::const_iterator it = vector.begin(); it != vector.end(); ++it) {
if (it->contains(point)) return true;
}
for (const T &it : vector)
if (it.contains(point))
return true;
return false;
}
template bool contains(const Polygons &vector, const Point &point);
template bool contains(const ExPolygons &vector, const Point &point);
template<class T>
double
area(const std::vector<T> &vector)
{
double area = 0;
for (const T &it : vector)
area += it.area();
return area;
}
template double area(const Polygons &vector);
double
rad2deg(double angle)
{

View File

@ -20,6 +20,7 @@ void chained_path(const Points &points, std::vector<Points::size_type> &retval);
template<class T> void chained_path_items(Points &points, T &items, T &retval);
bool directions_parallel(double angle1, double angle2, double max_diff = 0);
template<class T> bool contains(const std::vector<T> &vector, const Point &point);
template<class T> double area(const std::vector<T> &vector);
double rad2deg(double angle);
double rad2deg_dir(double angle);
double deg2rad(double angle);

View File

@ -1,6 +1,7 @@
#include "Layer.hpp"
#include "BridgeDetector.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
#include "PerimeterGenerator.hpp"
#include "Print.hpp"
#include "Surface.hpp"
@ -65,34 +66,63 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection*
g.process();
}
// This function reads lower_layer->slices and writes this->bridged and this->fill_surfaces,
// so it's thread-safe.
// This function reads layer->slices and lower_layer->slices
// and writes this->bridged and this->fill_surfaces, so it's thread-safe.
void
LayerRegion::process_external_surfaces()
{
const Surfaces &surfaces = this->fill_surfaces.surfaces;
const double margin = scale_(EXTERNAL_INFILL_MARGIN);
Surfaces &surfaces = this->fill_surfaces.surfaces;
for (size_t j = 0; j < surfaces.size(); ++j) {
Surface &surface = surfaces[j];
if (this->layer()->lower_layer != NULL && surface.is_bridge()) {
// If this bridge has one or more holes that are internal surfaces
// (thus not visible from the outside), like a slab sustained by
// pillars, include them in the bridge in order to have better and
// more continuous bridging.
Polygons &holes = surface.expolygon.holes;
for (int i = 0; i < holes.size(); ++i) {
// reverse the hole and consider it a polygon
Polygon h = holes[i];
h.reverse();
// Is this hole fully contained in the layer slices?
if (diff(h, this->layer()->slices).empty()) {
// remove any other surface contained in this hole
for (int k = 0; k < surfaces.size(); ++k) {
if (k == j) continue;
if (h.contains(surfaces[k].expolygon.contour.first_point())) {
surfaces.erase(surfaces.begin() + k);
--k;
}
}
holes.erase(holes.begin() + i);
--i;
}
}
}
}
SurfaceCollection bottom;
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
if (!surface->is_bottom()) continue;
const ExPolygons grown = offset_ex(surface->expolygon, +margin);
for (const Surface &surface : surfaces) {
if (!surface.is_bottom()) continue;
/* detect bridge direction before merging grown surfaces otherwise adjacent bridges
would get merged into a single one while they need different directions
also, supply the original expolygon instead of the grown one, because in case
of very thin (but still working) anchors, the grown expolygon would go beyond them */
double angle = -1;
if (this->layer()->lower_layer != NULL && surface->is_bridge()) {
if (this->layer()->lower_layer != NULL && surface.is_bridge()) {
BridgeDetector bd(
surface->expolygon,
surface.expolygon,
this->layer()->lower_layer->slices,
this->flow(frInfill, true).scaled_width()
);
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer %zu:\n", this->layer()->id());
printf("Processing bridge at layer %zu (z = %f):\n", this->layer()->id(), this->layer()->print_z);
#endif
if (bd.detect_angle()) {
@ -105,28 +135,22 @@ LayerRegion::process_external_surfaces()
}
}
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
Surface s = *surface;
s.expolygon = *it;
s.bridge_angle = angle;
bottom.surfaces.push_back(s);
}
const ExPolygons grown = offset_ex(surface.expolygon, +SCALED_EXTERNAL_INFILL_MARGIN);
Surface templ = surface;
templ.bridge_angle = angle;
bottom.append(grown, templ);
}
SurfaceCollection top;
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
if (surface->surface_type != stTop) continue;
for (const Surface &surface : surfaces) {
if (surface.surface_type != stTop) continue;
// give priority to bottom surfaces
ExPolygons grown = diff_ex(
offset(surface->expolygon, +margin),
offset(surface.expolygon, +SCALED_EXTERNAL_INFILL_MARGIN),
(Polygons)bottom
);
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
Surface s = *surface;
s.expolygon = *it;
top.surfaces.push_back(s);
}
top.append(grown, surface);
}
/* if we're slicing with no infill, we can't extend external surfaces
@ -135,10 +159,9 @@ LayerRegion::process_external_surfaces()
if (this->region()->config.fill_density.value > 0) {
fill_boundaries = SurfaceCollection(surfaces);
} else {
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) {
if (it->surface_type != stInternal)
fill_boundaries.surfaces.push_back(*it);
}
for (const Surface &s : surfaces)
if (s.surface_type != stInternal)
fill_boundaries.surfaces.push_back(s);
}
// intersect the grown surfaces with the actual fill boundaries
@ -152,10 +175,10 @@ LayerRegion::process_external_surfaces()
std::vector<SurfacesConstPtr> groups;
tb.group(&groups);
for (std::vector<SurfacesConstPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
for (const SurfacesConstPtr &g : groups) {
Polygons subject;
for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s)
append_to(subject, (Polygons)**s);
for (const Surface* s : g)
append_to(subject, (Polygons)*s);
ExPolygons expp = intersection_ex(
subject,
@ -163,45 +186,36 @@ LayerRegion::process_external_surfaces()
true // to ensure adjacent expolygons are unified
);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = *g->front();
s.expolygon = *ex;
new_surfaces.surfaces.push_back(s);
}
new_surfaces.append(expp, *g.front());
}
}
/* subtract the new top surfaces from the other non-top surfaces and re-add them */
{
SurfaceCollection other;
for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
if (s->surface_type != stTop && !s->is_bottom())
other.surfaces.push_back(*s);
}
for (const Surface &s : surfaces)
if (s.surface_type != stTop && !s.is_bottom())
other.surfaces.push_back(s);
// group surfaces
std::vector<SurfacesConstPtr> groups;
other.group(&groups);
for (std::vector<SurfacesConstPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
for (const SurfacesConstPtr &g : groups) {
Polygons subject;
for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s)
append_to(subject, (Polygons)**s);
for (const Surface* s : g)
append_to(subject, (Polygons)*s);
ExPolygons expp = diff_ex(
subject,
(Polygons)new_surfaces
);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = *g->front();
s.expolygon = *ex;
new_surfaces.surfaces.push_back(s);
}
new_surfaces.append(expp, *g.front());
}
}
this->fill_surfaces = new_surfaces;
this->fill_surfaces = std::move(new_surfaces);
}
void
@ -234,7 +248,8 @@ LayerRegion::prepare_fill_surfaces()
const float &fill_density = this->region()->config.fill_density;
if (fill_density > 0 && fill_density < 100) {
// scaling an area requires two calls!
const double min_area = scale_(scale_(this->region()->config.solid_infill_below_area.value));
// (we don't use scale_() because it would overflow the coord_t range
const double min_area = this->region()->config.solid_infill_below_area.value / SCALING_FACTOR / SCALING_FACTOR;
for (Surface &surface : this->fill_surfaces.surfaces) {
if (surface.surface_type == stInternal && surface.area() <= min_area)
surface.surface_type = stInternalSolid;

View File

@ -753,18 +753,20 @@ ModelObject::mirror(const Axis &axis)
}
void
ModelObject::transform_by_instance(const ModelInstance &instance, bool dont_translate)
ModelObject::transform_by_instance(ModelInstance instance, bool dont_translate)
{
// We get instance by copy because we would alter it in the loop below,
// causing inconsistent values in subsequent instances.
this->rotate(instance.rotation, Z);
this->scale(instance.scaling_factor);
if (!dont_translate)
this->translate(instance.offset.x, instance.offset.y, 0);
for (ModelInstancePtrs::iterator i = this->instances.begin(); i != this->instances.end(); ++i) {
(*i)->rotation -= instance.rotation;
(*i)->scaling_factor /= instance.scaling_factor;
for (ModelInstance* i : this->instances) {
i->rotation -= instance.rotation;
i->scaling_factor /= instance.scaling_factor;
if (!dont_translate)
(*i)->offset.translate(-instance.offset.x, -instance.offset.y);
i->offset.translate(-instance.offset.x, -instance.offset.y);
}
this->origin_translation = Pointf3(0,0,0);
this->invalidate_bounding_box();

View File

@ -163,7 +163,7 @@ class ModelObject
void scale_to_fit(const Sizef3 &size);
void rotate(float angle, const Axis &axis);
void mirror(const Axis &axis);
void transform_by_instance(const ModelInstance &instance, bool dont_translate = false);
void transform_by_instance(ModelInstance instance, bool dont_translate = false);
size_t materials_count() const;
size_t facets_count() const;
bool needed_repair() const;

View File

@ -163,6 +163,10 @@ Print::invalidate_state_by_config(const PrintConfigBase &config)
|| opt_key == "min_skirt_length"
|| opt_key == "ooze_prevention") {
steps.insert(psSkirt);
} else if (opt_key == "brim_width") {
steps.insert(psBrim);
steps.insert(psSkirt);
osteps.insert(posSupportMaterial);
} else if (opt_key == "brim_width"
|| opt_key == "interior_brim_width"
|| opt_key == "brim_connections_width") {
@ -176,6 +180,7 @@ Print::invalidate_state_by_config(const PrintConfigBase &config)
} else if (opt_key == "avoid_crossing_perimeters"
|| opt_key == "bed_shape"
|| opt_key == "bed_temperature"
|| opt_key == "between_objects_gcode"
|| opt_key == "bridge_acceleration"
|| opt_key == "bridge_fan_speed"
|| opt_key == "complete_objects"
@ -760,13 +765,18 @@ Print::brim_flow() const
extruders and take the one with, say, the smallest index.
The same logic should be applied to the code that selects the extruder during G-code
generation as well. */
return Flow::new_from_config_width(
Flow flow = Flow::new_from_config_width(
frPerimeter,
width,
this->config.nozzle_diameter.get_at(this->regions.front()->config.perimeter_extruder-1),
this->skirt_first_layer_height(),
0
);
// Adjust extrusion width in order to fill the total brim width with an integer number of lines.
flow.set_solid_spacing(this->config.brim_width.value);
return flow;
}
Flow
@ -814,23 +824,21 @@ Print::_make_brim()
const coord_t grow_distance = flow.scaled_width()/2;
Polygons islands;
FOREACH_OBJECT(this, object) {
const Layer &layer0 = *(*object)->get_layer(0);
for (PrintObject* object : this->objects) {
const Layer* layer0 = object->get_layer(0);
Polygons object_islands = layer0.slices.contours();
Polygons object_islands = layer0->slices.contours();
if (!(*object)->support_layers.empty()) {
const SupportLayer &support_layer0 = *(*object)->get_support_layer(0);
if (!object->support_layers.empty()) {
const SupportLayer* support_layer0 = object->get_support_layer(0);
for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_fills.entities.begin();
it != support_layer0.support_fills.entities.end(); ++it)
append_to(object_islands, offset((*it)->as_polyline(), grow_distance));
for (const ExtrusionEntity* e : support_layer0->support_fills.entities)
append_to(object_islands, offset(e->as_polyline(), grow_distance));
for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_interface_fills.entities.begin();
it != support_layer0.support_interface_fills.entities.end(); ++it)
append_to(object_islands, offset((*it)->as_polyline(), grow_distance));
for (const ExtrusionEntity* e : support_layer0->support_interface_fills.entities)
append_to(object_islands, offset(e->as_polyline(), grow_distance));
}
for (const Point &copy : (*object)->_shifted_copies) {
for (const Point &copy : object->_shifted_copies) {
for (Polygon p : object_islands) {
p.translate(copy);
islands.push_back(p);
@ -847,8 +855,8 @@ Print::_make_brim()
// perimeters because here we're offsetting outwards)
append_to(loops, offset2(
islands,
flow.scaled_width() + flow.scaled_spacing() * (i - 1.0 + 0.5),
flow.scaled_spacing() * -1.0,
flow.scaled_width() + flow.scaled_spacing() * (i - 1.5 + 0.5),
flow.scaled_spacing() * -0.5,
100000,
ClipperLib::jtSquare
));

View File

@ -81,6 +81,15 @@ PrintConfigDef::PrintConfigDef()
def->height = 50;
def->default_value = new ConfigOptionString("");
def = this->add("between_objects_gcode", coString);
def->label = "Between objects G-code";
def->tooltip = "This code is inserted between objects when using sequential printing. By default extruder and bed temperature are reset using non-wait command; however if M104, M109, M140 or M190 are detected in this custom code, Slic3r will not add temperature commands. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want.";
def->cli = "between-objects-gcode=s";
def->multiline = true;
def->full_width = true;
def->height = 120;
def->default_value = new ConfigOptionString("");
def = this->add("bottom_infill_pattern", external_fill_pattern);
def->label = "Bottom";
def->full_label = "Bottom infill pattern";
@ -786,7 +795,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("max_volumetric_speed", coFloat);
def->label = "Max volumetric speed";
def->category = "Speed";
def->tooltip = "This experimental setting is used to set the maximum volumetric speed your extruder supports.";
def->tooltip = "If set to a non-zero value, extrusion will be limited to this volumetric speed. You may want to set it to your extruder maximum. As a hint, you can read calculated volumetric speeds in the comments of any G-code file you export from Slic3r.";
def->sidetext = "mm³/s";
def->cli = "max-volumetric-speed=f";
def->min = 0;
@ -961,6 +970,19 @@ PrintConfigDef::PrintConfigDef()
def->height = 60;
def->default_value = new ConfigOptionStrings();
def = this->add("printer_notes", coStrings);
def->label = "Printer notes";
def->tooltip = "You can put your notes regarding the printer here.";
def->cli = "printer-notes=s@";
def->multiline = true;
def->full_width = true;
def->height = 130;
{
ConfigOptionStrings* opt = new ConfigOptionStrings();
opt->values.push_back("");
def->default_value = opt;
}
def = this->add("print_settings_id", coString);
def->default_value = new ConfigOptionString("");
@ -1623,7 +1645,7 @@ PrintConfigDef::PrintConfigDef()
def->default_value = new ConfigOptionFloat(0);
}
PrintConfigDef print_config_def;
const PrintConfigDef print_config_def;
void
DynamicPrintConfig::normalize() {
@ -1856,6 +1878,6 @@ CLIConfigDef::CLIConfigDef()
def->default_value = new ConfigOptionPoint3(Pointf3(0,0,0));
}
CLIConfigDef cli_config_def;
const CLIConfigDef cli_config_def;
}

View File

@ -103,7 +103,7 @@ class PrintConfigDef : public ConfigDef
// The one and only global definition of SLic3r configuration options.
// This definition is constant.
extern PrintConfigDef print_config_def;
extern const PrintConfigDef print_config_def;
// Slic3r configuration storage with print_config_def assigned.
class PrintConfigBase : public virtual ConfigBase
@ -298,6 +298,7 @@ class GCodeConfig : public virtual StaticPrintConfig
{
public:
ConfigOptionString before_layer_gcode;
ConfigOptionString between_objects_gcode;
ConfigOptionString end_gcode;
ConfigOptionStrings end_filament_gcode;
ConfigOptionString extrusion_axis;
@ -335,6 +336,7 @@ class GCodeConfig : public virtual StaticPrintConfig
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(before_layer_gcode);
OPT_PTR(between_objects_gcode);
OPT_PTR(end_gcode);
OPT_PTR(end_filament_gcode);
OPT_PTR(extrusion_axis);
@ -426,6 +428,7 @@ class PrintConfig : public GCodeConfig
ConfigOptionString output_filename_format;
ConfigOptionFloat perimeter_acceleration;
ConfigOptionStrings post_process;
ConfigOptionStrings printer_notes;
ConfigOptionFloat resolution;
ConfigOptionFloats retract_before_travel;
ConfigOptionBools retract_layer_change;
@ -490,6 +493,7 @@ class PrintConfig : public GCodeConfig
OPT_PTR(output_filename_format);
OPT_PTR(perimeter_acceleration);
OPT_PTR(post_process);
OPT_PTR(printer_notes);
OPT_PTR(resolution);
OPT_PTR(retract_before_travel);
OPT_PTR(retract_layer_change);
@ -580,18 +584,6 @@ class SLAPrintConfig
ConfigOptionFloat support_material_spacing;
ConfigOptionInt threads;
SLAPrintConfig() : StaticPrintConfig() {
this->set_defaults();
// override some defaults
this->fill_density.value = 100;
this->fill_pattern.value = ipGrid;
this->infill_extrusion_width.value = 0.5;
this->infill_extrusion_width.percent = false;
this->perimeter_extrusion_width.value = 1;
this->perimeter_extrusion_width.percent = false;
};
virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) {
OPT_PTR(fill_angle);
OPT_PTR(fill_density);
@ -617,7 +609,7 @@ class CLIConfigDef : public ConfigDef
CLIConfigDef();
};
extern CLIConfigDef cli_config_def;
extern const CLIConfigDef cli_config_def;
class CLIConfig
: public virtual ConfigBase, public StaticConfig

View File

@ -85,11 +85,19 @@ PrintRegion::invalidate_state_by_config(const PrintConfigBase &config)
steps.insert(posPerimeters);
} else if (opt_key == "first_layer_extrusion_width") {
steps.insert(posSupportMaterial);
} else if (opt_key == "solid_infill_below_area") {
const float &cur_value = config.opt<ConfigOptionFloat>(opt_key)->value;
const float &new_value = this->config.solid_infill_below_area.value;
if (new_value >= cur_value) {
steps.insert(posPrepareInfill);
} else {
// prepare_infill is not idempotent when solid_infill_below_area is reduced
steps.insert(posPerimeters);
}
} else if (opt_key == "infill_every_layers"
|| opt_key == "solid_infill_every_layers"
|| opt_key == "bottom_solid_layers"
|| opt_key == "top_solid_layers"
|| opt_key == "solid_infill_below_area"
|| opt_key == "infill_extruder"
|| opt_key == "solid_infill_extruder"
|| opt_key == "infill_extrusion_width") {
@ -108,10 +116,10 @@ PrintRegion::invalidate_state_by_config(const PrintConfigBase &config)
} else if (opt_key == "fill_density") {
const float &cur_value = config.opt<ConfigOptionFloat>("fill_density")->value;
const float &new_value = this->config.fill_density.value;
if ((cur_value == 0) != (new_value == 0))
if ((cur_value == 0) != (new_value == 0) || (cur_value == 100) != (new_value == 100))
steps.insert(posPerimeters);
steps.insert(posPrepareInfill);
steps.insert(posInfill);
} else if (opt_key == "external_perimeter_extrusion_width"
|| opt_key == "perimeter_extruder") {
steps.insert(posPerimeters);

View File

@ -35,6 +35,11 @@ SVG::SVG(const char* filename, const BoundingBox &bbox)
h, w);
}
SVG::~SVG()
{
this->Close();
}
void
SVG::draw(const Line &line, std::string stroke, coord_t stroke_width)
{
@ -68,10 +73,10 @@ void SVG::draw(const ThickLine &line, const std::string &fill, const std::string
}
void
SVG::draw(const Lines &lines, std::string stroke)
SVG::draw(const Lines &lines, std::string stroke, coord_t stroke_width)
{
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
this->draw(*it, stroke);
this->draw(*it, stroke, stroke_width);
}
void
@ -87,18 +92,30 @@ SVG::draw(const ExPolygon &expolygon, std::string fill)
this->fill = fill;
std::string d;
Polygons pp = expolygon;
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) {
d += this->get_path_d(*p, true) + " ";
}
for (const Polygon &p : (Polygons)expolygon)
d += this->get_path_d(p, true) + " ";
this->path(d, true);
}
void
SVG::draw(const ExPolygonCollection &coll, std::string fill)
{
this->draw(coll.expolygons, fill);
}
void
SVG::draw(const SurfaceCollection &coll, std::string fill)
{
for (const Surface &s : coll.surfaces)
this->draw(s.expolygon, fill);
}
void
SVG::draw(const ExPolygons &expolygons, std::string fill)
{
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it)
this->draw(*it, fill);
for (const ExPolygon &e : expolygons)
this->draw(e, fill);
}
void
@ -126,7 +143,7 @@ void
SVG::draw(const Polylines &polylines, std::string stroke, coord_t stroke_width)
{
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw(*it, fill, stroke_width);
this->draw(*it, stroke, stroke_width);
}
void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coord_t stroke_width)
@ -202,9 +219,12 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const
void
SVG::Close()
{
if (this->f != NULL) {
fprintf(this->f, "</svg>\n");
fclose(this->f);
this->f = NULL;
printf("SVG written to %s\n", this->filename.c_str());
}
}
}

View File

@ -3,7 +3,9 @@
#include "libslic3r.h"
#include "ExPolygon.hpp"
#include "ExPolygonCollection.hpp"
#include "Line.hpp"
#include "SurfaceCollection.hpp"
#include "TriangleMesh.hpp"
namespace Slic3r {
@ -17,12 +19,15 @@ class SVG
SVG(const char* filename);
SVG(const char* filename, const BoundingBox &bbox);
~SVG();
void draw(const Line &line, std::string stroke = "black", coord_t stroke_width = 0);
void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width = 0);
void draw(const Lines &lines, std::string stroke = "black");
void draw(const Lines &lines, std::string stroke = "black", coord_t stroke_width = 0);
void draw(const IntersectionLines &lines, std::string stroke = "black");
void draw(const ExPolygon &expolygon, std::string fill = "grey");
void draw(const ExPolygons &expolygons, std::string fill = "grey");
void draw(const ExPolygonCollection &coll, std::string fill = "grey");
void draw(const SurfaceCollection &coll, std::string fill = "grey");
void draw(const Polygon &polygon, std::string fill = "grey");
void draw(const Polygons &polygons, std::string fill = "grey");
void draw(const Polyline &polyline, std::string stroke = "black", coord_t stroke_width = 0);

View File

@ -41,6 +41,7 @@ class SurfaceCollection
bool empty() const { return this->surfaces.empty(); };
size_t size() const { return this->surfaces.size(); };
void clear() { this->surfaces.clear(); };
void erase(size_t i) { this->surfaces.erase(this->surfaces.begin() + i); };
};
}

View File

@ -511,6 +511,13 @@ TriangleMesh::require_shared_vertices()
if (this->stl.v_shared == NULL) stl_generate_shared_vertices(&(this->stl));
}
void
TriangleMesh::reverse_normals()
{
stl_reverse_all_facets(&this->stl);
if (this->stl.stats.volume != -1) this->stl.stats.volume *= -1.0;
}
void
TriangleMesh::extrude_tin(float offset)
{

View File

@ -59,6 +59,7 @@ class TriangleMesh
size_t facets_count() const;
void extrude_tin(float offset);
void require_shared_vertices();
void reverse_normals();
static TriangleMesh make_cube(double x, double y, double z);
static TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360));

View File

@ -64,6 +64,7 @@ constexpr auto LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER = 0.15;
constexpr coord_t SMALL_PERIMETER_LENGTH = scale_(6.5) * 2 * PI;
constexpr coordf_t INSET_OVERLAP_TOLERANCE = 0.4;
constexpr coordf_t EXTERNAL_INFILL_MARGIN = 3;
constexpr coord_t SCALED_EXTERNAL_INFILL_MARGIN = scale_(EXTERNAL_INFILL_MARGIN);
enum Axis { X=0, Y, Z };

View File

@ -110,92 +110,90 @@ PROTOTYPES: DISABLE
SV*
print_config_def()
CODE:
t_optiondef_map &def = Slic3r::print_config_def.options;
HV* options_hv = newHV();
for (t_optiondef_map::iterator oit = def.begin(); oit != def.end(); ++oit) {
for (const auto &oit : Slic3r::print_config_def.options) {
HV* hv = newHV();
t_config_option_key opt_key = oit->first;
ConfigOptionDef* optdef = &oit->second;
const t_config_option_key &opt_key = oit.first;
const ConfigOptionDef &optdef = oit.second;
const char* opt_type;
if (optdef->type == coFloat || optdef->type == coFloats || optdef->type == coFloatOrPercent) {
if (optdef.type == coFloat || optdef.type == coFloats || optdef.type == coFloatOrPercent) {
opt_type = "f";
} else if (optdef->type == coPercent) {
} else if (optdef.type == coPercent) {
opt_type = "percent";
} else if (optdef->type == coInt || optdef->type == coInts) {
} else if (optdef.type == coInt || optdef.type == coInts) {
opt_type = "i";
} else if (optdef->type == coString) {
} else if (optdef.type == coString) {
opt_type = "s";
} else if (optdef->type == coStrings) {
} else if (optdef.type == coStrings) {
opt_type = "s@";
} else if (optdef->type == coPoint || optdef->type == coPoint3 || optdef->type == coPoints) {
} else if (optdef.type == coPoint || optdef.type == coPoint3 || optdef.type == coPoints) {
opt_type = "point";
} else if (optdef->type == coBool || optdef->type == coBools) {
} else if (optdef.type == coBool || optdef.type == coBools) {
opt_type = "bool";
} else if (optdef->type == coEnum) {
} else if (optdef.type == coEnum) {
opt_type = "select";
} else {
throw "Unknown option type";
}
(void)hv_stores( hv, "type", newSVpv(opt_type, 0) );
(void)hv_stores( hv, "gui_type", newSVpvn(optdef->gui_type.c_str(), optdef->gui_type.length()) );
(void)hv_stores( hv, "gui_flags", newSVpvn(optdef->gui_flags.c_str(), optdef->gui_flags.length()) );
(void)hv_stores( hv, "label", newSVpvn_utf8(optdef->label.c_str(), optdef->label.length(), true) );
if (!optdef->full_label.empty())
(void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef->full_label.c_str(), optdef->full_label.length(), true) );
(void)hv_stores( hv, "category", newSVpvn_utf8(optdef->category.c_str(), optdef->category.length(), true) );
(void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef->tooltip.c_str(), optdef->tooltip.length(), true) );
(void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef->sidetext.c_str(), optdef->sidetext.length(), true) );
(void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) );
(void)hv_stores( hv, "ratio_over", newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) );
(void)hv_stores( hv, "multiline", newSViv(optdef->multiline ? 1 : 0) );
(void)hv_stores( hv, "full_width", newSViv(optdef->full_width ? 1 : 0) );
(void)hv_stores( hv, "readonly", newSViv(optdef->readonly ? 1 : 0) );
(void)hv_stores( hv, "height", newSViv(optdef->height) );
(void)hv_stores( hv, "width", newSViv(optdef->width) );
(void)hv_stores( hv, "min", newSViv(optdef->min) );
(void)hv_stores( hv, "max", newSViv(optdef->max) );
(void)hv_stores( hv, "gui_type", newSVpvn(optdef.gui_type.c_str(), optdef.gui_type.length()) );
(void)hv_stores( hv, "gui_flags", newSVpvn(optdef.gui_flags.c_str(), optdef.gui_flags.length()) );
(void)hv_stores( hv, "label", newSVpvn_utf8(optdef.label.c_str(), optdef.label.length(), true) );
if (!optdef.full_label.empty())
(void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef.full_label.c_str(), optdef.full_label.length(), true) );
(void)hv_stores( hv, "category", newSVpvn_utf8(optdef.category.c_str(), optdef.category.length(), true) );
(void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef.tooltip.c_str(), optdef.tooltip.length(), true) );
(void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef.sidetext.c_str(), optdef.sidetext.length(), true) );
(void)hv_stores( hv, "cli", newSVpvn(optdef.cli.c_str(), optdef.cli.length()) );
(void)hv_stores( hv, "ratio_over", newSVpvn(optdef.ratio_over.c_str(), optdef.ratio_over.length()) );
(void)hv_stores( hv, "multiline", newSViv(optdef.multiline ? 1 : 0) );
(void)hv_stores( hv, "full_width", newSViv(optdef.full_width ? 1 : 0) );
(void)hv_stores( hv, "readonly", newSViv(optdef.readonly ? 1 : 0) );
(void)hv_stores( hv, "height", newSViv(optdef.height) );
(void)hv_stores( hv, "width", newSViv(optdef.width) );
(void)hv_stores( hv, "min", newSViv(optdef.min) );
(void)hv_stores( hv, "max", newSViv(optdef.max) );
// aliases
if (!optdef->aliases.empty()) {
if (!optdef.aliases.empty()) {
AV* av = newAV();
av_fill(av, optdef->aliases.size()-1);
for (std::vector<t_config_option_key>::iterator it = optdef->aliases.begin(); it != optdef->aliases.end(); ++it)
av_store(av, it - optdef->aliases.begin(), newSVpvn(it->c_str(), it->length()));
av_fill(av, optdef.aliases.size()-1);
for (std::vector<t_config_option_key>::const_iterator it = optdef.aliases.begin(); it != optdef.aliases.end(); ++it)
av_store(av, it - optdef.aliases.begin(), newSVpvn(it->c_str(), it->length()));
(void)hv_stores( hv, "aliases", newRV_noinc((SV*)av) );
}
// shortcut
if (!optdef->shortcut.empty()) {
if (!optdef.shortcut.empty()) {
AV* av = newAV();
av_fill(av, optdef->shortcut.size()-1);
for (std::vector<t_config_option_key>::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it)
av_store(av, it - optdef->shortcut.begin(), newSVpvn(it->c_str(), it->length()));
av_fill(av, optdef.shortcut.size()-1);
for (std::vector<t_config_option_key>::const_iterator it = optdef.shortcut.begin(); it != optdef.shortcut.end(); ++it)
av_store(av, it - optdef.shortcut.begin(), newSVpvn(it->c_str(), it->length()));
(void)hv_stores( hv, "shortcut", newRV_noinc((SV*)av) );
}
// enum_values
if (!optdef->enum_values.empty()) {
if (!optdef.enum_values.empty()) {
AV* av = newAV();
av_fill(av, optdef->enum_values.size()-1);
for (std::vector<std::string>::iterator it = optdef->enum_values.begin(); it != optdef->enum_values.end(); ++it)
av_store(av, it - optdef->enum_values.begin(), newSVpvn(it->c_str(), it->length()));
av_fill(av, optdef.enum_values.size()-1);
for (std::vector<std::string>::const_iterator it = optdef.enum_values.begin(); it != optdef.enum_values.end(); ++it)
av_store(av, it - optdef.enum_values.begin(), newSVpvn(it->c_str(), it->length()));
(void)hv_stores( hv, "values", newRV_noinc((SV*)av) );
}
// enum_labels
if (!optdef->enum_labels.empty()) {
if (!optdef.enum_labels.empty()) {
AV* av = newAV();
av_fill(av, optdef->enum_labels.size()-1);
for (std::vector<std::string>::iterator it = optdef->enum_labels.begin(); it != optdef->enum_labels.end(); ++it)
av_store(av, it - optdef->enum_labels.begin(), newSVpvn_utf8(it->c_str(), it->length(), true));
av_fill(av, optdef.enum_labels.size()-1);
for (std::vector<std::string>::const_iterator it = optdef.enum_labels.begin(); it != optdef.enum_labels.end(); ++it)
av_store(av, it - optdef.enum_labels.begin(), newSVpvn_utf8(it->c_str(), it->length(), true));
(void)hv_stores( hv, "labels", newRV_noinc((SV*)av) );
}
if (optdef->default_value != NULL)
(void)hv_stores( hv, "default", ConfigOption_to_SV(*optdef->default_value, *optdef) );
if (optdef.default_value != NULL)
(void)hv_stores( hv, "default", ConfigOption_to_SV(*optdef.default_value, optdef) );
(void)hv_store( options_hv, opt_key.c_str(), opt_key.length(), newRV_noinc((SV*)hv), 0 );
}

View File

@ -37,7 +37,7 @@ SV*
ExtrusionLoop::arrayref()
CODE:
AV* av = newAV();
av_fill(av, THIS->paths.size()-1);
if (!THIS->paths.empty()) av_extend(av, THIS->paths.size()-1);
for (ExtrusionPaths::iterator it = THIS->paths.begin(); it != THIS->paths.end(); ++it) {
av_store(av, it - THIS->paths.begin(), perl_to_SV_ref(*it));
}

View File

@ -78,8 +78,3 @@ new_from_type(CLASS, type)
%}
};
%package{Slic3r::Filler};
coord_t adjust_solid_spacing(coord_t width, coord_t distance)
%code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %};

View File

@ -81,3 +81,6 @@ _constant()
%}
coord_t solid_spacing(coord_t width, coord_t distance)
%code{% RETVAL = Flow::solid_spacing(width, distance); %};

View File

@ -254,6 +254,15 @@ ModelMaterial::attributes()
%code%{ THIS->material_id(material_id); %};
Ref<ModelMaterial> material();
Clone<BoundingBoxf3> bounding_box()
%code%{
try {
RETVAL = THIS->mesh.bounding_box();
} catch (std::exception& e) {
croak("%s", e.what());
}
%};
Ref<DynamicPrintConfig> config()
%code%{ RETVAL = &THIS->config; %};
Ref<TriangleMesh> mesh()

View File

@ -41,6 +41,7 @@
%code{% RETVAL = THIS->bounding_box().center(); %};
int facets_count();
void reset_repair_stats();
void reverse_normals();
%{