mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-14 03:16:01 +08:00
Merge branch 'master' into adaptive-slicing
This commit is contained in:
commit
57826c9262
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,3 +14,5 @@ xs/assertlib*
|
||||
local-lib
|
||||
package/osx/Slic3r*.app
|
||||
*.dmg
|
||||
*.swp
|
||||
*.swo
|
||||
|
49
.travis.yml
49
.travis.yml
@ -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
|
||||
- master
|
||||
- xsgui
|
||||
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=
|
||||
|
@ -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
45
appveyor.yml
Normal 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:
|
@ -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;
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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';
|
||||
|
||||
|
@ -47,11 +47,11 @@ sub new {
|
||||
'<html>' .
|
||||
'<body bgcolor="#ffffff" link="#808080">' .
|
||||
'<font color="#808080">' .
|
||||
'Copyright © 2011-2016 Alessandro Ranellucci. <br />' .
|
||||
'Copyright © 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>' .
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 (!$Slic3r::GUI::Settings->{_}{background_processing}) {
|
||||
$self->hide_preview if $invalidated;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($invalidated) {
|
||||
if (!$Slic3r::GUI::Settings->{_}{background_processing}) {
|
||||
$self->hide_preview;
|
||||
return;
|
||||
}
|
||||
|
||||
# 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);
|
||||
|
@ -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,11 +158,47 @@ 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) });
|
||||
EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete);
|
||||
|
||||
|
||||
$self->reload_tree;
|
||||
|
||||
return $self;
|
||||
@ -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;
|
||||
|
@ -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';
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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) = @_;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
92
package/common/util.sh
Normal 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
|
||||
}
|
@ -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
26
package/deploy/sftp.sh
Executable 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
|
BIN
package/deploy/slic3r-upload.ppk.enc
Normal file
BIN
package/deploy/slic3r-upload.ppk.enc
Normal file
Binary file not shown.
BIN
package/deploy/slic3r-upload.rsa.enc
Normal file
BIN
package/deploy/slic3r-upload.rsa.enc
Normal file
Binary file not shown.
19
package/deploy/winscp.ps1
Executable file
19
package/deploy/winscp.ps1
Executable 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"
|
||||
}
|
||||
}
|
45
package/linux/appimage-bundler.sh
Executable file
45
package/linux/appimage-bundler.sh
Executable 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/*
|
31
package/linux/build_shell.mk
Normal file
31
package/linux/build_shell.mk
Normal 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*
|
28
package/linux/libpaths.txt
Normal file
28
package/linux/libpaths.txt
Normal 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
131
package/linux/make_archive.sh
Executable 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"
|
@ -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
|
13
package/linux/slic3r.desktop.in
Normal file
13
package/linux/slic3r.desktop.in
Normal 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
|
5
package/linux/startup_script.sh
Normal file
5
package/linux/startup_script.sh
Normal 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" $@
|
7
package/linux/travis-decrypt-key
Normal file
7
package/linux/travis-decrypt-key
Normal 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
30
package/linux/travis-setup.sh
Executable 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
|
@ -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
|
||||
|
@ -93,6 +93,8 @@ cat << EOF >> $plistfile
|
||||
<string>10.7</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
EOF
|
||||
|
21
package/win/appveyor_buildscript.ps1
Normal file
21
package/win/appveyor_buildscript.ps1
Normal 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
|
8
package/win/appveyor_deploy.ps1
Normal file
8
package/win/appveyor_deploy.ps1
Normal 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
|
71
package/win/appveyor_preinstall.ps1
Normal file
71
package/win/appveyor_preinstall.ps1
Normal 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
|
||||
}
|
@ -1 +0,0 @@
|
||||
@perl5.24.0.exe slic3r.pl %*
|
@ -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
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Written by Joseph Lenox
|
||||
# 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" `
|
||||
|
@ -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
92
t/adaptive_width.t
Normal 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__
|
24
t/bridges.t
24
t/bridges.t
@ -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;
|
||||
|
@ -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__
|
||||
|
93
t/dynamic.t
93
t/dynamic.t
@ -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__
|
2
t/fill.t
2
t/fill.t
@ -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';
|
||||
}
|
||||
|
64
t/loops.t
64
t/loops.t
@ -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] ],
|
||||
|
||||
# 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] ],
|
||||
|
||||
# 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] ];
|
||||
# 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');
|
||||
|
||||
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';
|
||||
# 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),
|
||||
);
|
||||
|
||||
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';
|
||||
my $mesh2 = $mesh1->clone;
|
||||
$mesh2->scale(1.2);
|
||||
|
||||
my $mesh3 = $mesh2->clone;
|
||||
$mesh3->scale(1.2);
|
||||
|
||||
$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__
|
||||
|
@ -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
BIN
var/arrow_in.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 600 B |
@ -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,143 +26,129 @@ 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 */
|
||||
|
||||
// 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
|
||||
// generate the list of candidate angles
|
||||
std::vector<BridgeDirection> candidates;
|
||||
{
|
||||
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());
|
||||
// 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
|
||||
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 (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());
|
||||
}
|
||||
}
|
||||
|
||||
/* 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());
|
||||
}
|
||||
|
||||
// remove duplicates
|
||||
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)) {
|
||||
angles.erase(angles.begin() + i);
|
||||
--i;
|
||||
// remove duplicates
|
||||
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)) {
|
||||
angles.erase(angles.begin() + i);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
/* compare first value with last one and remove the greatest one (PI)
|
||||
in case they are parallel (PI, 0) */
|
||||
if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
|
||||
angles.pop_back();
|
||||
|
||||
for (auto angle : angles)
|
||||
candidates.push_back(BridgeDirection(angle));
|
||||
}
|
||||
/* compare first value with last one and remove the greatest one (PI)
|
||||
in case they are parallel (PI, 0) */
|
||||
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;
|
||||
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;
|
||||
|
||||
/*
|
||||
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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() {};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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!
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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 © : (*object)->_shifted_copies) {
|
||||
for (const Point © : 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
|
||||
));
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
{
|
||||
fprintf(this->f, "</svg>\n");
|
||||
fclose(this->f);
|
||||
printf("SVG written to %s\n", this->filename.c_str());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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); };
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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); %};
|
||||
|
@ -81,3 +81,6 @@ _constant()
|
||||
|
||||
%}
|
||||
|
||||
coord_t solid_spacing(coord_t width, coord_t distance)
|
||||
%code{% RETVAL = Flow::solid_spacing(width, distance); %};
|
||||
|
||||
|
@ -253,6 +253,15 @@ ModelMaterial::attributes()
|
||||
void set_material_id(t_model_material_id material_id)
|
||||
%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; %};
|
||||
|
@ -41,6 +41,7 @@
|
||||
%code{% RETVAL = THIS->bounding_box().center(); %};
|
||||
int facets_count();
|
||||
void reset_repair_stats();
|
||||
void reverse_normals();
|
||||
|
||||
%{
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user