Merge branch 'master_27x'

This commit is contained in:
Lukas Matena 2024-01-26 13:07:19 +01:00
commit a42223f963
172 changed files with 6427 additions and 13767 deletions

1
.gitignore vendored
View File

@ -12,7 +12,6 @@ xs/MANIFEST.bak
xs/assertlib*
.init_bundle.ini
.vs/*
local-lib
/src/TAGS
/.vscode/
build-linux/*

132
Build.PL
View File

@ -1,132 +0,0 @@
#!/usr/bin/perl
print "This script is currently used for installing Perl dependenices for running\n";
print "the libslic3r unit / integration tests through Perl prove.\n";
print "If you don't plan to run the unit / integration tests, you don't need to\n";
print "install these dependencies to build and run PrusaSlicer.\n";
use strict;
use warnings;
use Config;
use File::Spec;
my %prereqs = qw(
Devel::CheckLib 0
ExtUtils::MakeMaker 6.80
ExtUtils::ParseXS 3.22
ExtUtils::XSpp 0
ExtUtils::XSpp::Cmd 0
ExtUtils::CppGuess 0
ExtUtils::Typemaps 0
ExtUtils::Typemaps::Basic 0
File::Basename 0
File::Spec 0
Getopt::Long 0
Module::Build::WithXSpp 0.14
Moo 1.003001
POSIX 0
Scalar::Util 0
Test::More 0
IO::Scalar 0
Time::HiRes 0
);
my %recommends = qw(
Class::XSAccessor 0
Test::Harness 0
);
my $sudo = grep { $_ eq '--sudo' } @ARGV;
my $nolocal = grep { $_ eq '--nolocal' } @ARGV;
my @missing_prereqs = ();
if ($ENV{SLIC3R_NO_AUTO}) {
foreach my $module (sort keys %prereqs) {
my $version = $prereqs{$module};
next if eval "use $module $version; 1";
push @missing_prereqs, $module if exists $prereqs{$module};
print "Missing prerequisite $module $version\n";
}
foreach my $module (sort keys %recommends) {
my $version = $recommends{$module};
next if eval "use $module $version; 1";
print "Missing optional $module $version\n";
}
} else {
my @try = (
$ENV{CPANM} // (),
File::Spec->catfile($Config{sitebin}, 'cpanm'),
File::Spec->catfile($Config{installscript}, 'cpanm'),
);
my $cpanm;
foreach my $path (@try) {
if (-e $path) { # don't use -x because it fails on Windows
$cpanm = $path;
last;
}
}
if (!$cpanm) {
if ($^O =~ /^(?:darwin|linux)$/ && system(qw(which cpanm)) == 0) {
$cpanm = 'cpanm';
}
}
die <<'EOF'
cpanm was not found. Please install it before running this script.
There are several ways to install cpanm, try one of these:
apt-get install cpanminus
curl -L http://cpanmin.us | perl - --sudo App::cpanminus
cpan App::cpanminus
If it is installed in a non-standard location you can do:
CPANM=/path/to/cpanm perl Build.PL
EOF
if !$cpanm;
my @cpanm_args = ();
push @cpanm_args, "--sudo" if $sudo;
# install local::lib without --local-lib otherwise it's not usable afterwards
if (!eval "use local::lib qw(local-lib); 1") {
my $res = system $cpanm, @cpanm_args, 'local::lib';
warn "Warning: local::lib is required. You might need to run the `cpanm --sudo local::lib` command in order to install it.\n"
if $res != 0;
}
push @cpanm_args, ('--local-lib', 'local-lib') if ! $nolocal;
# make sure our cpanm is updated (old ones don't support the ~ syntax)
system $cpanm, @cpanm_args, 'App::cpanminus';
my %modules = (%prereqs, %recommends);
foreach my $module (sort keys %modules) {
my $version = $modules{$module};
my @cmd = ($cpanm, @cpanm_args);
# temporary workaround for upstream bug in test
push @cmd, '--notest'
if $module =~ /^(?:OpenGL|Test::Harness)$/;
push @cmd, "$module~$version";
my $res = system @cmd;
if ($res != 0) {
if (exists $prereqs{$module}) {
push @missing_prereqs, $module;
} else {
printf "Don't worry, this module is optional.\n";
}
}
}
}
print "\n";
print "In the next step, you need to build the PrusaSlicer C++ library.\n";
print "1) Create a build directory and change to it\n";
print "2) run cmake .. -DCMAKE_BUILD_TYPE=Release\n";
print "3) run make\n";
print "4) to execute the automatic tests, run ctest --verbose\n";
__END__

View File

@ -39,7 +39,6 @@ option(SLIC3R_FHS "Assume PrusaSlicer is to be installed in a FHS
option(SLIC3R_PCH "Use precompiled headers" 1)
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0)
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
option(SLIC3R_UBSAN "Enable UBSan on Clang and GCC" 0)
option(SLIC3R_ENABLE_FORMAT_STEP "Enable compilation of STEP file support" ON)
@ -83,7 +82,6 @@ option(SLIC3R_BUILD_TESTS "Build unit tests" ON)
if (IS_CROSS_COMPILE)
message("Detected cross compilation setup. Tests and encoding checks will be forcedly disabled!")
set(SLIC3R_PERL_XS OFF CACHE BOOL "" FORCE)
set(SLIC3R_BUILD_TESTS OFF CACHE BOOL "" FORCE)
endif ()
@ -176,10 +174,6 @@ if(NOT WIN32)
add_compile_options("$<$<CONFIG:DEBUG>:-DDEBUG>")
endif()
# To be able to link libslic3r with the Perl XS module.
# Once we get rid of Perl and libslic3r is linked statically, we can get rid of -fPIC
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
# We pick it from environment if it is not defined in another way
if(WIN32)
@ -621,13 +615,6 @@ set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT P
add_dependencies(gettext_make_pot hintsToPot)
# Perl bindings, currently only used for the unit / integration tests of libslic3r.
# Also runs the unit / integration tests.
#FIXME Port the tests into C++ to finally get rid of the Perl!
if (SLIC3R_PERL_XS)
add_subdirectory(xs)
endif ()
if(SLIC3R_BUILD_SANDBOXES)
add_subdirectory(sandboxes)
endif()

View File

@ -1,103 +0,0 @@
{
"build_systems":
[
{
"name": "List",
//"file_regex": " at ([^-\\s]*) line ([0-9]*)",
// "file_regex": " at (D\\:\\/src\\/Slic3r\\/.*?) line ([0-9]*)",
"shell_cmd": "ls -l"
},
{
"name": "Run",
"working_dir": "$project_path",
"file_regex": " at (.*?) line ([0-9]*)",
// "shell_cmd": "chdir & perl slic3r.pl --DataDir \"C:\\Users\\Public\\Documents\\Prusa3D\\Slic3r settings MK2\" --gui \"..\\Slic3r-tests\\gap fill torture 20 -rt.stl\""
"shell_cmd": "chdir & perl slic3r.pl"
},
{
"name": "full",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"shell_cmd": "chdir & perl Build.pl"
},
{
"name": "xs",
"working_dir": "$project_path/build",
// for Visual Studio:
"file_regex": "^(..[^:]*)\\(([0-9]+)\\)(.*)$",
// For GCC:
// "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"shell_cmd": "chdir & ninja -j 6 -v",
"env": {
// "PATH": "C:\\Program Files (x86)\\MSBuild\\12.0\\bin\\amd64;C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\BIN\\amd64;C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\IDE;C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\Tools;%PATH%;c:\\wperl64d\\site\\bin;c:\\wperl64d\\bin",
// "PERL_CPANM_HOME": "c:\\wperl64d\\cpanm",
// "WXDIR": "D:\\src-perl\\wxWidgets-3.0.3-beta1",
// "BOOST_DIR": "D:\\src-perl\\boost_1_61_0",
// "BOOST_INCLUDEDIR": "D:\\src-perl\\boost_1_61_0",
// "BOOST_LIBRARYDIR": "D:\\src-perl\\boost_1_61_0\\stage\\x64\\lib",
// "SLIC3R_STATIC": "1"
}
},
{
"name": "xs & run",
"working_dir": "$project_path/build",
"file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
"shell_cmd": "chdir & ninja -j 6 & cd .. & perl slic3r.pl --gui \"..\\Slic3r-tests\\star3-big2.stl\""
},
{
"name": "Slic3r - clean",
"working_dir": "$project_path/build",
"file_regex": "^(..[^:]*)(?::|\\()([0-9]+)(?::|\\))(?:([0-9]+):)?\\s*(.*)",
"shell_cmd": ["chdir & ninja clean"]
},
{
"name": "run tests",
"working_dir": "$project_path/build",
// for Visual Studio:
"file_regex": "^(..[^:]*)\\(([0-9]+)\\)(.*)$",
"shell_cmd": "chdir & ctest --verbose"
},
{
"name": "Clean & Configure",
"working_dir": "$project_path",
// for Visual Studio:
"file_regex": "^(..[^:]*)(?::|\\()([0-9]+)(?::|\\))(?:([0-9]+):)?\\s*(.*)",
"shell_cmd": "chdir & rmdir /S /Q build & mkdir build & cd build & cmake -G Ninja .. -DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_RULE_PROGRESS=OFF -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo"
},
{
"name": "Configure",
"working_dir": "$project_path/build",
// for Visual Studio:
"file_regex": "^(..[^:]*)(?::|\\()([0-9]+)(?::|\\))(?:([0-9]+):)?\\s*(.*)",
"shell_cmd": "cmake -G Ninja .. -DCMAKE_COLOR_MAKEFILE=OFF -DCMAKE_RULE_PROGRESS=OFF -DCMAKE_RULE_MESSAGES=OFF -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo"
}
],
"folders":
[
{
"path": ".",
// "folder_exclude_patterns": [".svn", "._d", ".metadata", ".settings"],
"file_exclude_patterns": ["XS.c", "*.pch", "*.ilk", "*.js" ]
}
],
"settings":
{
"sublimegdb_workingdir": "${folder:${project_path:run}}",
// NOTE: You MUST provide --interpreter=mi for the plugin to work
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi -ex 'target localhost:2345'",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi perl --args perl slic3r.pl",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi perl --args slic3r.pl ",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi -e C:\\Strawberry\\perl\\bin\\perl.exe -s C:\\Strawberry\\perl\\site\\lib\\auto\\Slic3r\\XS\\XS.xs.dll --args perl slic3r.pl -j 1 --gui D:\\src\\Slic3r-tests\\star3-big.stl",
"sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi perl.exe --args perl slic3r.pl -j 1 --gui", // D:\\src\\Slic3r-tests\\star3-big.stl",
// "sublimegdb_commandline": "D:\\Qt\\Tools\\mingw492_32\\bin\\gdb.exe --interpreter=mi -x slic3r.gdb",
// "arguments": "slic3r -j 1 --gui ../Slic3r-tests/star3-big.stl",
// "arguments": "../slic3r.pl -j 1 --gui",
// "sublimegdb_exec_cmd": "-exec-continue",
// Add "pending breakpoints" for symbols that are dynamically loaded from
// external shared libraries
"debug_ext" : true,
"run_after_init": false,
"close_views": false
}
}

View File

@ -19,10 +19,7 @@ the [documentation directory](doc/) for more information.
### What language is it written in?
All user facing code is written in C++, and some legacy code as well as unit
tests are written in Perl. Perl is not required for either development or use
of PrusaSlicer.
All user facing code is written in C++.
The slicing core is the `libslic3r` library, which can be built and used in a standalone way.
The command line interface is a thin wrapper over `libslic3r`.

View File

@ -1,109 +0,0 @@
# Find the wxWidgets module based on the information provided by the Perl Alien::wxWidgets module.
# Check for the Perl & PerlLib modules
include(LibFindMacros)
libfind_package(AlienWx Perl)
libfind_package(AlienWx PerlLibs)
if (AlienWx_DEBUG)
message(STATUS " AlienWx_FIND_COMPONENTS=${AlienWx_FIND_COMPONENTS}")
endif()
# Execute an Alien::Wx module to find the relevant information regarding
# the wxWidgets used by the Perl interpreter.
# Perl specific stuff
set(AlienWx_TEMP_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/AlienWx_TEMP_INCLUDE.txt)
execute_process(
COMMAND ${PERL_EXECUTABLE} -e "
# Import Perl modules.
use strict;
use warnings;
use Text::ParseWords;
BEGIN {
# CMake sets the environment variables CC and CXX to the detected C compiler.
# There is an issue with the Perl ExtUtils::CBuilder, which does not handle whitespaces
# in the paths correctly on Windows, so we rather drop the CMake auto-detected paths.
delete \$ENV{CC};
delete \$ENV{CXX};
}
use Alien::wxWidgets;
use ExtUtils::CppGuess;
# Test for a Visual Studio compiler
my \$cpp_guess = ExtUtils::CppGuess->new;
my \$mswin = \$^O eq 'MSWin32';
my \$msvc = \$cpp_guess->is_msvc;
# List of wxWidgets components to be used.
my @components = split /;/, '${AlienWx_FIND_COMPONENTS}';
# Query the available data from Alien::wxWidgets.
my \$version = Alien::wxWidgets->version;
my \$config = Alien::wxWidgets->config;
my \$compiler = Alien::wxWidgets->compiler;
my \$linker = Alien::wxWidgets->linker;
my \$include_path = ' ' . Alien::wxWidgets->include_path;
my \$defines = ' ' . Alien::wxWidgets->defines;
my \$cflags = Alien::wxWidgets->c_flags;
my \$linkflags = Alien::wxWidgets->link_flags;
my \$libraries = ' ' . Alien::wxWidgets->libraries(@components);
my \$gui_toolkit = Alien::wxWidgets->config->{toolkit};
#my @libraries = Alien::wxWidgets->link_libraries(@components);
#my @implib = Alien::wxWidgets->import_libraries(@components);
#my @shrlib = Alien::wxWidgets->shared_libraries(@components);
#my @keys = Alien::wxWidgets->library_keys; # 'gl', 'adv', ...
#my \$library_path = Alien::wxWidgets->shared_library_path;
#my \$key = Alien::wxWidgets->key;
#my \$prefix = Alien::wxWidgets->prefix;
my \$filename = '${AlienWx_TEMP_INCLUDE}';
open(my $fh, '>', \$filename) or die \"Could not open file '\$filename' \$!\";
# Convert a space separated lists to CMake semicolon separated lists,
# escape the backslashes,
# export the resulting list to a temp file.
sub cmake_set_var {
my (\$varname, \$content) = @_;
# Remove line separators.
\$content =~ s/\\r|\\n//g;
# Escape the path separators.
\$content =~ s/\\\\/\\\\\\\\\\\\\\\\/g;
my @words = shellwords(\$content);
print \$fh \"set(AlienWx_\$varname \\\"\" . join(';', @words) . \"\\\")\\n\";
}
cmake_set_var('VERSION', \$version);
\$include_path =~ s/ -I/ /g;
cmake_set_var('INCLUDE_DIRS', \$include_path);
\$libraries =~ s/ -L/ -LIBPATH:/g if \$msvc;
cmake_set_var('LIBRARIES', \$libraries);
#cmake_set_var('LIBRARY_DIRS', );
#\$defines =~ s/ -D/ /g;
cmake_set_var('DEFINITIONS', \$defines);
#cmake_set_var('DEFINITIONS_DEBUG', );
cmake_set_var('CXX_FLAGS', \$cflags);
cmake_set_var('GUI_TOOLKIT', \$gui_toolkit);
close \$fh;
")
include(${AlienWx_TEMP_INCLUDE})
file(REMOVE ${AlienWx_TEMP_INCLUDE})
unset(AlienWx_TEMP_INCLUDE)
if (AlienWx_DEBUG)
message(STATUS " AlienWx_VERSION = ${AlienWx_VERSION}")
message(STATUS " AlienWx_INCLUDE_DIRS = ${AlienWx_INCLUDE_DIRS}")
message(STATUS " AlienWx_LIBRARIES = ${AlienWx_LIBRARIES}")
message(STATUS " AlienWx_LIBRARY_DIRS = ${AlienWx_LIBRARY_DIRS}")
message(STATUS " AlienWx_DEFINITIONS = ${AlienWx_DEFINITIONS}")
message(STATUS " AlienWx_DEFINITIONS_DEBUG = ${AlienWx_DEFINITIONS_DEBUG}")
message(STATUS " AlienWx_CXX_FLAGS = ${AlienWx_CXX_FLAGS}")
message(STATUS " AlienWx_GUI_TOOLKIT = ${AlienWx_GUI_TOOLKIT}")
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(AlienWx
REQUIRED_VARS AlienWx_INCLUDE_DIRS AlienWx_LIBRARIES
# HANDLE_COMPONENTS
VERSION_VAR AlienWx_VERSION)

View File

@ -1,88 +0,0 @@
# Find the dependencies for linking with the Perl runtime library.
# Check for the Perl & PerlLib modules
include(LibFindMacros)
libfind_package(PerlEmbed Perl)
libfind_package(PerlEmbed PerlLibs)
# Execute an Alien::Wx module to find the relevant information regarding
# the wxWidgets used by the Perl interpreter.
# Perl specific stuff
set(PerlEmbed_TEMP_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/PerlEmbed_TEMP_INCLUDE.txt)
execute_process(
COMMAND ${PERL_EXECUTABLE} -MExtUtils::Embed -e "
# Import Perl modules.
use strict;
use warnings;
use Config;
use Text::ParseWords;
use ExtUtils::CppGuess;
# Test for a Visual Studio compiler
my \$cpp_guess = ExtUtils::CppGuess->new;
my \$mswin = \$^O eq 'MSWin32';
my \$msvc = \$cpp_guess->is_msvc;
# Query the available data from Alien::wxWidgets.
my \$ccflags;
my \$ldflags;
{ local *STDOUT; open STDOUT, '>', \\\$ccflags; ccflags; }
{ local *STDOUT; open STDOUT, '>', \\\$ldflags; ldopts; }
\$ccflags = ' ' . \$ccflags;
\$ldflags = ' ' . \$ldflags;
my \$filename = '${PerlEmbed_TEMP_INCLUDE}';
open(my $fh, '>', \$filename) or die \"Could not open file '\$filename' \$!\";
# Convert a space separated lists to CMake semicolon separated lists,
# escape the backslashes,
# export the resulting list to a temp file.
sub cmake_set_var {
my (\$varname, \$content) = @_;
# Remove line separators.
\$content =~ s/\\r|\\n//g;
# Escape the path separators.
\$content =~ s/\\\\/\\\\\\\\\\\\\\\\/g;
my @words = shellwords(\$content);
print \$fh \"set(PerlEmbed_\$varname \\\"\" . join(';', @words) . \"\\\")\\n\";
}
cmake_set_var('ARCHNAME', \$Config{archname});
cmake_set_var('CCFLAGS', \$ccflags);
\$ldflags =~ s/ -L/ -LIBPATH:/g if \$msvc;
cmake_set_var('LD', \$Config{ld});
cmake_set_var('LDFLAGS', \$ldflags);
cmake_set_var('CCCDLFLAGS', \$Config{cccdlflags});
cmake_set_var('LDDLFLAGS', \$Config{lddlflags});
cmake_set_var('DLEXT', \$Config{dlext});
close \$fh;
")
include(${PerlEmbed_TEMP_INCLUDE})
file(REMOVE ${PerlEmbed_TEMP_INCLUDE})
unset(PerlEmbed_TEMP_INCLUDE)
if (PerlEmbed_DEBUG)
# First show the configuration extracted by FindPerl & FindPerlLibs:
message(STATUS " PERL_INCLUDE_PATH = ${PERL_INCLUDE_PATH}")
message(STATUS " PERL_LIBRARY = ${PERL_LIBRARY}")
message(STATUS " PERL_EXECUTABLE = ${PERL_EXECUTABLE}")
message(STATUS " PERL_SITESEARCH = ${PERL_SITESEARCH}")
message(STATUS " PERL_SITELIB = ${PERL_SITELIB}")
message(STATUS " PERL_VENDORARCH = ${PERL_VENDORARCH}")
message(STATUS " PERL_VENDORLIB = ${PERL_VENDORLIB}")
message(STATUS " PERL_ARCHLIB = ${PERL_ARCHLIB}")
message(STATUS " PERL_PRIVLIB = ${PERL_PRIVLIB}")
message(STATUS " PERL_EXTRA_C_FLAGS = ${PERL_EXTRA_C_FLAGS}")
# Second show the configuration extracted by this module (FindPerlEmbed):
message(STATUS " PerlEmbed_ARCHNAME = ${PerlEmbed_ARCHNAME}")
message(STATUS " PerlEmbed_CCFLAGS = ${PerlEmbed_CCFLAGS}")
message(STATUS " PerlEmbed_CCCDLFLAGS = ${PerlEmbed_CCCDLFLAGS}")
message(STATUS " LD = ${PerlEmbed_LD}")
message(STATUS " PerlEmbed_LDFLAGS = ${PerlEmbed_LDFLAGS}")
message(STATUS " PerlEmbed_LDDLFLAGS = ${PerlEmbed_LDDLFLAGS}")
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(PerlEmbed
REQUIRED_VARS PerlEmbed_CCFLAGS PerlEmbed_LDFLAGS
VERSION_VAR PERL_VERSION)

View File

@ -15,7 +15,7 @@ endif ()
add_cmake_project(OpenVDB
# 8.2 patched
URL https://github.com/tamasmeszaros/openvdb/archive/a68fd58d0e2b85f01adeb8b13d7555183ab10aa5.zip
URL https://github.com/prusa3d/openvdb/archive/a68fd58d0e2b85f01adeb8b13d7555183ab10aa5.zip
URL_HASH SHA256=f353e7b99bd0cbfc27ac9082de51acf32a8bc0b3e21ff9661ecca6f205ec1d81
CMAKE_ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON

View File

@ -1,61 +0,0 @@
#/|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros, Vojtěch Král @vojtechkral, Oleksandra Iushchenko @YuSanka
#/|/ Copyright (c) 2018 Martin Loidl @LoidlM
#/|/ Copyright (c) Slic3r 2011 - 2016 Alessandro Ranellucci @alranel
#/|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
#/|/ Copyright (c) 2012 Mark Hindess
#/|/ Copyright (c) 2012 Michael Moon
#/|/ Copyright (c) 2011 Clarence Risher
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
# This package loads all the non-GUI Slic3r perl packages.
package Slic3r;
# Copyright holder: Alessandro Ranellucci
# This application is licensed under the GNU Affero General Public License, version 3
use strict;
use warnings;
use Config;
require v5.10;
our $VERSION = VERSION();
our $BUILD = BUILD();
our $FORK_NAME = FORK_NAME();
our $debug = 0;
sub debugf {
printf @_ if $debug;
}
our $loglevel = 0;
BEGIN {
$debug = 1 if (defined($ENV{'SLIC3R_DEBUGOUT'}) && $ENV{'SLIC3R_DEBUGOUT'} == 1);
print "Debugging output enabled\n" if $debug;
}
use FindBin;
use Moo 1.003001;
use Slic3r::XS; # import all symbols (constants etc.) before they get parsed
use Slic3r::Config;
use Slic3r::GCode::Reader;
use Slic3r::Line;
use Slic3r::Model;
use Slic3r::Point;
use Slic3r::Polygon;
use Slic3r::Polyline;
our $build = eval "use Slic3r::Build; 1";
# Scaling between the float and integer coordinates.
# Floats are in mm.
use constant SCALING_FACTOR => 0.000001;
# Set the logging level at the Slic3r XS module.
$Slic3r::loglevel = (defined($ENV{'SLIC3R_LOGLEVEL'}) && $ENV{'SLIC3R_LOGLEVEL'} =~ /^[1-9]/) ? $ENV{'SLIC3R_LOGLEVEL'} : 0;
set_logging_level($Slic3r::loglevel);
1;

View File

@ -1,46 +0,0 @@
#/|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) 2017 Joseph Lenox @lordofhyphens
#/|/ Copyright (c) Slic3r 2011 - 2016 Alessandro Ranellucci @alranel
#/|/ Copyright (c) 2015 Alexander Rössler @machinekoder
#/|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
#/|/ Copyright (c) 2012 Mark Hindess
#/|/ Copyright (c) 2012 Josh McCullough
#/|/ Copyright (c) 2011 - 2012 Michael Moon
#/|/ Copyright (c) 2012 Simon George
#/|/ Copyright (c) 2012 Johannes Reinhardt
#/|/ Copyright (c) 2011 Clarence Risher
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
# Extends C++ class Slic3r::DynamicPrintConfig
# This perl class does not keep any perl class variables,
# all the storage is handled by the underlying C++ code.
package Slic3r::Config;
use strict;
use warnings;
use utf8;
use List::Util qw(first max);
# C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes.
# The C++ counterpart is a constant singleton.
our $Options = print_config_def();
# Generate accessors.
{
no strict 'refs';
for my $opt_key (keys %$Options) {
*{$opt_key} = sub {
#print "Slic3r::Config::accessor $opt_key\n";
$_[0]->get($opt_key)
};
}
}
package Slic3r::Config::Static;
use parent 'Slic3r::Config';
sub Slic3r::Config::GCode::new { Slic3r::Config::Static::new_GCodeConfig }
sub Slic3r::Config::Print::new { Slic3r::Config::Static::new_PrintConfig }
1;

View File

@ -1,95 +0,0 @@
#/|/ Copyright (c) Prusa Research 2016 - 2017 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) Slic3r 2013 - 2015 Alessandro Ranellucci @alranel
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
# Helper module to parse and interpret a G-code file,
# invoking a callback for each move extracted from the G-code.
# Currently used by the automatic tests only.
package Slic3r::GCode::Reader;
use Moo;
has 'config' => (is => 'ro', default => sub { Slic3r::Config::GCode->new });
has 'X' => (is => 'rw', default => sub {0});
has 'Y' => (is => 'rw', default => sub {0});
has 'Z' => (is => 'rw', default => sub {0});
has 'E' => (is => 'rw', default => sub {0});
has 'F' => (is => 'rw', default => sub {0});
has '_extrusion_axis' => (is => 'rw', default => sub {"E"});
our $Verbose = 0;
my @AXES = qw(X Y Z E);
sub apply_print_config {
my ($self, $print_config) = @_;
$self->config->apply_static($print_config);
$self->_extrusion_axis($self->config->get_extrusion_axis);
}
sub clone {
my $self = shift;
return (ref $self)->new(
map { $_ => $self->$_ } (@AXES, 'F', '_extrusion_axis', 'config'),
);
}
sub parse {
my $self = shift;
my ($gcode, $cb) = @_;
foreach my $raw_line (split /\R+/, $gcode) {
print "$raw_line\n" if $Verbose || $ENV{SLIC3R_TESTS_GCODE};
my $line = $raw_line;
$line =~ s/\s*;(.*)//; # strip comment
my %info = (comment => $1, raw => $raw_line);
# parse command
my ($command, @args) = split /\s+/, $line;
$command //= '';
my %args = map { /([A-Z])(.*)/; ($1 => $2) } @args;
# convert extrusion axis
if (exists $args{ $self->_extrusion_axis }) {
$args{E} = $args{ $self->_extrusion_axis };
}
# check motion
if ($command =~ /^G[01]$/) {
foreach my $axis (@AXES) {
if (exists $args{$axis}) {
$self->$axis(0) if $axis eq 'E' && $self->config->use_relative_e_distances;
$info{"dist_$axis"} = $args{$axis} - $self->$axis;
$info{"new_$axis"} = $args{$axis};
} else {
$info{"dist_$axis"} = 0;
$info{"new_$axis"} = $self->$axis;
}
}
$info{dist_XY} = sqrt(($info{dist_X}**2) + ($info{dist_Y}**2));
if (exists $args{E}) {
if ($info{dist_E} > 0) {
$info{extruding} = 1;
} elsif ($info{dist_E} < 0) {
$info{retracting} = 1
}
} else {
$info{travel} = 1;
}
}
# run callback
$cb->($self, $command, \%args, \%info);
# update coordinates
if ($command =~ /^(?:G[01]|G92)$/) {
for my $axis (@AXES, 'F') {
$self->$axis($args{$axis}) if exists $args{$axis};
}
}
# TODO: update temperatures
}
}
1;

View File

@ -1,46 +0,0 @@
#/|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
#/|/ Copyright (c) 2013 Jose Luis Perez Diez
#/|/ Copyright (c) 2013 Anders Sundman
#/|/ Copyright (c) 2013 Jesse Vincent
#/|/ Copyright (c) 2012 Mike Sheldrake @mesheldrake
#/|/ Copyright (c) 2012 Mark Hindess
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
package Slic3r::Geometry;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
# Exported by this module. The last section starting with convex_hull is exported by Geometry.xsp
our @EXPORT_OK = qw(
PI epsilon
scale
unscale
scaled_epsilon
X Y Z
convex_hull
deg2rad
rad2deg
);
use constant PI => 4 * atan2(1, 1);
use constant A => 0;
use constant B => 1;
use constant X1 => 0;
use constant Y1 => 1;
use constant X2 => 2;
use constant Y2 => 3;
sub epsilon () { 1E-4 }
sub scaled_epsilon () { epsilon / &Slic3r::SCALING_FACTOR }
sub scale ($) { $_[0] / &Slic3r::SCALING_FACTOR }
sub unscale ($) { $_[0] * &Slic3r::SCALING_FACTOR }
1;

View File

@ -1,13 +0,0 @@
#/|/ Copyright (c) Prusa Research 2022 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
package Slic3r::Line;
use strict;
use warnings;
# a line is a two-points line
use parent 'Slic3r::Polyline';
1;

View File

@ -1,141 +0,0 @@
#/|/ Copyright (c) Prusa Research 2016 - 2022 Vojtěch Bubník @bubnikv, Enrico Turri @enricoturri1966
#/|/ Copyright (c) Slic3r 2012 - 2016 Alessandro Ranellucci @alranel
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
# extends C++ class Slic3r::Model
package Slic3r::Model;
use List::Util qw(first max any);
sub merge {
my $class = shift;
my @models = @_;
my $new_model = ref($class)
? $class
: $class->new;
$new_model->add_object($_) for map @{$_->objects}, @models;
return $new_model;
}
sub add_object {
my $self = shift;
if (@_ == 1) {
# we have a Model::Object
my ($object) = @_;
return $self->_add_object_clone($object);
} else {
my (%args) = @_;
my $new_object = $self->_add_object;
$new_object->set_name($args{name})
if defined $args{name};
$new_object->set_input_file($args{input_file})
if defined $args{input_file};
$new_object->config->apply($args{config})
if defined $args{config};
$new_object->set_layer_height_ranges($args{layer_height_ranges})
if defined $args{layer_height_ranges};
$new_object->set_origin_translation($args{origin_translation})
if defined $args{origin_translation};
return $new_object;
}
}
sub set_material {
my $self = shift;
my ($material_id, $attributes) = @_;
my $material = $self->add_material($material_id);
$material->apply($attributes // {});
return $material;
}
# Extends C++ class Slic3r::ModelMaterial
package Slic3r::Model::Material;
sub apply {
my ($self, $attributes) = @_;
$self->set_attribute($_, $attributes{$_}) for keys %$attributes;
}
# Extends C++ class Slic3r::ModelObject
package Slic3r::Model::Object;
use List::Util qw(first sum);
sub add_volume {
my $self = shift;
my $new_volume;
if (@_ == 1) {
# we have a Model::Volume
my ($volume) = @_;
$new_volume = $self->_add_volume_clone($volume);
if ($volume->material_id ne '') {
# merge material attributes and config (should we rename materials in case of duplicates?)
if (my $material = $volume->object->model->get_material($volume->material_id)) {
my %attributes = %{ $material->attributes };
if ($self->model->has_material($volume->material_id)) {
%attributes = (%attributes, %{ $self->model->get_material($volume->material_id)->attributes })
}
my $new_material = $self->model->set_material($volume->material_id, {%attributes});
$new_material->config->apply($material->config);
}
}
} else {
my %args = @_;
$new_volume = $self->_add_volume($args{mesh});
$new_volume->set_name($args{name})
if defined $args{name};
$new_volume->set_material_id($args{material_id})
if defined $args{material_id};
$new_volume->set_modifier($args{modifier})
if defined $args{modifier};
$new_volume->config->apply($args{config})
if defined $args{config};
}
if ($new_volume->material_id ne '' && !defined $self->model->get_material($new_volume->material_id)) {
# TODO: this should be a trigger on Volume::material_id
$self->model->set_material($new_volume->material_id);
}
$self->invalidate_bounding_box;
return $new_volume;
}
sub add_instance {
my $self = shift;
if (@_ == 1) {
# we have a Model::Instance
my ($instance) = @_;
return $self->_add_instance_clone($instance);
} else {
my (%args) = @_;
my $new_instance = $self->_add_instance;
$new_instance->set_rotations($args{rotation})
if defined $args{rotation};
$new_instance->set_scaling_factors($args{scaling_factor})
if defined $args{scaling_factor};
$new_instance->set_offset($args{offset})
if defined $args{offset};
return $new_instance;
}
}
1;

View File

@ -1,33 +0,0 @@
#/|/ Copyright (c) Prusa Research 2018 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) Slic3r 2011 - 2015 Alessandro Ranellucci @alranel
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
package Slic3r::Point;
use strict;
use warnings;
sub new_scale {
my $class = shift;
return $class->new(map Slic3r::Geometry::scale($_), @_);
}
package Slic3r::Pointf;
use strict;
use warnings;
sub new_unscale {
my $class = shift;
return $class->new(map Slic3r::Geometry::unscale($_), @_);
}
package Slic3r::Pointf3;
use strict;
use warnings;
sub new_unscale {
my $class = shift;
return $class->new(map Slic3r::Geometry::unscale($_), @_);
}
1;

View File

@ -1,14 +0,0 @@
#/|/ Copyright (c) Prusa Research 2017 - 2022 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
#/|/ Copyright (c) 2012 Mark Hindess
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
package Slic3r::Polygon;
use strict;
use warnings;
# a polygon is a closed polyline.
use parent 'Slic3r::Polyline';
1;

View File

@ -1,19 +0,0 @@
#/|/ Copyright (c) Prusa Research 2018 Vojtěch Bubník @bubnikv
#/|/ Copyright (c) Slic3r 2011 - 2014 Alessandro Ranellucci @alranel
#/|/ Copyright (c) 2012 Mark Hindess
#/|/
#/|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
#/|/
package Slic3r::Polyline;
use strict;
use warnings;
use Slic3r::Geometry qw(X Y);
sub new_scale {
my $class = shift;
my @points = map { ref($_) eq 'Slic3r::Point' ? $_->pp : $_ } @_;
return $class->new(map [ Slic3r::Geometry::scale($_->[X]), Slic3r::Geometry::scale($_->[Y]) ], @points);
}
1;

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,6 @@
min_slic3r_version = 2.6.0-alpha4
1.1.3 Fixed top solid infill extrusion width for M5C.
1.1.2 Added AnkerMake M5C.
1.1.1 Initial official version
1.0.1 Initial Version
min_slic3r_version = 2.6.0-alpha1

View File

@ -5,7 +5,7 @@
name = AnkerMake
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.1.1
config_version = 1.1.3
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Anker/
# changelog_url = https://files.prusa3d.com/?latest=slicer-profiles&lng=%1%
@ -24,6 +24,18 @@ bed_texture = M5-texture_v2.svg
thumbnail = M5_thumbnail_v2.png
default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER;
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
[printer_model:M5C]
name = AnkerMake M5C
variants = 0.4
technology = FFF
family = AnkerMake
bed_model = M5C-bed.stl
bed_texture = M5C-texture_v2.svg
thumbnail = M5C_thumbnail_v2.png
default_materials = Generic PLA+ @ANKER; Generic PLA @ANKER; Generic PET @ANKER; Generic ABS @ANKER;
# All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface.
@ -166,16 +178,265 @@ top_solid_layers = 4
[print:0.10 mm HIGHDETAIL (0.4 mm nozzle) @ANKER]
inherits = *0.10mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
compatible_printers_condition = printer_notes=~/.*(MACHINE_M5_).*/ and nozzle_diameter[0]==0.4
[print:0.20 mm NORMAL (0.4 mm nozzle) @ANKER]
inherits = *0.20mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
compatible_printers_condition = printer_notes=~/.*(MACHINE_M5_).*/ and nozzle_diameter[0]==0.4
[print:0.30 mm SUPERDRAFT (0.4 mm nozzle) @ANKER]
inherits = *0.30mm*
compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==0.4
compatible_printers_condition = printer_notes=~/.*(MACHINE_M5_).*/ and nozzle_diameter[0]==0.4
[print:*common-M5C*]
avoid_crossing_curled_overhangs = 0
avoid_crossing_perimeters = 0
avoid_crossing_perimeters_max_detour = 0
bottom_fill_pattern = rectilinear
bottom_solid_min_thickness = 0.8
bridge_angle = 0
bridge_flow_ratio = 1
bridge_speed = 30
brim_separation = 0.1
brim_type = no_brim
brim_width = 8
complete_objects = 0
dont_support_bridges = 1
draft_shield = disabled
elefant_foot_compensation = 0
enable_dynamic_overhang_speeds = 1
external_perimeter_extrusion_width = 0.44
external_perimeter_speed = 75
external_perimeters_first = 1
extra_perimeters = 1
extra_perimeters_on_overhangs = 0
extruder_clearance_height = 20
extruder_clearance_radius = 20
extrusion_width = 0.4
fill_angle = 45
fill_density = 10%
fill_pattern = grid
first_layer_acceleration = 2500
first_layer_acceleration_over_raft = 0
first_layer_extrusion_width = 0.5
first_layer_speed = 50
first_layer_speed_over_raft = 30
fuzzy_skin = none
fuzzy_skin_point_dist = 0.8
fuzzy_skin_thickness = 0.3
gap_fill_enabled = 1
gap_fill_speed = 75
gcode_comments = 0
gcode_label_objects = 0
gcode_resolution = 0.0125
gcode_substitutions = "(G28 ;Home.*$)";"${1}\\nM4899 T3\\n";;";S-Curve"
infill_acceleration = 4000
infill_anchor = 30
infill_anchor_max = 60
infill_every_layers = 1
infill_extruder = 1
infill_extrusion_width = 0.4
infill_first = 0
infill_overlap = 25%
infill_speed = 250
interface_shells = 0
ironing = 0
ironing_flowrate = 15%
ironing_spacing = 0.1
ironing_speed = 15
ironing_type = top
max_print_speed = 500
max_volumetric_extrusion_rate_slope_negative = 0
max_volumetric_extrusion_rate_slope_positive = 0
max_volumetric_speed = 0
min_bead_width = 85%
min_feature_size = 25%
min_skirt_length = 0
mmu_segmented_region_max_width = 0
only_retract_when_crossing_perimeters = 0
ooze_prevention = 0
output_filename_format = {input_filename_base}_{layer_height}mm_{initial_filament_type}_{printer_model}.gcode
overhang_speed_0 = 25
overhang_speed_1 = 55
overhang_speed_2 = 55
overhang_speed_3 = 100%
overhangs = 1
perimeter_extruder = 1
perimeter_extrusion_width = 0.4
perimeter_generator = classic
perimeter_speed = 200
post_process =
print_settings_id =
raft_contact_distance = 0.1
raft_expansion = 1.5
raft_first_layer_density = 90%
raft_first_layer_expansion = 0
raft_layers = 0
resolution = 0
seam_position = aligned
single_extruder_multi_material_priming = 1
skirt_distance = 6
skirt_height = 1
skirts = 1
slice_closing_radius = 0.049
slicing_mode = regular
small_perimeter_speed = 75
solid_infill_below_area = 0
solid_infill_every_layers = 0
solid_infill_extruder = 1
solid_infill_extrusion_width = 0.4
solid_infill_speed = 100
spiral_vase = 0
staggered_inner_seams = 0
standby_temperature_delta = -5
support_material = 0
support_material_angle = 90
support_material_auto = 1
support_material_bottom_contact_distance = 0
support_material_bottom_interface_layers = -1
support_material_buildplate_only = 0
support_material_closing_radius = 2
support_material_contact_distance = 0.2
support_material_enforce_layers = 0
support_material_extruder = 1
support_material_extrusion_width = 0.3
support_material_interface_contact_loops = 0
support_material_interface_extruder = 1
support_material_interface_layers = 0
support_material_interface_pattern = rectilinear
support_material_interface_spacing = 0.2
support_material_interface_speed = 100%
support_material_pattern = rectilinear
support_material_spacing = 1.333
support_material_speed = 150
support_material_style = grid
support_material_synchronize_layers = 0
support_material_threshold = 30
support_material_with_sheath = 0
support_material_xy_spacing = 0.8
support_tree_angle = 40
support_tree_angle_slow = 25
support_tree_branch_diameter = 2
support_tree_branch_diameter_angle = 5
support_tree_branch_distance = 1
support_tree_tip_diameter = 0.8
support_tree_top_rate = 15%
thick_bridges = 0
thin_walls = 1
threads = 10
top_fill_pattern = rectilinear
top_infill_extrusion_width = 0.4
top_solid_infill_speed = 50
top_solid_min_thickness = 0.8
travel_speed = 500
travel_speed_z = 10
wall_distribution_count = 1
wall_transition_angle = 10
wall_transition_filter_deviation = 25%
wall_transition_length = 100%
wipe_tower = 0
wipe_tower_bridging = 10
wipe_tower_brim_width = 2
wipe_tower_cone_angle = 0
wipe_tower_extra_spacing = 100%
wipe_tower_no_sparse_layers = 0
wipe_tower_rotation_angle = 0
wipe_tower_width = 60
wipe_tower_x = 180
wipe_tower_y = 140
xy_size_compensation = 0
[print:Precision - M5C (0.4 mm nozzle) @ANKER]
inherits = *common-M5C*
compatible_printers_condition = printer_notes=~/.*(MACHINE_M5C_).*/ and printer_notes=~/.*(P_PRECISION).*/ and nozzle_diameter[0]==0.4
first_layer_height = 0.14
layer_height = 0.16
top_solid_layers = 5
bottom_solid_layers = 3
perimeters = 2
bridge_acceleration = 500
default_acceleration = 4000
external_perimeter_acceleration = 3000
perimeter_acceleration = 3000
top_solid_infill_acceleration = 2500
travel_acceleration = 4000
solid_infill_acceleration = 2500
[print:Normal - M5C (0.4 mm nozzle) @ANKER]
inherits = *common-M5C*
notes = PRINT_COMPATIBLE\n P_NORMAL\n F_PLA__Basic\n
compatible_printers_condition = printer_notes=~/.*(MACHINE_M5C_).*/ and printer_notes=~/.*(P_NORMAL).*/ and nozzle_diameter[0]==0.4
first_layer_height = 0.14
layer_height = 0.2
top_solid_layers = 4
bottom_solid_layers = 4
perimeters = 3
bridge_acceleration = 500
default_acceleration = 2500
external_perimeter_acceleration = 2500
external_perimeter_speed = 150
first_layer_acceleration = 2500
first_layer_acceleration_over_raft = 0
first_layer_speed = 50
gap_fill_speed = 75
infill_acceleration = 2500
infill_speed = 250
max_print_speed = 250
overhang_speed_0 = 15
overhang_speed_1 = 20
overhang_speed_2 = 30
overhang_speed_3 = 30
perimeter_acceleration = 2500
perimeter_speed = 250
small_perimeter_speed = 75
solid_infill_acceleration = 2500
solid_infill_speed = 150
top_solid_infill_acceleration = 2500
top_solid_infill_speed = 150
travel_acceleration = 2500
travel_speed = 250
travel_speed_z = 10
[print:Fast - M5C (0.4 mm nozzle) @ANKER]
inherits = *common-M5C*
notes = PRINT_COMPATIBLE\n P_FAST\n F_PLA__Basic\n
compatible_printers_condition = printer_notes=~/.*(MACHINE_M5C_).*/ and printer_notes=~/.*(P_FAST).*/ and nozzle_diameter[0]==0.4
layer_height = 0.25
first_layer_height = 0.14
perimeters = 2
top_solid_layers = 4
bottom_solid_layers = 3
external_perimeter_acceleration = 3000
perimeter_acceleration = 5000
top_solid_infill_acceleration = 4000
solid_infill_acceleration = 4000
infill_acceleration = 5000
bridge_acceleration = 500
first_layer_acceleration = 2500
first_layer_acceleration_over_raft = 0
travel_acceleration = 5000
default_acceleration = 5000
# Speed
perimeter_speed = 250
small_perimeter_speed = 75
external_perimeter_speed = 150
infill_speed = 270
solid_infill_speed = 100
top_solid_infill_speed = 100
support_material_speed = 150
support_material_interface_speed = 100%
bridge_speed = 20
gap_fill_speed = 75
overhang_speed_0 = 15
overhang_speed_1 = 20
overhang_speed_2 = 30
overhang_speed_3 = 30
travel_speed = 500
travel_speed_z = 10
first_layer_speed = 50
first_layer_speed_over_raft = 30
max_print_speed = 500
# When submitting new filaments please print the following temperature tower at 0.1mm layer height:
# https://www.thingiverse.com/thing:2615842
@ -186,7 +447,7 @@ compatible_printers_condition = printer_model=~/(M5).*/ and nozzle_diameter[0]==
# So having some leeway to get good bed adhesion is not a luxury for many users
[filament:*common*]
cooling = 0
cooling = 1
compatible_printers =
extrusion_multiplier = 1
filament_cost = 0
@ -218,13 +479,13 @@ temperature = 200
[filament:*PLA+*]
inherits = *common*
bed_temperature = 60
bed_temperature = 65
fan_below_layer_time = 100
filament_colour = #DDDDDD
filament_type = PLA+
filament_density = 1.24
filament_cost = 20
first_layer_bed_temperature = 60
first_layer_bed_temperature = 65
first_layer_temperature = 230
fan_always_on = 1
max_fan_speed = 100
@ -243,12 +504,12 @@ filament_type = PETG
filament_density = 1.27
filament_cost = 30
first_layer_bed_temperature = 80
first_layer_temperature = 260
first_layer_temperature = 255
fan_always_on = 1
max_fan_speed = 50
min_fan_speed = 50
bridge_fan_speed = 100
temperature = 260
temperature = 255
[filament:*ABS*]
inherits = *common*
@ -260,13 +521,13 @@ filament_type = ABS
filament_density = 1.04
filament_cost = 20
first_layer_bed_temperature = 90
first_layer_temperature = 260
first_layer_temperature = 255
fan_always_on = 0
max_fan_speed = 0
min_fan_speed = 0
bridge_fan_speed = 30
top_fan_speed = 0
temperature = 260
temperature = 255
[filament:Generic PLA @ANKER]
inherits = *PLA*
@ -289,61 +550,91 @@ filament_vendor = Generic
# Common printer preset
[printer:*common*]
printer_technology = FFF
before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;{layer_z}
between_objects_gcode =
pause_print_gcode =
deretract_speed = 60
extruder_colour = #FCE94F
extruder_offset = 0x0
gcode_flavor = marlin
silent_mode = 1
remaining_times = 1
machine_max_acceleration_e = 2500
machine_max_acceleration_extruding = 2500
machine_max_acceleration_retracting = 2500
machine_max_acceleration_travel = 1500,1250
machine_max_acceleration_x = 2500
machine_max_acceleration_y = 2500
machine_max_acceleration_z = 2500
machine_max_feedrate_e = 100
machine_max_feedrate_x = 300
machine_max_feedrate_y = 300
machine_max_feedrate_z = 20
machine_max_jerk_e = 3
machine_max_jerk_x = 15
machine_max_jerk_y = 15
machine_max_jerk_z = 0.3
machine_min_extruding_rate = 0
machine_min_travel_rate = 0
layer_gcode = ;AFTER_LAYER_CHANGE\n;{layer_z}
printer_vendor = AnkerMake
# General
## Size and coordinates
max_print_height = 250
printer_notes =
printer_settings_id =
retract_before_travel = 3
retract_before_wipe = 0
retract_layer_change = 1
retract_length_toolchange = 4
z_offset = 0
## Capabilities
#extruders_count = 1
single_extruder_multi_material = 0
## Firmware
gcode_flavor = marlin2
thumbnails = 256x256
thumbnails_format = PNG
silent_mode = 0
remaining_times = 1
## Advanced
use_relative_e_distances = 1
use_firmware_retraction = 0
use_volumetric_e = 0
variable_layer_height = 1
# Custom G-code
start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\n;LAYER_COUNT:{total_layer_count}\n
autoemit_temperature_commands = 1
end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84
before_layer_gcode = ;BEFORE_LAYER_CHANGE\n;{layer_z}\n;LAYER:{layer_num+1}
layer_gcode = ;AFTER_LAYER_CHANGE\nG92 E0
toolchange_gcode =
between_objects_gcode =
color_change_gcode = M600
pause_print_gcode = M601
template_custom_gcode =
# Machine limits
## General
### machine_limits_usage = emit_to_gcode time_estimate_only ignore
machine_limits_usage = time_estimate_only
## Maximum feedrates
machine_max_feedrate_e = 100
machine_max_feedrate_x = 500
machine_max_feedrate_y = 500
machine_max_feedrate_z = 50
## Maximum accelerations
machine_max_acceleration_e = 10000
machine_max_acceleration_extruding = 10000
machine_max_acceleration_retracting = 10000
machine_max_acceleration_travel = 10000
machine_max_acceleration_x = 10000
machine_max_acceleration_y = 10000
machine_max_acceleration_z = 10000
## Jerk limits
machine_max_jerk_x = 15, 15
machine_max_jerk_y = 15, 15
machine_max_jerk_z = 0.3, 0.3
machine_max_jerk_e = 3, 3
machine_min_extruding_rate = 0, 0
machine_min_travel_rate = 0, 0
# Extruder 1 2 3 4 5 6
## Size
nozzle_diameter = 0.4
## Preview
extruder_colour = #9BE198
## Layer height limits
min_layer_height = 0.08
max_layer_height = 0.32
## Position (for multi-extruder printers)
extruder_offset = 0x0
## Retraction
retract_length = 3
retract_lift = 0
retract_lift_above = 0
retract_lift_below = 0
retract_restart_extra = 0
retract_restart_extra_toolchange = 0
retract_speed = 60
single_extruder_multi_material = 0
thumbnails = 64x64,256x256
thumbnails_format = JPG
toolchange_gcode =
use_firmware_retraction = 0
use_relative_e_distances = 0
use_volumetric_e = 0
variable_layer_height = 1
deretract_speed = 0
retract_restart_extra = 0
retract_before_travel = 0.8
retract_layer_change = 1
wipe = 0
z_offset = 0
retract_before_wipe = 0%
## Retraction when tool is disabled (advanced settings for multi-extruder setups)
retract_length_toolchange = 4
retract_restart_extra_toolchange = 0
# Notes
printer_notes = Base printer notes is EMPTY!
default_print_profile =
default_filament_profile = Generic PLA+ @ANKER
start_gcode = M104 S{first_layer_temperature[0]} ; set final nozzle temp\nM190 S{first_layer_bed_temperature[0]} ; set and wait for bed temp to stabilize\nM109 S{first_layer_temperature[0]} ; wait for nozzle temp to stabilize\nG28 ;Home\nM420 S1; restore saved Auto Bed Leveling data\nG1 E10 F3600; push out retracted filament(fix for over retraction after prime)
end_gcode = M104 S0\nM140 S0\n;Retract the filament\nG92 E1\nG1 E-1 F300\nG28 X0 Y0\nM84
printer_technology = FFF
printer_settings_id =
[printer:*M5*]
inherits = *common*
bed_shape = 0x0,235-0,235x235,0x235
@ -354,8 +645,19 @@ retract_speed = 60
deretract_speed = 60
retract_before_travel = 3
retract_before_wipe = 0%
printer_notes = Don not remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_ANKERMAKE\nPRINTER_MODEL_M5
printer_notes = MACHINE_M5_\n PRINTER_VENDOR_ANKERMAKE\n PRINTER_MODEL_M5\n
[printer:*M5C*]
inherits = *common*
bed_shape = 0x0,220x0,220x220,0x220
max_print_height = 250
printer_model = M5C
retract_length = 0.8
retract_speed = 60
deretract_speed = 60
retract_before_travel = 3
retract_before_wipe = 0%
wipe = 1
printer_notes = MACHINE_M5C_\n PRINTER_VENDOR_ANKERMAKE\n P_PRECISION P_NORMAL P_FAST\n
[printer:AnkerMake M5 (0.4 mm nozzle)]
inherits = *M5*
nozzle_diameter = 0.4
@ -364,3 +666,11 @@ min_layer_height = 0.08
max_layer_height = 0.32
retract_lift_above = 0
default_print_profile = 0.2 mm OPTIMAL (0.4 mm nozzle) @ANKER
[printer:AnkerMake M5C (0.4 mm nozzle)]
inherits = *M5C*
nozzle_diameter = 0.4
printer_variant = 0.4
min_layer_height = 0.08
max_layer_height = 0.32
retract_lift_above = 0
default_print_profile = 0.2 mm OPTIMAL (0.4 mm nozzle) @ANKER

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,2 +1,6 @@
min_slic3r_version = 2.6.0
1.1.1 Fixed overhang speeds. Fixed branch diameter angle for organic supports.
1.1.0 Add more Snapmaker printers.
min_slic3r_version = 2.4.1
1.0.1 Fix for leading zeroes.
1.0.0 Initial Snapmaker bundle

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -130,12 +130,6 @@ int CLI::run(int argc, char **argv)
// On Unix systems, the prusa-slicer binary may be symlinked to give the application a different meaning.
boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), "gcodeviewer");
#endif // _WIN32
#if ENABLE_GL_CORE_PROFILE
std::pair<int, int> opengl_version = { 0, 0 };
#if ENABLE_OPENGL_DEBUG_OPTION
bool opengl_debug = false;
#endif // ENABLE_OPENGL_DEBUG_OPTION
#endif // ENABLE_GL_CORE_PROFILE
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility", true)->value;
@ -174,7 +168,12 @@ int CLI::run(int argc, char **argv)
m_print_config.apply(config);
}
#ifdef SLIC3R_GUI
#if ENABLE_GL_CORE_PROFILE
std::pair<int, int> opengl_version = { 0, 0 };
bool opengl_debug = false;
bool opengl_compatibility_profile = false;
// search for special keys into command line parameters
auto it = std::find(m_actions.begin(), m_actions.end(), "gcodeviewer");
if (it != m_actions.end()) {
@ -185,30 +184,37 @@ int CLI::run(int argc, char **argv)
it = std::find(m_actions.begin(), m_actions.end(), "opengl-version");
if (it != m_actions.end()) {
std::string opengl_version_str = m_config.opt_string("opengl-version");
if (std::find(Slic3r::GUI::OpenGLVersions::core_str.begin(), Slic3r::GUI::OpenGLVersions::core_str.end(), opengl_version_str) == Slic3r::GUI::OpenGLVersions::core_str.end()) {
if (std::find(Slic3r::GUI::OpenGLVersions::precore_str.begin(), Slic3r::GUI::OpenGLVersions::precore_str.end(), opengl_version_str) == Slic3r::GUI::OpenGLVersions::precore_str.end()) {
boost::nowide::cerr << "Found invalid OpenGL version: " << opengl_version_str << std::endl;
opengl_version_str.clear();
const Semver opengl_minimum = Semver(3,2,0);
const std::string opengl_version_str = m_config.opt_string("opengl-version");
boost::optional<Semver> semver = Semver::parse(opengl_version_str);
if (semver.has_value() && (*semver) >= opengl_minimum ) {
opengl_version.first = semver->maj();
opengl_version.second = semver->min();
if (std::find(Slic3r::GUI::OpenGLVersions::core.begin(), Slic3r::GUI::OpenGLVersions::core.end(), std::make_pair(opengl_version.first, opengl_version.second)) == Slic3r::GUI::OpenGLVersions::core.end()) {
opengl_version = { 0, 0 };
boost::nowide::cerr << "Required OpenGL version " << opengl_version_str << " not recognized.\n Option 'opengl-version' ignored." << std::endl;
}
}
if (!opengl_version_str.empty()) {
std::vector<std::string> tokens;
boost::split(tokens, opengl_version_str, boost::is_any_of("."), boost::token_compress_on);
opengl_version.first = std::stoi(tokens[0].c_str());
opengl_version.second = std::stoi(tokens[1].c_str());
}
} else
boost::nowide::cerr << "Required OpenGL version " << opengl_version_str << " is invalid. Must be greater than or equal to " <<
opengl_minimum.to_string() << "\n Option 'opengl-version' ignored." << std::endl;
start_gui = true;
m_actions.erase(it);
}
it = std::find(m_actions.begin(), m_actions.end(), "opengl-compatibility");
if (it != m_actions.end()) {
start_gui = true;
opengl_compatibility_profile = true;
// reset version as compatibility profile always take the highest version
// supported by the graphic card
opengl_version = std::make_pair(0, 0);
m_actions.erase(it);
}
it = std::find(m_actions.begin(), m_actions.end(), "opengl-debug");
if (it != m_actions.end()) {
start_gui = true;
#if ENABLE_OPENGL_DEBUG_OPTION
opengl_debug = true;
#endif // ENABLE_OPENGL_DEBUG_OPTION
m_actions.erase(it);
}
#else
@ -222,6 +228,17 @@ int CLI::run(int argc, char **argv)
}
}
#endif // ENABLE_GL_CORE_PROFILE
#else // SLIC3R_GUI
// If there is no GUI, we shall ignore the parameters. Remove them from the list.
for (const std::string& s : { "opengl-version", "opengl-compatibility", "opengl-debug", "gcodeviewer" }) {
auto it = std::find(m_actions.cbegin(), m_actions.cend(), s);
if (it != m_actions.end()) {
boost::nowide::cerr << "Parameter '" << s << "' is ignored, this PrusaSlicer build is CLI only." << std::endl;
m_actions.erase(it);
}
}
#endif
// Read input file(s) if any.
for (const std::string& file : m_input_files)
@ -703,10 +720,9 @@ int CLI::run(int argc, char **argv)
params.download_url = download_url;
params.delete_after_load = delete_after_load;
#if ENABLE_GL_CORE_PROFILE
#if ENABLE_OPENGL_DEBUG_OPTION
params.opengl_version = opengl_version;
params.opengl_debug = opengl_debug;
#endif // ENABLE_OPENGL_DEBUG_OPTION
params.opengl_compatibiity_profile = opengl_compatibility_profile;
#endif // ENABLE_GL_CORE_PROFILE
return Slic3r::GUI::GUI_Run(params);
#else // SLIC3R_GUI
@ -806,7 +822,13 @@ bool CLI::setup(int argc, char **argv)
if (opt_loglevel != 0)
set_logging_level(opt_loglevel->value);
}
{
const ConfigOptionInt *opt_threads = m_config.opt<ConfigOptionInt>("threads");
if (opt_threads != nullptr)
thread_count = opt_threads->value;
}
//FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
std::string validity = m_config.validate();

View File

@ -266,7 +266,7 @@ int wmain(int argc, wchar_t **argv)
// In that case, use Mesa.
(::GetSystemMetrics(SM_REMOTESESSION) && !force_hw) ||
// Try to load the default OpenGL driver and test its context version.
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(3, 2);
#endif /* SLIC3R_GUI */
wchar_t path_to_exe[MAX_PATH + 1] = { 0 };

View File

@ -61,7 +61,7 @@ class DefaultArrangerCtl : public Arranger<ArrItem>::Ctl {
public:
DefaultArrangerCtl() = default;
explicit DefaultArrangerCtl(ArrangeTaskBase::Ctl &ctl) : taskctl{&ctl} {}
explicit DefaultArrangerCtl(ArrangeTaskCtl &ctl) : taskctl{&ctl} {}
void update_status(int st) override
{

View File

@ -292,7 +292,7 @@ class DefaultArranger: public Arranger<ArrItem> {
firstfit::SelectionStrategy sel{cmpfn, on_arranged, stop_cond};
constexpr auto ep = ex_tbb;
constexpr auto ep = ex_seq;
VariantKernel basekernel;
switch (m_settings.get_arrange_strategy()) {
@ -328,7 +328,7 @@ class DefaultArranger: public Arranger<ArrItem> {
// a pure RectangleBed with inner-fit polygon calculation.
if (!with_wipe_tower &&
m_settings.get_arrange_strategy() == ArrangeSettingsView::asAuto &&
std::is_convertible_v<Bed, RectangleBed>) {
IsRectangular<Bed>) {
PackStrategyNFP base_strategy{std::move(kernel), ep, Accuracy, stop_cond};
RectangleOverfitPackingStrategy final_strategy{std::move(base_strategy)};

View File

@ -184,6 +184,12 @@ inline ExPolygons to_expolygons(const ArrangeBed &bed)
ArrangeBed to_arrange_bed(const Points &bedpts);
template<class Bed, class En = void> struct IsRectangular_ : public std::false_type {};
template<> struct IsRectangular_<RectangleBed>: public std::true_type {};
template<> struct IsRectangular_<BoundingBox>: public std::true_type {};
template<class Bed> static constexpr bool IsRectangular = IsRectangular_<Bed>::value;
} // namespace arr2
inline BoundingBox &bounding_box(BoundingBox &bb) { return bb; }

View File

@ -54,9 +54,9 @@ protected:
public:
TMArrangeKernel() = default;
TMArrangeKernel(Vec2crd gravity_center, size_t itm_cnt, double bedarea = NaNd)
: sink{gravity_center}
, m_bin_area(bedarea)
: m_bin_area(bedarea)
, m_item_cnt{itm_cnt}
, sink{gravity_center}
{}
TMArrangeKernel(size_t itm_cnt, double bedarea = NaNd)
@ -90,18 +90,12 @@ public:
// Will hold the resulting score
double score = 0;
// Density is the pack density: how big is the arranged pile
double density = 0;
// Distinction of cases for the arrangement scene
enum e_cases {
// This branch is for big items in a mixed (big and small) scene
// OR for all items in a small-only scene.
BIG_ITEM,
// This branch is for the last big item in a mixed scene
LAST_BIG_ITEM,
// For small items in a mixed scene.
SMALL_ITEM,
@ -112,10 +106,8 @@ public:
bool bigitems = is_big(envelope_area(item)) || m_rtree.empty();
if (is_wt)
compute_case = WIPE_TOWER;
else if (bigitems && m_rem_cnt > 0)
else if (bigitems)
compute_case = BIG_ITEM;
else if (bigitems && m_rem_cnt == 0)
compute_case = LAST_BIG_ITEM;
else
compute_case = SMALL_ITEM;
@ -132,20 +124,8 @@ public:
Point top_left{minc.x(), maxc.y()};
Point bottom_right{maxc.x(), minc.y()};
// Now the distance of the gravity center will be calculated to the
// five anchor points and the smallest will be chosen.
std::array<double, 5> dists;
auto cc = fullbb.center(); // The gravity center
dists[0] = (minc - cc).cast<double>().norm();
dists[1] = (maxc - cc).cast<double>().norm();
dists[2] = (itmcntr - cc).template cast<double>().norm();
dists[3] = (top_left - cc).cast<double>().norm();
dists[4] = (bottom_right - cc).cast<double>().norm();
// The smalles distance from the arranged pile center:
double dist = norm(*(std::min_element(dists.begin(), dists.end())));
double bindist = norm((ibb.center() - active_sink).template cast<double>().norm());
dist = 0.8 * dist + 0.2 * bindist;
// The smallest distance from the arranged pile center:
double dist = norm((itmcntr - m_pilebb.center()).template cast<double>().norm());
// Prepare a variable for the alignment score.
// This will indicate: how well is the candidate item
@ -153,7 +133,7 @@ public:
// with all neighbors and return the score for the best
// alignment. So it is enough for the candidate to be
// aligned with only one item.
auto alignment_score = 1.0;
auto alignment_score = 1.;
auto query = bgi::intersects(ibb);
auto& index = is_big(envelope_area(item)) ? m_rtree : m_smallsrtree;
@ -173,31 +153,23 @@ public:
auto bb = p.bb;
bb.merge(ibb);
auto bbarea = area(bb);
auto ascore = 1.0 - (fixed_area(item) + parea) / bbarea;
auto ascore = 1.0 - (area(fixed_bounding_box(item)) + area(p.bb)) / bbarea;
if(ascore < alignment_score)
alignment_score = ascore;
}
}
auto fullbbsz = fullbb.size();
density = std::sqrt(norm(fullbbsz.x()) * norm(fullbbsz.y()));
double R = double(m_rem_cnt) / (m_item_cnt);
R = std::pow(R, 1./3.);
// The final mix of the score is the balance between the
// distance from the full pile center, the pack density and
// the alignment with the neighbors
if (result.empty())
score = 0.50 * dist + 0.50 * density;
else
// Let the density matter more when fewer objects remain
score = 0.50 * dist + (1.0 - R) * 0.20 * density +
0.30 * alignment_score;
break;
}
case LAST_BIG_ITEM: {
score = norm((itmcntr - m_pilebb.center()).template cast<double>().norm());
// Let the density matter more when fewer objects remain
score = 0.6 * dist + 0.1 * alignment_score + (1.0 - R) * (0.3 * dist) + R * 0.3 * alignment_score;
break;
}
case SMALL_ITEM: {
@ -239,8 +211,11 @@ public:
if (m_item_cnt == 0)
m_item_cnt = m_rem_cnt + fixed.size() + 1;
if (std::isnan(m_bin_area))
m_bin_area = area(bed);
if (std::isnan(m_bin_area)) {
auto sz = bounding_box(bed).size();
m_bin_area = scaled<double>(unscaled(sz.x()) * unscaled(sz.y()));
}
m_norm = std::sqrt(m_bin_area);
@ -248,7 +223,7 @@ public:
m_itemstats.reserve(fixed.size());
m_rtree.clear();
m_smallsrtree.clear();
m_pilebb = {};
m_pilebb = {active_sink, active_sink};
unsigned idx = 0;
for (auto &fixitem : fixed) {
auto fixitmbb = fixed_bounding_box(fixitem);

View File

@ -104,6 +104,10 @@ ExPolygons to_expolygons(const SegmentedRectangleBed<Args...> &bed)
return to_expolygons(RectangleBed{bed.bb});
}
template<class SegB>
struct IsRectangular_<SegB, std::enable_if_t<IsSegmentedBed<SegB>, void>> : public std::true_type
{};
}} // namespace Slic3r::arr2
#endif // SEGMENTEDRECTANGLEBED_HPP

View File

@ -118,21 +118,13 @@ ArrangeTask<ArrItem>::process_native(Ctl &ctl)
} subctl{ctl, *this};
auto fixed_items = printable.unselected;
// static (unselected) unprintable objects should not be overlapped by
// movable and printable objects
std::copy(unprintable.unselected.begin(),
unprintable.unselected.end(),
std::back_inserter(fixed_items));
arranger->arrange(printable.selected, fixed_items, bed, subctl);
arranger->arrange(printable.selected, printable.unselected, bed, subctl);
std::vector<int> printable_bed_indices =
get_bed_indices(crange(printable.selected), crange(printable.unselected));
// If there are no printables, leave the physical bed empty
constexpr int SearchFrom = 1;
static constexpr int SearchFrom = 1;
// Unprintable items should go to the first logical (!) bed not containing
// any printable items

View File

@ -147,6 +147,16 @@ public:
return this->min.x() < other.max.x() && this->max.x() > other.min.x() && this->min.y() < other.max.y() && this->max.y() > other.min.y() &&
this->min.z() < other.max.z() && this->max.z() > other.min.z();
}
// Shares some boundary.
bool shares_boundary(const BoundingBox3Base<PointType>& other) const {
return is_approx(this->min.x(), other.max.x()) ||
is_approx(this->max.x(), other.min.x()) ||
is_approx(this->min.y(), other.max.y()) ||
is_approx(this->max.y(), other.min.y()) ||
is_approx(this->min.z(), other.max.z()) ||
is_approx(this->max.z(), other.min.z());
}
};
// Will prevent warnings caused by non existing definition of template in hpp

View File

@ -88,7 +88,6 @@ set(SLIC3R_SOURCES
ExtrusionSimulator.hpp
FileParserError.hpp
Fill/Fill.cpp
Fill/Fill.hpp
Fill/Fill3DHoneycomb.cpp
Fill/Fill3DHoneycomb.hpp
Fill/FillAdaptive.cpp
@ -194,8 +193,6 @@ set(SLIC3R_SOURCES
GCode/AvoidCrossingPerimeters.hpp
GCode/Travels.cpp
GCode/Travels.hpp
GCode/LayerChanges.cpp
GCode/LayerChanges.hpp
GCode.cpp
GCode.hpp
GCodeReader.cpp

View File

@ -105,7 +105,7 @@ struct EmbossShape
// Note: image is only cache it is not neccessary to store
// Store file data as plain string
assert(file_data != nullptr);
// For Embossed text file_data are nullptr
ar(path, path_in_3mf, (file_data != nullptr) ? *file_data : std::string(""));
}
template<class Archive> void load(Archive &ar) {

View File

@ -446,7 +446,7 @@ inline void expolygons_rotate(ExPolygons &expolys, double angle)
expoly.rotate(angle);
}
inline bool expolygons_contain(ExPolygons &expolys, const Point &pt, bool border_result = true)
inline bool expolygons_contain(const ExPolygons &expolys, const Point &pt, bool border_result = true)
{
for (const ExPolygon &expoly : expolys)
if (expoly.contains(pt, border_result))

View File

@ -699,13 +699,16 @@ void gcode_spread_points(
}
}
*/
float area_total = 0;
float volume_total = 0;
float volume_excess = 0;
float volume_deficit = 0;
size_t n_cells = 0;
float area_circle_total = 0;
float area_total = 0;
float volume_total = 0;
size_t n_cells = 0;
#if 0
float volume_excess = 0;
float volume_deficit = 0;
float area_circle_total = 0;
// The intermediate lines.
for (int j = row_first; j < row_last; ++ j) {
const std::pair<float, float> &span1 = spans[j];
@ -759,7 +762,11 @@ void gcode_spread_points(
cell.volume = acc[j][i];
cell.area = mask[j][i];
assert(cell.area >= 0.f && cell.area <= 1.000001f);
area_circle_total += area;
#if 0
area_circle_total += area;
#endif
if (cell.area < area)
cell.area = area;
cell.fraction_covered = std::clamp((cell.area > 0) ? (area / cell.area) : 0, 0.f, 1.f);
@ -769,10 +776,15 @@ void gcode_spread_points(
}
float cell_height = cell.volume / cell.area;
cell.excess_height = cell_height - height_target;
#if 0
area_circle_total += area;
if (cell.excess_height > 0.f)
volume_excess += cell.excess_height * cell.area * cell.fraction_covered;
else
volume_deficit -= cell.excess_height * cell.area * cell.fraction_covered;
#endif
volume_total += cell.volume * cell.fraction_covered;
area_total += cell.area * cell.fraction_covered;
}

View File

@ -1,37 +0,0 @@
///|/ Copyright (c) Prusa Research 2016 - 2020 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#ifndef slic3r_Fill_hpp_
#define slic3r_Fill_hpp_
#include <memory.h>
#include <float.h>
#include <stdint.h>
#include "../libslic3r.h"
#include "../PrintConfig.hpp"
#include "FillBase.hpp"
namespace Slic3r {
class ExtrusionEntityCollection;
class LayerRegion;
// An interface class to Perl, aggregating an instance of a Fill and a FillData.
class Filler
{
public:
Filler() : fill(nullptr) {}
~Filler() {
delete fill;
fill = nullptr;
}
Fill *fill;
FillParams params;
};
} // namespace Slic3r
#endif // slic3r_Fill_hpp_

View File

@ -35,7 +35,6 @@
#include "GCode/WipeTower.hpp"
#include "GCode/WipeTowerIntegration.hpp"
#include "GCode/Travels.hpp"
#include "GCode/LayerChanges.hpp"
#include "Point.hpp"
#include "Polygon.hpp"
#include "PrintConfig.hpp"
@ -1214,7 +1213,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer
m_avoid_crossing_perimeters.use_external_mp_once();
file.write(this->retract_and_wipe());
file.write(this->travel_to(Point(0, 0), ExtrusionRole::None, "move to origin position for next object"));
file.write(this->travel_to(*this->last_position, Point(0, 0), ExtrusionRole::None, "move to origin position for next object"));
m_enable_cooling_markers = true;
// Disable motion planner when traveling to first object point.
m_avoid_crossing_perimeters.disable_once();
@ -1250,7 +1249,12 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
// Prusa Multi-Material wipe tower.
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower = std::make_unique<GCode::WipeTowerIntegration>(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get());
file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
// Set position for wipe tower generation.
Vec3d new_position = this->writer().get_position();
new_position.z() = first_layer_height;
this->writer().update_position(new_position);
if (print.config().single_extruder_multi_material_priming) {
file.write(m_wipe_tower->prime(*this));
// Verify, whether the print overaps the priming extrusions.
@ -1637,7 +1641,7 @@ std::string GCodeGenerator::placeholder_parser_process(
if (const std::vector<double> &pos = ppi.opt_position->values; ppi.position != pos) {
// Update G-code writer.
m_writer.update_position({ pos[0], pos[1], pos[2] });
this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] }));
this->last_position = this->gcode_to_point({ pos[0], pos[1] });
}
for (const Extruder &e : m_writer.extruders()) {
@ -2061,24 +2065,48 @@ bool GCodeGenerator::line_distancer_is_required(const std::vector<unsigned int>&
return false;
}
namespace GCode::Impl {
AABBTreeLines::LinesDistancer<Linef> get_previous_layer_distancer(
const GCodeGenerator::ObjectsLayerToPrint& objects_to_print,
const ExPolygons& slices
) {
std::vector<Linef> lines;
for (const GCodeGenerator::ObjectLayerToPrint &object_to_print : objects_to_print) {
for (const PrintInstance& instance: object_to_print.object()->instances()) {
for (const ExPolygon& polygon : slices) {
for (const Line& line : polygon.lines()) {
lines.emplace_back(unscaled(Point{line.a + instance.shift}), unscaled(Point{line.b + instance.shift}));
}
}
std::string GCodeGenerator::get_layer_change_gcode(const Vec3d& from, const Vec3d& to, const unsigned extruder_id) {
const Polyline xy_path{
this->gcode_to_point(from.head<2>()),
this->gcode_to_point(to.head<2>())
};
}
using namespace GCode::Impl::Travels;
ElevatedTravelParams elevation_params{
get_elevated_traval_params(xy_path, this->m_config, extruder_id, this->m_travel_obstacle_tracker)};
const double initial_elevation = from.z();
const double z_change = to.z() - from.z();
elevation_params.lift_height = std::max(z_change, elevation_params.lift_height);
const double path_length = unscaled(xy_path.length());
const double lift_at_travel_end =
(elevation_params.lift_height / elevation_params.slope_end * path_length);
if (lift_at_travel_end < z_change) {
elevation_params.lift_height = z_change;
elevation_params.slope_end = path_length;
}
return AABBTreeLines::LinesDistancer{std::move(lines)};
}
const std::vector<double> ensure_points_at_distances = linspace(
elevation_params.slope_end - elevation_params.blend_width / 2.0,
elevation_params.slope_end + elevation_params.blend_width / 2.0,
elevation_params.parabola_points_count
);
Points3 travel{generate_elevated_travel(
xy_path.points, ensure_points_at_distances, initial_elevation,
ElevatedTravelFormula{elevation_params}
)};
std::string travel_gcode;
Vec3d previous_point{this->point_to_gcode(travel.front())};
for (const Vec3crd& point : tcb::span{travel}.subspan(1)) {
const Vec3d gcode_point{this->point_to_gcode(point)};
travel_gcode += this->m_writer.get_travel_to_xyz_gcode(previous_point, gcode_point, "layer change");
previous_point = gcode_point;
}
return travel_gcode;
}
// In sequential mode, process_layer is called once per each object and its copy,
@ -2124,7 +2152,7 @@ LayerResult GCodeGenerator::process_layer(
return result;
// Extract 1st object_layer and support_layer of this set of layers with an equal print_z.
coordf_t print_z = layer.print_z;
coordf_t print_z = layer.print_z + m_config.z_offset.value;
bool first_layer = layer.id() == 0;
unsigned int first_extruder_id = layer_tools.extruders.front();
@ -2150,6 +2178,7 @@ LayerResult GCodeGenerator::process_layer(
m_enable_loop_clipping = !enable;
}
std::string gcode;
assert(is_decimal_separator_point()); // for the sprintfs
@ -2168,9 +2197,10 @@ LayerResult GCodeGenerator::process_layer(
m_last_layer_z = static_cast<float>(print_z);
m_max_layer_z = std::max(m_max_layer_z, m_last_layer_z);
m_last_height = height;
m_current_layer_first_position = std::nullopt;
// Set new layer - this will change Z and force a retraction if retract_layer_change is enabled.
if (! print.config().before_layer_gcode.value.empty()) {
if (!first_layer && ! print.config().before_layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
@ -2179,13 +2209,13 @@ LayerResult GCodeGenerator::process_layer(
print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config)
+ "\n";
}
gcode += this->change_layer(previous_layer_z, print_z, result.spiral_vase_enable); // this will increase m_layer_index
gcode += this->change_layer(previous_layer_z, print_z); // this will increase m_layer_index
m_layer = &layer;
if (this->line_distancer_is_required(layer_tools.extruders) && this->m_layer != nullptr && this->m_layer->lower_layer != nullptr) {
this->m_previous_layer_distancer = GCode::Impl::get_previous_layer_distancer(layers, layer.lower_layer->lslices);
}
if (this->line_distancer_is_required(layer_tools.extruders) && this->m_layer != nullptr && this->m_layer->lower_layer != nullptr)
m_travel_obstacle_tracker.init_layer(layer, layers);
m_object_layer_over_raft = false;
if (! print.config().layer_gcode.value.empty()) {
if (!first_layer && ! print.config().layer_gcode.value.empty()) {
DynamicConfig config;
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
@ -2305,6 +2335,32 @@ LayerResult GCodeGenerator::process_layer(
is_anything_overridden, false /* print_wipe_extrusions */);
}
// During layer change the starting position of next layer is now known.
// The solution is thus to emplace a temporary tag to the gcode, cache the postion and
// replace the tag later. The tag is Layer_Change_Travel, the cached position is
// m_current_layer_first_position and it is replaced here.
const std::string tag = GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Travel);
std::string layer_change_gcode;
const bool do_ramping_layer_change = (
m_previous_layer_last_position
&& m_current_layer_first_position
&& m_layer_change_extruder_id
&& !result.spiral_vase_enable
&& print_z > previous_layer_z
&& EXTRUDER_CONFIG(travel_ramping_lift)
&& EXTRUDER_CONFIG(travel_slope) > 0 && EXTRUDER_CONFIG(travel_slope) < 90
);
if (first_layer) {
layer_change_gcode = ""; // Explicit for readability.
} else if (do_ramping_layer_change) {
layer_change_gcode = this->get_layer_change_gcode(*m_previous_layer_last_position, *m_current_layer_first_position, *m_layer_change_extruder_id);
} else {
layer_change_gcode = this->writer().get_travel_to_z_gcode(print_z, "simple layer change");
}
boost::algorithm::replace_first(gcode, tag, layer_change_gcode);
BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z <<
log_memory_info();
@ -2350,10 +2406,10 @@ void GCodeGenerator::process_layer_single_object(
m_avoid_crossing_perimeters.init_layer(*m_layer);
// When starting a new object, use the external motion planner for the first travel move.
const Point &offset = print_object.instances()[print_instance.instance_id].shift;
std::pair<const PrintObject*, Point> this_object_copy(&print_object, offset);
if (m_last_obj_copy != this_object_copy)
GCode::PrintObjectInstance next_instance = {&print_object, int(print_instance.instance_id)};
if (m_current_instance != next_instance)
m_avoid_crossing_perimeters.use_external_mp_once();
m_last_obj_copy = this_object_copy;
m_current_instance = next_instance;
this->set_origin(unscale(offset));
gcode += m_label_objects.start_object(print_instance.print_object.instances()[print_instance.instance_id], GCode::LabelObjects::IncludeName::No);
}
@ -2462,9 +2518,10 @@ void GCodeGenerator::process_layer_single_object(
init_layer_delayed();
m_config.apply(region.config());
const auto extrusion_name = ironing ? "ironing"sv : "infill"sv;
for (const ExtrusionEntityReference &fill : chain_extrusion_references(temp_fill_extrusions, &m_last_pos))
const Point* start_near = this->last_position ? &(*(this->last_position)) : nullptr;
for (const ExtrusionEntityReference &fill : chain_extrusion_references(temp_fill_extrusions, start_near))
if (auto *eec = dynamic_cast<const ExtrusionEntityCollection*>(&fill.extrusion_entity()); eec) {
for (const ExtrusionEntityReference &ee : chain_extrusion_references(*eec, &m_last_pos, fill.flipped()))
for (const ExtrusionEntityReference &ee : chain_extrusion_references(*eec, start_near, fill.flipped()))
gcode += this->extrude_entity(ee, smooth_path_cache, extrusion_name);
} else
gcode += this->extrude_entity(fill, smooth_path_cache, extrusion_name);
@ -2493,9 +2550,11 @@ void GCodeGenerator::process_layer_single_object(
init_layer_delayed();
m_config.apply(region.config());
}
for (const ExtrusionEntity *ee : *eec)
for (const ExtrusionEntity *ee : *eec) {
// Don't reorder, don't flip.
gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, comment_perimeter, -1.);
gcode += this->extrude_entity({*ee, false}, smooth_path_cache, comment_perimeter, -1.);
m_travel_obstacle_tracker.mark_extruded(ee, print_instance.object_layer_to_print_id, print_instance.instance_id);
}
}
}
};
@ -2586,7 +2645,9 @@ void GCodeGenerator::set_origin(const Vec2d &pointf)
{
// if origin increases (goes towards right), last_pos decreases because it goes towards left
const auto offset = Point::new_scale(m_origin - pointf);
m_last_pos += offset;
if (last_position.has_value())
*(this->last_position) += offset;
m_wipe.offset_path(offset);
m_origin = pointf;
}
@ -2604,63 +2665,10 @@ std::string GCodeGenerator::preamble()
return gcode;
}
std::optional<std::string> GCodeGenerator::get_helical_layer_change_gcode(
const coordf_t previous_layer_z,
const coordf_t print_z,
const std::string& comment
) {
if (!this->last_pos_defined()) {
return std::nullopt;
}
const double circle_radius{2};
const unsigned n_gon_points_count{16};
const Point n_gon_start_point{this->last_pos()};
GCode::Impl::LayerChanges::Bed bed{
this->m_config.bed_shape.values,
circle_radius * 2
};
if (!bed.contains_within_padding(this->point_to_gcode(n_gon_start_point))) {
return std::nullopt;
}
const Vec2crd n_gon_vector{scaled(Vec2d{
(bed.centroid - this->point_to_gcode(n_gon_start_point)).normalized() * circle_radius
})};
const Point n_gon_centeroid{n_gon_start_point + n_gon_vector};
const Polygon n_gon{GCode::Impl::LayerChanges::generate_regular_polygon(
n_gon_centeroid,
n_gon_start_point,
n_gon_points_count
)};
const double n_gon_circumference = unscaled(n_gon.length());
const double z_change{print_z - previous_layer_z};
Points3 helix{GCode::Impl::Travels::generate_elevated_travel(
n_gon.points,
{},
previous_layer_z,
[&](const double distance){
return distance / n_gon_circumference * z_change;
}
)};
helix.emplace_back(to_3d(this->last_pos(), scaled(print_z)));
return this->generate_travel_gcode(helix, comment);
}
// called by GCodeGenerator::process_layer()
std::string GCodeGenerator::change_layer(
coordf_t previous_layer_z,
coordf_t print_z,
const bool spiral_vase_enabled
coordf_t print_z
) {
std::string gcode;
if (m_layer_count > 0)
@ -2670,31 +2678,16 @@ std::string GCodeGenerator::change_layer(
if (EXTRUDER_CONFIG(retract_layer_change))
gcode += this->retract_and_wipe();
const std::string comment{"move to next layer (" + std::to_string(m_layer_index) + ")"};
Vec3d new_position = this->writer().get_position();
new_position.z() = print_z;
this->writer().update_position(new_position);
bool do_helical_layer_change{
!spiral_vase_enabled
&& print_z > previous_layer_z
&& EXTRUDER_CONFIG(retract_layer_change)
&& EXTRUDER_CONFIG(retract_length) > 0
&& EXTRUDER_CONFIG(travel_ramping_lift)
&& EXTRUDER_CONFIG(travel_slope) > 0 && EXTRUDER_CONFIG(travel_slope) < 90
};
m_previous_layer_last_position = this->last_position ?
std::optional{to_3d(this->point_to_gcode(*this->last_position), previous_layer_z)} :
std::nullopt;
const std::optional<std::string> helix_gcode{
do_helical_layer_change ?
this->get_helical_layer_change_gcode(
m_config.z_offset.value + previous_layer_z,
m_config.z_offset.value + print_z,
comment
) :
std::nullopt
};
gcode += (
helix_gcode ?
*helix_gcode :
m_writer.travel_to_z(m_config.z_offset.value + print_z, comment)
);
gcode += GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change_Travel);
this->m_layer_change_extruder_id = m_writer.extruder()->id();
// forget last wiping path as wiping after raising Z is pointless
m_wipe.reset_path();
@ -2719,11 +2712,11 @@ static constexpr const double min_gcode_segment_length = 0.002;
std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed)
{
// Extrude all loops CCW.
bool is_hole = loop_src.is_clockwise();
Point seam_point = this->last_pos();
if (! m_config.spiral_vase && comment_is_perimeter(description)) {
bool is_hole = loop_src.is_clockwise();
Point seam_point = this->last_position.has_value() ? *this->last_position : Point::Zero();
if (!m_config.spiral_vase && comment_is_perimeter(description)) {
assert(m_layer != nullptr);
seam_point = m_seam_placer.place_seam(m_layer, loop_src, m_config.external_perimeters_first, this->last_pos());
seam_point = m_seam_placer.place_seam(m_layer, loop_src, m_config.external_perimeters_first, seam_point);
}
// Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns,
// thus empty path segments will not be produced by G-code export.
@ -2762,7 +2755,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC
if (std::optional<Point> pt = wipe_hide_seam(smooth_path, is_hole, scale_(EXTRUDER_CONFIG(nozzle_diameter))); pt) {
// Generate the seam hiding travel move.
gcode += m_writer.travel_to_xy(this->point_to_gcode(*pt), "move inwards before travel");
this->set_last_pos(*pt);
this->last_position = *pt;
}
}
@ -2774,8 +2767,9 @@ std::string GCodeGenerator::extrude_skirt(
const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed)
{
assert(loop_src.is_counter_clockwise());
Point seam_point = this->last_position.has_value() ? *this->last_position : Point::Zero();
GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam(
loop_src, false, m_scaled_resolution, this->last_pos(), scaled<double>(0.0015));
loop_src, false, m_scaled_resolution, seam_point, scaled<double>(0.0015));
// Clip the path to avoid the extruder to get exactly on the first point of the loop;
// if polyline was shorter than the clipping distance we'd get a null polyline, so
@ -2954,28 +2948,63 @@ void GCodeGenerator::GCodeOutputStream::write_format(const char* format, ...)
va_end(args);
}
std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point) {
std::string gcode;
const Vec3d gcode_point = to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z()));
if (!this->last_position) {
double lift{
EXTRUDER_CONFIG(travel_ramping_lift) ? EXTRUDER_CONFIG(travel_max_lift) :
EXTRUDER_CONFIG(retract_lift)};
const double upper_limit = EXTRUDER_CONFIG(retract_lift_below);
const double lower_limit = EXTRUDER_CONFIG(retract_lift_above);
if ((lower_limit > 0 && gcode_point.z() < lower_limit) ||
(upper_limit > 0 && gcode_point.z() > upper_limit)) {
lift = 0.0;
}
gcode += this->writer().get_travel_to_z_gcode(gcode_point.z() + lift, "lift");
}
this->last_position = point.head<2>();
this->writer().update_position(gcode_point);
std::string comment{"move to first layer point"};
gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment);
gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment);
m_current_layer_first_position = gcode_point;
return gcode;
}
std::string GCodeGenerator::_extrude(
const ExtrusionAttributes &path_attr,
const Geometry::ArcWelder::Path &path,
const ExtrusionAttributes &path_attr,
const Geometry::ArcWelder::Path &path,
const std::string_view description,
double speed)
{
std::string gcode;
const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv;
// go to first point of extrusion path
if (!m_last_pos_defined) {
const double z = this->m_last_layer_z + this->m_config.z_offset.value;
const std::string comment{"move to print after unknown position"};
gcode += this->retract_and_wipe();
gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment);
gcode += this->m_writer.get_travel_to_z_gcode(z, comment);
} else if ( m_last_pos != path.front().point) {
std::string comment = "move to first ";
comment += description;
comment += description_bridge;
comment += " point";
gcode += this->travel_to(path.front().point, path_attr.role, comment);
if (!m_current_layer_first_position) {
const Vec3crd point = to_3d(path.front().point, scaled(this->m_last_layer_z));
gcode += this->travel_to_first_position(point);
} else {
// go to first point of extrusion path
if (!this->last_position) {
const double z = this->m_last_layer_z;
const std::string comment{"move to print after unknown position"};
gcode += this->retract_and_wipe();
gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment);
gcode += this->m_writer.get_travel_to_z_gcode(z, comment);
} else if ( this->last_position != path.front().point) {
std::string comment = "move to first ";
comment += description;
comment += description_bridge;
comment += " point";
const std::string travel_gcode{this->travel_to(*this->last_position, path.front().point, path_attr.role, comment)};
gcode += travel_gcode;
}
}
// compensate retraction
@ -3142,7 +3171,7 @@ std::string GCodeGenerator::_extrude(
gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments);
if (dynamic_speed_and_fan_speed.second >= 0)
gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_speed_and_fan_speed.second)) + "\n";
double path_length = 0.;
std::string comment;
if (m_config.gcode_comments) {
comment = description;
@ -3176,16 +3205,13 @@ std::string GCodeGenerator::_extrude(
}
if (radius == 0) {
// Extrude line segment.
if (const double line_length = (p - prev).norm(); line_length > 0) {
path_length += line_length;
if (const double line_length = (p - prev).norm(); line_length > 0)
gcode += m_writer.extrude_to_xy(p, e_per_mm * line_length, comment);
}
} else {
double angle = Geometry::ArcWelder::arc_angle(prev.cast<double>(), p.cast<double>(), double(radius));
assert(angle > 0);
const double line_length = angle * std::abs(radius);
path_length += line_length;
const double dE = e_per_mm * line_length;
const double dE = e_per_mm * line_length;
assert(dE > 0);
gcode += m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment);
}
@ -3200,7 +3226,7 @@ std::string GCodeGenerator::_extrude(
if (dynamic_speed_and_fan_speed.second >= 0)
gcode += ";_RESET_FAN_SPEED\n";
this->set_last_pos(path.back().point);
this->last_position = path.back().point;
return gcode;
}
@ -3220,9 +3246,13 @@ std::string GCodeGenerator::generate_travel_gcode(
// use G1 because we rely on paths being straight (G0 may make round paths)
gcode += this->m_writer.set_travel_acceleration(acceleration);
for (const Vec3crd& point : travel) {
gcode += this->m_writer.travel_to_xyz(to_3d(this->point_to_gcode(point.head<2>()), unscaled(point.z())), comment);
this->set_last_pos(point.head<2>());
Vec3d previous_point{this->point_to_gcode(travel.front())};
for (const Vec3crd& point : tcb::span{travel}.subspan(1)) {
const Vec3d gcode_point{this->point_to_gcode(point)};
gcode += this->m_writer.travel_to_xyz(previous_point, gcode_point, comment);
this->last_position = point.head<2>();
previous_point = gcode_point;
}
if (! GCodeWriter::supports_separate_travel_acceleration(config().gcode_flavor)) {
@ -3314,18 +3344,16 @@ Polyline GCodeGenerator::generate_travel_xy_path(
}
// This method accepts &point in print coordinates.
std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, std::string comment)
{
const Point start_point = this->last_pos();
std::string GCodeGenerator::travel_to(
const Point &start_point, const Point &end_point, ExtrusionRole role, const std::string &comment
) {
// check whether a straight travel move would need retraction
bool could_be_wipe_disabled {false};
bool needs_retraction = this->needs_retraction(Polyline{start_point, point}, role);
bool needs_retraction = this->needs_retraction(Polyline{start_point, end_point}, role);
Polyline xy_path{generate_travel_xy_path(
start_point, point, needs_retraction, could_be_wipe_disabled
start_point, end_point, needs_retraction, could_be_wipe_disabled
)};
needs_retraction = this->needs_retraction(xy_path, role);
@ -3336,12 +3364,12 @@ std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, st
m_wipe.reset_path();
}
Point position_before_wipe{this->last_pos()};
Point position_before_wipe{*this->last_position};
wipe_retract_gcode = this->retract_and_wipe();
if (this->last_pos() != position_before_wipe) {
if (*this->last_position != position_before_wipe) {
xy_path = generate_travel_xy_path(
this->last_pos(), point, needs_retraction, could_be_wipe_disabled
*this->last_position, end_point, needs_retraction, could_be_wipe_disabled
);
}
} else {
@ -3353,16 +3381,24 @@ std::string GCodeGenerator::travel_to(const Point &point, ExtrusionRole role, st
const unsigned extruder_id = this->m_writer.extruder()->id();
const double retract_length = this->m_config.retract_length.get_at(extruder_id);
bool can_be_flat{!needs_retraction || retract_length == 0};
const double initial_elevation = this->m_last_layer_z + this->m_config.z_offset.value;
const double initial_elevation = this->m_last_layer_z;
const double upper_limit = this->m_config.retract_lift_below.get_at(extruder_id);
const double lower_limit = this->m_config.retract_lift_above.get_at(extruder_id);
if ((lower_limit > 0 && initial_elevation < lower_limit) ||
(upper_limit > 0 && initial_elevation > upper_limit)) {
can_be_flat = true;
}
const Points3 travel = (
can_be_flat ?
GCode::Impl::Travels::generate_flat_travel(xy_path.points, initial_elevation) :
GCode::Impl::Travels::generate_travel_to_extrusion(
xy_path,
this->m_config,
m_config,
extruder_id,
initial_elevation,
this->m_previous_layer_distancer,
m_travel_obstacle_tracker,
scaled(m_origin)
)
);
@ -3497,7 +3533,7 @@ std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_
gcode += m_ooze_prevention.post_toolchange(*this);
// The position is now known after the tool change.
this->m_last_pos_defined = false;
this->last_position = std::nullopt;
return gcode;
}

View File

@ -38,8 +38,9 @@
#include "GCode/WipeTowerIntegration.hpp"
#include "GCode/SeamPlacer.hpp"
#include "GCode/GCodeProcessor.hpp"
#include "EdgeGrid.hpp"
#include "GCode/ThumbnailData.hpp"
#include "GCode/Travels.hpp"
#include "EdgeGrid.hpp"
#include "tcbspan/span.hpp"
#include <memory>
@ -89,6 +90,30 @@ struct LayerResult {
static LayerResult make_nop_layer_result() { return {"", std::numeric_limits<coord_t>::max(), false, false, true}; }
};
namespace GCode {
// Object and support extrusions of the same PrintObject at the same print_z.
// public, so that it could be accessed by free helper functions from GCode.cpp
struct ObjectLayerToPrint
{
ObjectLayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
struct PrintObjectInstance
{
const PrintObject *print_object = nullptr;
int instance_idx = -1;
bool operator==(const PrintObjectInstance &other) const {return print_object == other.print_object && instance_idx == other.instance_idx; }
bool operator!=(const PrintObjectInstance &other) const { return *this == other; }
};
} // namespace GCode
class GCodeGenerator {
public:
@ -103,7 +128,6 @@ public:
m_layer(nullptr),
m_object_layer_over_raft(false),
m_volumetric_speed(0),
m_last_pos_defined(false),
m_last_extrusion_role(GCodeExtrusionRole::None),
m_last_width(0.0f),
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
@ -112,7 +136,7 @@ public:
m_brim_done(false),
m_second_layer_things_done(false),
m_silent_time_estimator_enabled(false),
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
m_current_instance({nullptr, -1})
{}
~GCodeGenerator() = default;
@ -124,14 +148,27 @@ public:
const Vec2d& origin() const { return m_origin; }
void set_origin(const Vec2d &pointf);
void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); }
const Point& last_pos() const { return m_last_pos; }
// Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset.
template<typename Derived>
Vec2d point_to_gcode(const Eigen::MatrixBase<Derived> &point) const {
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "GCodeGenerator::point_to_gcode(): first parameter is not a 2D vector");
return Vec2d(unscaled<double>(point.x()), unscaled<double>(point.y())) + m_origin
- m_config.extruder_offset.get_at(m_writer.extruder()->id());
Eigen::Matrix<double, Derived::SizeAtCompileTime, 1, Eigen::DontAlign> point_to_gcode(const Eigen::MatrixBase<Derived> &point) const {
static_assert(
Derived::IsVectorAtCompileTime,
"GCodeGenerator::point_to_gcode(): first parameter is not a vector"
);
static_assert(
int(Derived::SizeAtCompileTime) == 2 || int(Derived::SizeAtCompileTime) == 3,
"GCodeGenerator::point_to_gcode(): first parameter is not a 2D or 3D vector"
);
if constexpr (Derived::SizeAtCompileTime == 2) {
return Vec2d(unscaled<double>(point.x()), unscaled<double>(point.y())) + m_origin
- m_config.extruder_offset.get_at(m_writer.extruder()->id());
} else {
const Vec2d gcode_point_xy{this->point_to_gcode(point.template head<2>())};
return to_3d(gcode_point_xy, unscaled(point.z()));
}
}
// Convert coordinates of the active object to G-code coordinates, possibly adjusted for extruder offset and quantized to G-code resolution.
template<typename Derived>
Vec2d point_to_gcode_quantized(const Eigen::MatrixBase<Derived> &point) const {
@ -151,8 +188,6 @@ public:
std::string placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override = nullptr);
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
// For Perl bindings, to be used exclusively by unit tests.
unsigned int layer_count() const { return m_layer_count; }
void set_layer_count(unsigned int value) { m_layer_count = value; }
void apply_print_config(const PrintConfig &print_config);
@ -161,18 +196,10 @@ public:
// translate full config into a list of <key, value> items
static void encode_full_config(const Print& print, std::vector<std::pair<std::string, std::string>>& config);
// Object and support extrusions of the same PrintObject at the same print_z.
// public, so that it could be accessed by free helper functions from GCode.cpp
struct ObjectLayerToPrint
{
ObjectLayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
using ObjectsLayerToPrint = std::vector<ObjectLayerToPrint>;
using ObjectLayerToPrint = GCode::ObjectLayerToPrint;
using ObjectsLayerToPrint = std::vector<GCode::ObjectLayerToPrint>;
std::optional<Point> last_position;
private:
class GCodeOutputStream {
@ -218,6 +245,9 @@ private:
static ObjectsLayerToPrint collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, ObjectsLayerToPrint>> collect_layers_to_print(const Print &print);
/** @brief Generates ramping travel gcode for layer change. */
std::string get_layer_change_gcode(const Vec3d& from, const Vec3d& to, const unsigned extruder_id);
LayerResult process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
@ -251,19 +281,11 @@ private:
const GCode::SmoothPathCache &smooth_path_cache_global,
GCodeOutputStream &output_stream);
void set_last_pos(const Point &pos) { m_last_pos = pos; m_last_pos_defined = true; }
bool last_pos_defined() const { return m_last_pos_defined; }
void set_extruders(const std::vector<unsigned int> &extruder_ids);
std::string preamble();
std::optional<std::string> get_helical_layer_change_gcode(
const coordf_t previous_layer_z,
const coordf_t print_z,
const std::string& comment
);
std::string change_layer(
coordf_t previous_layer_z,
coordf_t print_z,
const bool spiral_vase_enabled
coordf_t print_z
);
std::string extrude_entity(const ExtrusionEntityReference &entity, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
std::string extrude_loop(const ExtrusionLoop &loop, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed = -1.);
@ -323,7 +345,15 @@ private:
const bool needs_retraction,
bool& could_be_wipe_disabled
);
std::string travel_to(const Point &point, ExtrusionRole role, std::string comment);
std::string travel_to(
const Point &start_point,
const Point &end_point,
ExtrusionRole role,
const std::string &comment
);
std::string travel_to_first_position(const Vec3crd& point);
bool needs_retraction(const Polyline &travel, ExtrusionRole role = ExtrusionRole::None);
std::string retract_and_wipe(bool toolchange = false);
@ -379,6 +409,7 @@ private:
AvoidCrossingPerimeters m_avoid_crossing_perimeters;
JPSPathFinder m_avoid_crossing_curled_overhangs;
RetractWhenCrossingPerimeters m_retract_when_crossing_perimeters;
GCode::TravelObstacleTracker m_travel_obstacle_tracker;
bool m_enable_loop_clipping;
// If enabled, the G-code generator will put following comments at the ends
// of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END
@ -398,7 +429,6 @@ private:
// In non-sequential mode, all its copies will be printed.
const Layer* m_layer;
// m_layer is an object layer and it is being printed over raft surface.
std::optional<AABBTreeLines::LinesDistancer<Linef>> m_previous_layer_distancer;
bool m_object_layer_over_raft;
double m_volumetric_speed;
// Support for the extrusion role markers. Which marker is active?
@ -412,9 +442,10 @@ private:
double m_last_mm3_per_mm;
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
Point m_last_pos;
bool m_last_pos_defined;
std::optional<Vec3d> m_previous_layer_last_position;
// This needs to be populated during the layer processing!
std::optional<Vec3d> m_current_layer_first_position;
std::optional<unsigned> m_layer_change_extruder_id;
std::unique_ptr<CoolingBuffer> m_cooling_buffer;
std::unique_ptr<SpiralVase> m_spiral_vase;
std::unique_ptr<GCodeFindReplace> m_find_replace;
@ -427,8 +458,8 @@ private:
bool m_brim_done;
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
// Pointer to currently exporting PrintObject and instance index.
GCode::PrintObjectInstance m_current_instance;
bool m_silent_time_estimator_enabled;

View File

@ -1177,7 +1177,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCodeGenerator &gcodegen, cons
// Otherwise perform the path planning in the coordinate system of the active object.
bool use_external = m_use_external_mp || m_use_external_mp_once;
Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0);
const Point start = gcodegen.last_pos() + scaled_origin;
const Point start = *gcodegen.last_position + scaled_origin;
const Point end = point + scaled_origin;
const Line travel(start, end);

View File

@ -57,6 +57,7 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
"HEIGHT:",
"WIDTH:",
"LAYER_CHANGE",
"LAYER_CHANGE_TRAVEL",
"COLOR_CHANGE",
"PAUSE_PRINT",
"CUSTOM_GCODE",

View File

@ -192,6 +192,7 @@ namespace Slic3r {
Height,
Width,
Layer_Change,
Layer_Change_Travel,
Color_Change,
Pause_Print,
Custom_Code,

View File

@ -275,10 +275,8 @@ std::string GCodeWriter::set_speed(double F, const std::string_view comment, con
return w.string();
}
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment)
std::string GCodeWriter::get_travel_to_xy_gcode(const Vec2d &point, const std::string_view comment) const
{
m_pos.head<2>() = point.head<2>();
GCodeG1Formatter w;
w.emit_xy(point);
w.emit_f(this->config.travel_speed.value * 60.0);
@ -286,6 +284,12 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view
return w.string();
}
std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string_view comment)
{
m_pos.head<2>() = point.head<2>();
return this->get_travel_to_xy_gcode(point, comment);
}
std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment)
{
assert(std::abs(point.x()) < 1200.);
@ -303,35 +307,49 @@ std::string GCodeWriter::travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij
return w.string();
}
std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string_view comment)
std::string GCodeWriter::travel_to_xyz(const Vec3d& from, const Vec3d &to, const std::string_view comment)
{
if (std::abs(point.x() - m_pos.x()) < EPSILON && std::abs(point.y() - m_pos.y()) < EPSILON) {
return this->travel_to_z(point.z(), comment);
} else if (std::abs(point.z() - m_pos.z()) < EPSILON) {
return this->travel_to_xy(point.head<2>(), comment);
if (std::abs(to.x() - m_pos.x()) < EPSILON && std::abs(to.y() - m_pos.y()) < EPSILON) {
return this->travel_to_z(to.z(), comment);
} else if (std::abs(to.z() - m_pos.z()) < EPSILON) {
return this->travel_to_xy(to.head<2>(), comment);
} else {
m_pos = point;
GCodeG1Formatter w;
w.emit_xyz(point);
Vec2f speed {this->config.travel_speed_z.value, this->config.travel_speed.value};
w.emit_f(speed.norm() * 60.0);
w.emit_comment(this->config.gcode_comments, comment);
return w.string();
m_pos = to;
return this->get_travel_to_xyz_gcode(from, to, comment);
}
}
std::string GCodeWriter::get_travel_to_xyz_gcode(const Vec3d &from, const Vec3d &to, const std::string_view comment) const {
GCodeG1Formatter w;
w.emit_xyz(to);
const double distance_xy{(to.head<2>() - from.head<2>()).norm()};
const double distnace_z{std::abs(to.z() - from.z())};
const double time_z = distnace_z / this->config.travel_speed_z.value;
const double time_xy = distance_xy / this->config.travel_speed.value;
const double factor = time_z > 0 ? time_xy / time_z : 1;
if (factor < 1) {
w.emit_f((this->config.travel_speed.value * factor + (1 - factor) * this->config.travel_speed_z.value) * 60.0);
} else {
w.emit_f(this->config.travel_speed.value * 60.0);
}
w.emit_comment(this->config.gcode_comments, comment);
return w.string();
}
std::string GCodeWriter::travel_to_z(double z, const std::string_view comment)
{
return std::abs(m_pos.z() - z) < EPSILON ? "" : this->get_travel_to_z_gcode(z, comment);
if (std::abs(m_pos.z() - z) < EPSILON) {
return "";
} else {
m_pos.z() = z;
return this->get_travel_to_z_gcode(z, comment);
}
}
std::string GCodeWriter::get_travel_to_z_gcode(double z, const std::string_view comment)
std::string GCodeWriter::get_travel_to_z_gcode(double z, const std::string_view comment) const
{
m_pos.z() = z;
double speed = this->config.travel_speed_z.value;
if (speed == 0.)
speed = this->config.travel_speed.value;

View File

@ -66,10 +66,23 @@ public:
std::string toolchange_prefix() const;
std::string toolchange(unsigned int extruder_id);
std::string set_speed(double F, const std::string_view comment = {}, const std::string_view cooling_marker = {}) const;
std::string get_travel_to_xy_gcode(const Vec2d &point, const std::string_view comment) const;
std::string travel_to_xy(const Vec2d &point, const std::string_view comment = {});
std::string travel_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, const std::string_view comment = {});
std::string travel_to_xyz(const Vec3d &point, const std::string_view comment = {});
std::string get_travel_to_z_gcode(double z, const std::string_view comment);
/**
* @brief Return gcode with all three axis defined. Optionally adds feedrate.
*
* Feedrate is added the starting point "from" is specified.
*
* @param from Optional starting point of the travel.
* @param to Where to travel to.
* @param comment Description of the travel purpose.
*/
std::string get_travel_to_xyz_gcode(const Vec3d &from, const Vec3d &to, const std::string_view comment) const;
std::string travel_to_xyz(const Vec3d &from, const Vec3d &to, const std::string_view comment = {});
std::string get_travel_to_z_gcode(double z, const std::string_view comment) const;
std::string travel_to_z(double z, const std::string_view comment = {});
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string_view comment = {});
std::string extrude_to_xy_G2G3IJ(const Vec2d &point, const Vec2d &ij, const bool ccw, double dE, const std::string_view comment);
@ -200,7 +213,8 @@ public:
}
void emit_string(const std::string_view s) {
strncpy(ptr_err.ptr, s.data(), s.size());
// Be aware that std::string_view::data() returns a pointer to a buffer that is not necessarily null-terminated.
memcpy(ptr_err.ptr, s.data(), s.size());
ptr_err.ptr += s.size();
}

View File

@ -1,53 +0,0 @@
#include "LayerChanges.hpp"
#include "libslic3r/ClipperUtils.hpp"
namespace Slic3r::GCode::Impl::LayerChanges {
Polygon generate_regular_polygon(
const Point &centroid, const Point &start_point, const unsigned points_count
) {
Points points;
points.reserve(points_count);
const double part_angle{2 * M_PI / points_count};
for (unsigned i = 0; i < points_count; ++i) {
const double current_angle{i * part_angle};
points.emplace_back(scaled(std::cos(current_angle)), scaled(std::sin(current_angle)));
}
Polygon regular_polygon{points};
const Vec2d current_vector{unscaled(regular_polygon.points.front())};
const Vec2d expected_vector{unscaled(start_point) - unscaled(centroid)};
const double current_scale = current_vector.norm();
const double expected_scale = expected_vector.norm();
regular_polygon.scale(expected_scale / current_scale);
regular_polygon.rotate(angle(current_vector, expected_vector));
regular_polygon.translate(centroid);
return regular_polygon;
}
Bed::Bed(const std::vector<Vec2d> &shape, const double padding)
: inner_offset(get_inner_offset(shape, padding)), centroid(unscaled(inner_offset.centroid())) {}
bool Bed::contains_within_padding(const Vec2d &point) const {
return inner_offset.contains(scaled(point));
}
Polygon Bed::get_inner_offset(const std::vector<Vec2d> &shape, const double padding) {
Points shape_scaled;
shape_scaled.reserve(shape.size());
using std::begin, std::end, std::back_inserter, std::transform;
transform(begin(shape), end(shape), back_inserter(shape_scaled), [](const Vec2d &point) {
return scaled(point);
});
const Polygons inner_offset{shrink({Polygon{shape_scaled}}, scaled(padding))};
if (inner_offset.empty()) {
return Polygon{};
}
return inner_offset.front();
}
} // namespace Slic3r::GCode::Impl::LayerChanges

View File

@ -1,52 +0,0 @@
/**
* @file
* @brief Utility functions for layer change gcode generation.
*/
#ifndef slic3r_GCode_LayerChanges_hpp_
#define slic3r_GCode_LayerChanges_hpp_
#include "libslic3r/Point.hpp"
#include "libslic3r/Polygon.hpp"
namespace Slic3r::GCode::Impl::LayerChanges {
/**
* Generates a regular polygon - all angles are the same (e.g. typical hexagon).
*
* @param centroid Central point.
* @param start_point The polygon point are ordered. This is the first point.
* @param points_count Amount of nodes of the polygon (e.g. 6 for haxagon).
*
* Distance between centroid and start point sets the scale of the polygon.
*/
Polygon generate_regular_polygon(
const Point &centroid, const Point &start_point, const unsigned points_count
);
/**
* @brief A representation of the bed shape with inner padding.
*
* Its purpose is to facilitate the bed boundary checking.
*/
class Bed
{
private:
Polygon inner_offset;
static Polygon get_inner_offset(const std::vector<Vec2d> &shape, const double padding);
public:
/**
* Bed shape with inner padding.
*/
Bed(const std::vector<Vec2d> &shape, const double padding);
Vec2d centroid;
/**
* Returns true if the point is within the bed shape including inner padding.
*/
bool contains_within_padding(const Vec2d &point) const;
};
} // namespace Slic3r::GCode::Impl::LayerChanges
#endif // slic3r_GCode_LayerChanges_hpp_

View File

@ -1,11 +1,150 @@
#include "Travels.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Print.hpp"
#include "../GCode.hpp"
namespace Slic3r::GCode {
static Lines extrusion_entity_to_lines(const ExtrusionEntity &e_entity)
{
if (const auto *path = dynamic_cast<const ExtrusionPath *>(&e_entity)) {
return to_lines(path->as_polyline());
} else if (const auto *multipath = dynamic_cast<const ExtrusionMultiPath *>(&e_entity)) {
return to_lines(multipath->as_polyline());
} else if (const auto *loop = dynamic_cast<const ExtrusionLoop *>(&e_entity)) {
return to_lines(loop->polygon());
} else {
throw Slic3r::InvalidArgument("Invalid argument supplied to TODO()");
}
return {};
}
AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> get_previous_layer_distancer(
const GCodeGenerator::ObjectsLayerToPrint &objects_to_print, const ExPolygons &slices
) {
std::vector<ObjectOrExtrusionLinef> lines;
for (const GCodeGenerator::ObjectLayerToPrint &object_to_print : objects_to_print) {
if (const PrintObject *object = object_to_print.object(); object) {
const size_t object_layer_idx = &object_to_print - &objects_to_print.front();
for (const PrintInstance &instance : object->instances()) {
const size_t instance_idx = &instance - &object->instances().front();
for (const ExPolygon &polygon : slices)
for (const Line &line : polygon.lines())
lines.emplace_back(unscaled(Point{line.a + instance.shift}), unscaled(Point{line.b + instance.shift}), object_layer_idx, instance_idx);
}
}
}
return AABBTreeLines::LinesDistancer{std::move(lines)};
}
std::pair<AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef>, size_t> get_current_layer_distancer(const ObjectsLayerToPrint &objects_to_print)
{
std::vector<ObjectOrExtrusionLinef> lines;
size_t extrusion_entity_cnt = 0;
for (const ObjectLayerToPrint &object_to_print : objects_to_print) {
const size_t object_layer_idx = &object_to_print - &objects_to_print.front();
if (const Layer *layer = object_to_print.object_layer; layer) {
for (const PrintInstance &instance : layer->object()->instances()) {
const size_t instance_idx = &instance - &layer->object()->instances().front();
for (const LayerSlice &lslice : layer->lslices_ex) {
for (const LayerIsland &island : lslice.islands) {
const LayerRegion &layerm = *layer->get_region(island.perimeters.region());
for (uint32_t perimeter_id : island.perimeters) {
assert(dynamic_cast<const ExtrusionEntityCollection *>(layerm.perimeters().entities[perimeter_id]));
const auto *eec = static_cast<const ExtrusionEntityCollection *>(layerm.perimeters().entities[perimeter_id]);
for (const ExtrusionEntity *ee : *eec) {
if (ee->role().is_external_perimeter()) {
for (const Line &line : extrusion_entity_to_lines(*ee))
lines.emplace_back(unscaled(Point{line.a + instance.shift}), unscaled(Point{line.b + instance.shift}), object_layer_idx, instance_idx, ee);
}
++extrusion_entity_cnt;
}
}
}
}
}
}
}
return {AABBTreeLines::LinesDistancer{std::move(lines)}, extrusion_entity_cnt};
}
void TravelObstacleTracker::init_layer(const Layer &layer, const ObjectsLayerToPrint &objects_to_print)
{
size_t extrusion_entity_cnt = 0;
m_extruded_extrusion.clear();
m_objects_to_print = objects_to_print;
m_previous_layer_distancer = get_previous_layer_distancer(m_objects_to_print, layer.lower_layer->lslices);
std::tie(m_current_layer_distancer, extrusion_entity_cnt) = get_current_layer_distancer(m_objects_to_print);
m_extruded_extrusion.reserve(extrusion_entity_cnt);
}
void TravelObstacleTracker::mark_extruded(const ExtrusionEntity *extrusion_entity, size_t object_layer_idx, size_t instance_idx)
{
if (extrusion_entity->role().is_external_perimeter())
this->m_extruded_extrusion.insert({int(object_layer_idx), int(instance_idx), extrusion_entity});
}
bool TravelObstacleTracker::is_extruded(const ObjectOrExtrusionLinef &line) const
{
return m_extruded_extrusion.find({line.object_layer_idx, line.instance_idx, line.extrusion_entity}) != m_extruded_extrusion.end();
}
} // namespace Slic3r::GCode
namespace Slic3r::GCode::Impl::Travels {
ElevatedTravelFormula::ElevatedTravelFormula(const ElevatedTravelParams &params)
: smoothing_from(params.slope_end - params.blend_width / 2.0)
, smoothing_to(params.slope_end + params.blend_width / 2.0)
, blend_width(params.blend_width)
, lift_height(params.lift_height)
, slope_end(params.slope_end) {
if (smoothing_from < 0) {
smoothing_from = params.slope_end;
smoothing_to = params.slope_end;
}
}
double parabola(const double x, const double a, const double b, const double c) {
return a * x * x + b * x + c;
}
double ElevatedTravelFormula::slope_function(double distance_from_start) const {
if (distance_from_start < this->slope_end) {
const double lift_percent = distance_from_start / this->slope_end;
return lift_percent * this->lift_height;
} else {
return this->lift_height;
}
}
double ElevatedTravelFormula::operator()(const double distance_from_start) const {
if (distance_from_start > this->smoothing_from && distance_from_start < this->smoothing_to) {
const double slope = this->lift_height / this->slope_end;
// This is a part of a parabola going over a specific
// range and with specific end slopes.
const double a = -slope / 2.0 / this->blend_width;
const double b = slope * this->smoothing_to / this->blend_width;
const double c = this->lift_height + a * boost::math::pow<2>(this->smoothing_to);
return parabola(distance_from_start, a, b, c);
}
return slope_function(distance_from_start);
}
Points3 generate_flat_travel(tcb::span<const Point> xy_path, const float elevation) {
Points3 result;
result.reserve(xy_path.size() - 1);
for (const Point &point : xy_path.subspan(1)) {
result.reserve(xy_path.size());
for (const Point &point : xy_path) {
result.emplace_back(point.x(), point.y(), scaled(elevation));
}
return result;
@ -52,26 +191,6 @@ std::vector<DistancedPoint> slice_xy_path(
return result;
}
struct ElevatedTravelParams
{
double lift_height{};
double slope_end{};
};
struct ElevatedTravelFormula
{
double operator()(double distance_from_start) const {
if (distance_from_start < this->params.slope_end) {
const double lift_percent = distance_from_start / this->params.slope_end;
return lift_percent * this->params.lift_height;
} else {
return this->params.lift_height;
}
}
ElevatedTravelParams params{};
};
Points3 generate_elevated_travel(
const tcb::span<const Point> xy_path,
const std::vector<double> &ensure_points_at_distances,
@ -93,59 +212,153 @@ Points3 generate_elevated_travel(
return result;
}
std::optional<double> get_first_crossed_line_distance(
tcb::span<const Line> xy_path, const AABBTreeLines::LinesDistancer<Linef> &distancer
struct Intersection
{
int object_layer_idx = -1;
int instance_idx = -1;
bool is_inside = false;
bool is_print_instance_equal(const ObjectOrExtrusionLinef &print_istance) {
return this->object_layer_idx == print_istance.object_layer_idx && this->instance_idx == print_istance.instance_idx;
}
};
double get_first_crossed_line_distance(
tcb::span<const Line> xy_path,
const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &distancer,
const ObjectsLayerToPrint &objects_to_print,
const std::function<bool(const ObjectOrExtrusionLinef &)> &predicate,
const bool ignore_starting_object_intersection
) {
assert(!xy_path.empty());
if (xy_path.empty()) {
return {};
}
if (xy_path.empty())
return std::numeric_limits<double>::max();
const Point path_first_point = xy_path.front().a;
double traversed_distance = 0;
bool skip_intersection = ignore_starting_object_intersection;
Intersection first_intersection;
for (const Line &line : xy_path) {
const Linef unscaled_line = {unscaled(line.a), unscaled(line.b)};
auto intersections = distancer.intersections_with_line<true>(unscaled_line);
if (!intersections.empty()) {
const Vec2d intersection = intersections.front().first;
const double distance = traversed_distance + (unscaled_line.a - intersection).norm();
if (distance > EPSILON) {
return distance;
} else if (intersections.size() >= 2) { // Edge case
const Vec2d second_intersection = intersections[1].first;
return traversed_distance + (unscaled_line.a - second_intersection).norm();
}
const ObjectOrExtrusionLinef unscaled_line = {unscaled(line.a), unscaled(line.b)};
const std::vector<std::pair<Vec2d, size_t>> intersections = distancer.intersections_with_line<true>(unscaled_line);
if (intersections.empty())
continue;
if (!objects_to_print.empty() && ignore_starting_object_intersection && first_intersection.object_layer_idx == -1) {
const ObjectOrExtrusionLinef &intersection_line = distancer.get_line(intersections.front().second);
const Point shift = objects_to_print[intersection_line.object_layer_idx].layer()->object()->instances()[intersection_line.instance_idx].shift;
const Point shifted_first_point = path_first_point - shift;
const bool contain_first_point = expolygons_contain(objects_to_print[intersection_line.object_layer_idx].layer()->lslices, shifted_first_point);
first_intersection = {intersection_line.object_layer_idx, intersection_line.instance_idx, contain_first_point};
}
for (const auto &intersection : intersections) {
const ObjectOrExtrusionLinef &intersection_line = distancer.get_line(intersection.second);
const double distance = traversed_distance + (unscaled_line.a - intersection.first).norm();
if (distance <= EPSILON)
continue;
// There is only one external border for each object, so when we cross this border,
// we definitely know that we are outside the object.
if (skip_intersection && first_intersection.is_print_instance_equal(intersection_line) && first_intersection.is_inside) {
skip_intersection = false;
continue;
}
if (!predicate(intersection_line))
continue;
return distance;
}
traversed_distance += (unscaled_line.a - unscaled_line.b).norm();
}
return {};
return std::numeric_limits<double>::max();
}
std::optional<double> get_obstacle_adjusted_slope_end(
const Lines &xy_path,
const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer
double get_obstacle_adjusted_slope_end(const Lines &xy_path, const GCode::TravelObstacleTracker &obstacle_tracker) {
const double previous_layer_crossed_line = get_first_crossed_line_distance(
xy_path, obstacle_tracker.previous_layer_distancer(), obstacle_tracker.objects_to_print()
);
const double current_layer_crossed_line = get_first_crossed_line_distance(
xy_path, obstacle_tracker.current_layer_distancer(), obstacle_tracker.objects_to_print(),
[&obstacle_tracker](const ObjectOrExtrusionLinef &line) { return obstacle_tracker.is_extruded(line); }
);
return std::min(previous_layer_crossed_line, current_layer_crossed_line);
}
struct SmoothingParams
{
double blend_width{};
unsigned points_count{1};
};
SmoothingParams get_smoothing_params(
const double lift_height,
const double slope_end,
unsigned extruder_id,
const double travel_length,
const FullPrintConfig &config
) {
if (!previous_layer_distancer) {
return std::nullopt;
if (config.gcode_flavor != gcfMarlinFirmware)
// Smoothing is supported only on Marlin.
return {0, 1};
const double slope = lift_height / slope_end;
const double max_machine_z_velocity = config.machine_max_feedrate_z.get_at(extruder_id);
const double max_xy_velocity =
Vec2d{
config.machine_max_feedrate_x.get_at(extruder_id),
config.machine_max_feedrate_y.get_at(extruder_id)}
.norm();
const double xy_acceleration = config.machine_max_acceleration_travel.get_at(extruder_id);
const double xy_acceleration_time = max_xy_velocity / xy_acceleration;
const double xy_acceleration_distance = 1.0 / 2.0 * xy_acceleration *
boost::math::pow<2>(xy_acceleration_time);
if (travel_length < xy_acceleration_distance) {
return {0, 1};
}
std::optional<double> first_obstacle_distance =
get_first_crossed_line_distance(xy_path, *previous_layer_distancer);
if (!first_obstacle_distance) {
return std::nullopt;
}
return *first_obstacle_distance;
const double max_z_velocity = std::min(max_xy_velocity * slope, max_machine_z_velocity);
const double deceleration_time = max_z_velocity /
config.machine_max_acceleration_z.get_at(extruder_id);
const double deceleration_xy_distance = deceleration_time * max_xy_velocity;
const double blend_width = slope_end > deceleration_xy_distance / 2.0 ? deceleration_xy_distance :
slope_end * 2.0;
const unsigned points_count = blend_width > 0 ?
std::ceil(max_z_velocity / config.machine_max_jerk_z.get_at(extruder_id)) :
1;
if (blend_width <= 0 // When there is no blend with, there is no need for smoothing.
|| points_count > 6 // That would be way to many points. Do not do it at all.
|| points_count <= 0 // Always return at least one point.
)
return {0, 1};
return {blend_width, points_count};
}
ElevatedTravelParams get_elevated_traval_params(
const Lines &xy_path,
const Polyline& xy_path,
const FullPrintConfig &config,
const unsigned extruder_id,
const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer
const GCode::TravelObstacleTracker &obstacle_tracker
) {
ElevatedTravelParams elevation_params{};
if (!config.travel_ramping_lift.get_at(extruder_id)) {
elevation_params.slope_end = 0;
elevation_params.lift_height = config.retract_lift.get_at(extruder_id);
elevation_params.blend_width = 0;
return elevation_params;
}
elevation_params.lift_height = config.travel_max_lift.get_at(extruder_id);
@ -159,22 +372,44 @@ ElevatedTravelParams get_elevated_traval_params(
elevation_params.slope_end = elevation_params.lift_height / std::tan(slope_rad);
}
std::optional<double> obstacle_adjusted_slope_end{
get_obstacle_adjusted_slope_end(xy_path, previous_layer_distancer)};
const double obstacle_adjusted_slope_end = get_obstacle_adjusted_slope_end(xy_path.lines(), obstacle_tracker);
if (obstacle_adjusted_slope_end < elevation_params.slope_end)
elevation_params.slope_end = obstacle_adjusted_slope_end;
if (obstacle_adjusted_slope_end && obstacle_adjusted_slope_end < elevation_params.slope_end) {
elevation_params.slope_end = *obstacle_adjusted_slope_end;
}
SmoothingParams smoothing_params{get_smoothing_params(
elevation_params.lift_height, elevation_params.slope_end, extruder_id,
unscaled(xy_path.length()), config
)};
elevation_params.blend_width = smoothing_params.blend_width;
elevation_params.parabola_points_count = smoothing_params.points_count;
return elevation_params;
}
std::vector<double> linspace(const double from, const double to, const unsigned count) {
if (count == 0) {
return {};
}
std::vector<double> result;
result.reserve(count);
if (count == 1) {
result.emplace_back((from + to) / 2.0);
return result;
}
const double step = (to - from) / count;
for (unsigned i = 0; i < count - 1; ++i) {
result.emplace_back(from + i * step);
}
result.emplace_back(to); // Make sure the last value is exactly equal to the value of "to".
return result;
}
Points3 generate_travel_to_extrusion(
const Polyline &xy_path,
const FullPrintConfig &config,
const unsigned extruder_id,
const double initial_elevation,
const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer,
const GCode::TravelObstacleTracker &obstacle_tracker,
const Point &xy_path_coord_origin
) {
const double upper_limit = config.retract_lift_below.get_at(extruder_id);
@ -184,15 +419,20 @@ Points3 generate_travel_to_extrusion(
return generate_flat_travel(xy_path.points, initial_elevation);
}
Lines global_xy_path;
for (const Line &line : xy_path.lines()) {
global_xy_path.emplace_back(line.a + xy_path_coord_origin, line.b + xy_path_coord_origin);
Points global_xy_path;
for (const Point &point : xy_path.points) {
global_xy_path.emplace_back(point + xy_path_coord_origin);
}
ElevatedTravelParams elevation_params{
get_elevated_traval_params(global_xy_path, config, extruder_id, previous_layer_distancer)};
ElevatedTravelParams elevation_params{get_elevated_traval_params(
Polyline{std::move(global_xy_path)}, config, extruder_id, obstacle_tracker
)};
const std::vector<double> ensure_points_at_distances{elevation_params.slope_end};
const std::vector<double> ensure_points_at_distances = linspace(
elevation_params.slope_end - elevation_params.blend_width / 2.0,
elevation_params.slope_end + elevation_params.blend_width / 2.0,
elevation_params.parabola_points_count
);
Points3 result{generate_elevated_travel(
xy_path.points, ensure_points_at_distances, initial_elevation,

View File

@ -11,18 +11,137 @@
#include <functional>
#include <optional>
#include "libslic3r/Line.hpp"
#include "libslic3r/Point.hpp"
#include <boost/functional/hash.hpp>
#include <boost/math/special_functions/pow.hpp>
#include "libslic3r/AABBTreeLines.hpp"
#include "libslic3r/PrintConfig.hpp"
// Forward declarations.
namespace Slic3r {
class Layer;
class Point;
class Linef;
class Polyline;
class FullPrintConfig;
class ExtrusionEntity;
} // namespace Slic3r
namespace Slic3r::GCode {
struct ObjectLayerToPrint;
using ObjectsLayerToPrint = std::vector<ObjectLayerToPrint>;
class ObjectOrExtrusionLinef : public Linef
{
public:
ObjectOrExtrusionLinef() = delete;
ObjectOrExtrusionLinef(const Vec2d &a, const Vec2d &b) : Linef(a, b) {}
explicit ObjectOrExtrusionLinef(const Vec2d &a, const Vec2d &b, size_t object_layer_idx, size_t instance_idx)
: Linef(a, b), object_layer_idx(int(object_layer_idx)), instance_idx(int(instance_idx)) {}
ObjectOrExtrusionLinef(const Vec2d &a, const Vec2d &b, size_t object_layer_idx, size_t instance_idx, const ExtrusionEntity *extrusion_entity)
: Linef(a, b), object_layer_idx(int(object_layer_idx)), instance_idx(int(instance_idx)), extrusion_entity(extrusion_entity) {}
virtual ~ObjectOrExtrusionLinef() = default;
const int object_layer_idx = -1;
const int instance_idx = -1;
const ExtrusionEntity *extrusion_entity = nullptr;
};
struct ExtrudedExtrusionEntity
{
const int object_layer_idx = -1;
const int instance_idx = -1;
const ExtrusionEntity *extrusion_entity = nullptr;
bool operator==(const ExtrudedExtrusionEntity &other) const
{
return extrusion_entity == other.extrusion_entity && object_layer_idx == other.object_layer_idx &&
instance_idx == other.instance_idx;
}
};
struct ExtrudedExtrusionEntityHash
{
size_t operator()(const ExtrudedExtrusionEntity &eee) const noexcept
{
std::size_t seed = std::hash<const ExtrusionEntity *>{}(eee.extrusion_entity);
boost::hash_combine(seed, std::hash<int>{}(eee.object_layer_idx));
boost::hash_combine(seed, std::hash<int>{}(eee.instance_idx));
return seed;
}
};
class TravelObstacleTracker
{
public:
void init_layer(const Layer &layer, const ObjectsLayerToPrint &objects_to_print);
void mark_extruded(const ExtrusionEntity *extrusion_entity, size_t object_layer_idx, size_t instance_idx);
bool is_extruded(const ObjectOrExtrusionLinef &line) const;
const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &previous_layer_distancer() const { return m_previous_layer_distancer; }
const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &current_layer_distancer() const { return m_current_layer_distancer; }
const ObjectsLayerToPrint &objects_to_print() const { return m_objects_to_print; }
private:
ObjectsLayerToPrint m_objects_to_print;
AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> m_previous_layer_distancer;
AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> m_current_layer_distancer;
std::unordered_set<ExtrudedExtrusionEntity, ExtrudedExtrusionEntityHash> m_extruded_extrusion;
};
} // namespace Slic3r::GCode
namespace Slic3r::GCode::Impl::Travels {
/**
* @brief A point on a curve with a distance from start.
*/
struct DistancedPoint
{
Point point;
double distance_from_start;
};
struct ElevatedTravelParams
{
/** Maximal value of nozzle lift. */
double lift_height{};
/** Distance from travel to the middle of the smoothing parabola. */
double slope_end{};
/** Width of the smoothing parabola */
double blend_width{};
/** How many points should be used to approximate the parabola */
unsigned parabola_points_count{};
};
/**
* @brief A mathematical formula for a smooth function.
*
* It starts lineary increasing than there is a parabola part and
* at the end it is flat.
*/
struct ElevatedTravelFormula
{
ElevatedTravelFormula(const ElevatedTravelParams &params);
double operator()(const double distance_from_start) const;
private:
double slope_function(double distance_from_start) const;
double smoothing_from;
double smoothing_to;
double blend_width;
double lift_height;
double slope_end;
};
/**
* @brief Takes a path described as a list of points and adds points to it.
*
@ -48,6 +167,20 @@ std::vector<DistancedPoint> slice_xy_path(
tcb::span<const Point> xy_path, tcb::span<const double> sorted_distances
);
/**
* @brief Generate regulary spaced points on 1 axis. Includes both from and to.
*
* If count is 1, the point is in the middle of the range.
*/
std::vector<double> linspace(const double from, const double to, const unsigned count);
ElevatedTravelParams get_elevated_traval_params(
const Polyline& xy_path,
const FullPrintConfig &config,
const unsigned extruder_id,
const GCode::TravelObstacleTracker &obstacle_tracker
);
/**
* @brief Simply return the xy_path with z coord set to elevation.
*/
@ -76,13 +209,19 @@ Points3 generate_elevated_travel(
*
* @param xy_path A path in 2D.
* @param distancer AABB Tree over lines.
* @param objects_to_print Objects to print are used to determine in which object xy_path starts.
* @param ignore_starting_object_intersection When it is true, then the first intersection during traveling from the object out is ignored.
* @return Distance to the first intersection if there is one.
*
* **Ignores intersection with xy_path starting point.**
*/
std::optional<double> get_first_crossed_line_distance(
tcb::span<const Line> xy_path, const AABBTreeLines::LinesDistancer<Linef> &distancer
);
double get_first_crossed_line_distance(
tcb::span<const Line> xy_path,
const AABBTreeLines::LinesDistancer<ObjectOrExtrusionLinef> &distancer,
const ObjectsLayerToPrint &objects_to_print = {},
const std::function<bool(const ObjectOrExtrusionLinef &)> &predicate = [](const ObjectOrExtrusionLinef &) { return true; },
bool ignore_starting_object_intersection = true);
/**
* @brief Extract parameters and decide wheather the travel can be elevated.
@ -93,7 +232,7 @@ Points3 generate_travel_to_extrusion(
const FullPrintConfig &config,
const unsigned extruder_id,
const double initial_elevation,
const std::optional<AABBTreeLines::LinesDistancer<Linef>> &previous_layer_distancer,
const GCode::TravelObstacleTracker &obstacle_tracker,
const Point &xy_path_coord_origin
);
} // namespace Slic3r::GCode::Impl::Travels

View File

@ -21,9 +21,9 @@ void Wipe::init(const PrintConfig &config, const std::vector<unsigned int> &extr
if (config.wipe.get_at(id)) {
// Wipe length to extrusion ratio.
const double xy_to_e = this->calc_xy_to_e_ratio(config, id);
wipe_xy = std::max(wipe_xy, xy_to_e * config.retract_length.get_at(id));
wipe_xy = std::max(wipe_xy, config.retract_length.get_at(id) / xy_to_e);
if (multimaterial)
wipe_xy = std::max(wipe_xy, xy_to_e * config.retract_length_toolchange.get_at(id));
wipe_xy = std::max(wipe_xy, config.retract_length_toolchange.get_at(id) / xy_to_e);
}
if (wipe_xy == 0)
@ -37,11 +37,11 @@ void Wipe::set_path(SmoothPath &&path, bool reversed)
this->reset_path();
if (this->enabled() && ! path.empty()) {
if (reversed) {
if (coord_t wipe_len_max_scaled = scaled(m_wipe_len_max); reversed) {
m_path = std::move(path.back().path);
Geometry::ArcWelder::reverse(m_path);
int64_t len = Geometry::ArcWelder::estimate_path_length(m_path);
for (auto it = std::next(path.rbegin()); len < m_wipe_len_max && it != path.rend(); ++ it) {
for (auto it = std::next(path.rbegin()); len < wipe_len_max_scaled && it != path.rend(); ++ it) {
if (it->path_attributes.role.is_bridge())
break; // Do not perform a wipe on bridges.
assert(it->path.size() >= 2);
@ -55,7 +55,7 @@ void Wipe::set_path(SmoothPath &&path, bool reversed)
} else {
m_path = std::move(path.front().path);
int64_t len = Geometry::ArcWelder::estimate_path_length(m_path);
for (auto it = std::next(path.begin()); len < m_wipe_len_max && it != path.end(); ++ it) {
for (auto it = std::next(path.begin()); len < wipe_len_max_scaled && it != path.end(); ++ it) {
if (it->path_attributes.role.is_bridge())
break; // Do not perform a wipe on bridges.
assert(it->path.size() >= 2);
@ -164,7 +164,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
return done;
};
// Start with the current position, which may be different from the wipe path start in case of loop clipping.
Vec2d prev = gcodegen.point_to_gcode_quantized(gcodegen.last_pos());
Vec2d prev = gcodegen.point_to_gcode_quantized(*gcodegen.last_position);
auto it = this->path().begin();
Vec2d p = gcodegen.point_to_gcode(it->point + m_offset);
++ it;
@ -192,7 +192,7 @@ std::string Wipe::wipe(GCodeGenerator &gcodegen, bool toolchange)
// add tag for processor
assert(p == GCodeFormatter::quantize(p));
gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_End) + "\n";
gcodegen.set_last_pos(gcodegen.gcode_to_point(p));
gcodegen.last_position = gcodegen.gcode_to_point(p);
}
}

View File

@ -57,12 +57,23 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
|| is_ramming
|| will_go_down); // don't dig into the print
if (should_travel_to_tower) {
const Point xy_point = wipe_tower_point_to_object_point(gcodegen, start_pos);
gcode += gcodegen.retract_and_wipe();
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
gcode += gcodegen.travel_to(
wipe_tower_point_to_object_point(gcodegen, start_pos),
ExtrusionRole::Mixed,
"Travel to a Wipe Tower");
const std::string comment{"Travel to a Wipe Tower"};
if (gcodegen.m_current_layer_first_position) {
if (gcodegen.last_position) {
gcode += gcodegen.travel_to(
*gcodegen.last_position, xy_point, ExtrusionRole::Mixed, comment
);
} else {
gcode += gcodegen.writer().travel_to_xy(gcodegen.point_to_gcode(xy_point), comment);
gcode += gcodegen.writer().get_travel_to_z_gcode(z, comment);
}
} else {
const Vec3crd point = to_3d(xy_point, scaled(z));
gcode += gcodegen.travel_to_first_position(point);
}
gcode += gcodegen.unretract();
} else {
// When this is multiextruder printer without any ramming, we can just change
@ -81,10 +92,10 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
if (is_ramming)
gcodegen.m_wipe.reset_path(); // We don't want wiping on the ramming lines.
toolchange_gcode_str = gcodegen.set_extruder(new_extruder_id, tcr.print_z); // TODO: toolchange_z vs print_z
if (gcodegen.config().wipe_tower)
deretraction_str += gcodegen.writer().get_travel_to_z_gcode(z, "restore layer Z");
if (gcodegen.config().wipe_tower) {
deretraction_str += gcodegen.writer().get_travel_to_z_gcode(tcr.print_z, "restore layer Z");
deretraction_str += gcodegen.unretract();
}
}
assert(toolchange_gcode_str.empty() || toolchange_gcode_str.back() == '\n');
assert(deretraction_str.empty() || deretraction_str.back() == '\n');
@ -98,7 +109,7 @@ std::string WipeTowerIntegration::append_tcr(GCodeGenerator &gcodegen, const Wip
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(end_pos.cast<double>());
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
gcodegen.last_position = wipe_tower_point_to_object_point(gcodegen, end_pos);
if (!is_approx(z, current_z)) {
gcode += gcodegen.writer().retract();
gcode += gcodegen.writer().travel_to_z(current_z, "Travel back up to the topmost object layer.");
@ -247,7 +258,7 @@ std::string WipeTowerIntegration::finalize(GCodeGenerator &gcodegen)
std::string gcode;
if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON)
gcode += gcodegen.generate_travel_gcode(
{{gcodegen.last_pos().x(), gcodegen.last_pos().y(), scaled(m_final_purge.print_z)}},
{{gcodegen.last_position->x(), gcodegen.last_position->y(), scaled(m_final_purge.print_z)}},
"move to safe place for purging"
);
gcode += append_tcr(gcodegen, m_final_purge, -1);

View File

@ -26,7 +26,8 @@ public:
m_tool_changes(tool_changes),
m_final_purge(final_purge),
m_layer_idx(-1),
m_tool_change_idx(0)
m_tool_change_idx(0),
m_last_wipe_tower_print_z(print_config.z_offset.value)
{}
std::string prime(GCodeGenerator &gcodegen);
@ -56,7 +57,7 @@ private:
// Current layer index.
int m_layer_idx;
int m_tool_change_idx;
double m_last_wipe_tower_print_z = 0.f;
double m_last_wipe_tower_print_z;
};
} // namespace GCode

View File

@ -120,6 +120,7 @@ static inline bool circle_approximation_sufficient(const Circle &circle, const P
return true;
}
#if 0
static inline bool get_deviation_sum_squared(const Circle &circle, const Points::const_iterator begin, const Points::const_iterator end, const double tolerance, double &total_deviation)
{
// The circle was calculated from the 1st and last point of the point sequence, thus the fitting of those points does not need to be evaluated.
@ -148,6 +149,7 @@ static inline bool get_deviation_sum_squared(const Circle &circle, const Points:
return true;
}
#endif
double arc_fit_variance(const Point &start_pos, const Point &end_pos, const float radius, bool is_ccw,
const Points::const_iterator begin, const Points::const_iterator end)

View File

@ -14,7 +14,6 @@
#include "Point.hpp"
#include "Polygon.hpp"
#include "Print.hpp"
#include "Fill/Fill.hpp"
#include "ShortestPath.hpp"
#include "SVG.hpp"
#include "BoundingBox.hpp"
@ -874,12 +873,14 @@ void Layer::sort_perimeters_into_islands(
int sort_region_id = -1;
// Temporary vector of fills for reordering.
ExPolygons fills_temp;
// Temporary vector of fill_bboxes for reordering.
BoundingBoxes fill_bboxes_temp;
// Vector of new positions of the above.
std::vector<int> new_positions;
do {
sort_region_id = -1;
for (size_t source_slice_idx = 0; source_slice_idx < fill_expolygons_ranges.size(); ++ source_slice_idx)
if (ExPolygonRange fill_range = fill_expolygons_ranges[source_slice_idx]; fill_range.size() > 1) {
if (const ExPolygonRange fill_range = fill_expolygons_ranges[source_slice_idx]; fill_range.size() > 1) {
// More than one expolygon exists for a single island. Check whether they are contiguous inside a single LayerRegion::fill_expolygons() vector.
uint32_t fill_idx = *fill_range.begin();
if (const int fill_regon_id = map_expolygon_to_region_and_fill[fill_idx].region_id; fill_regon_id != -1) {
@ -916,15 +917,23 @@ void Layer::sort_perimeters_into_islands(
// Not referenced by any map_expolygon_to_region_and_fill.
new_pos = last ++;
// Move just the content of m_fill_expolygons to fills_temp, but don't move the container vector.
auto &fills = layerm.m_fill_expolygons;
auto &fills = layerm.m_fill_expolygons;
auto &fill_bboxes = layerm.m_fill_expolygons_bboxes;
assert(fills.size() == fill_bboxes.size());
assert(last == int(fills.size()));
fills_temp.reserve(fills.size());
fills_temp.insert(fills_temp.end(), std::make_move_iterator(fills.begin()), std::make_move_iterator(fills.end()));
for (ExPolygon &ex : fills)
ex.clear();
// Move / reoder the expolygons back into m_fill_expolygons.
for (size_t old_pos = 0; old_pos < new_positions.size(); ++ old_pos)
fills[new_positions[old_pos]] = std::move(fills_temp[old_pos]);
fills_temp.resize(fills.size());
fills_temp.assign(std::make_move_iterator(fills.begin()), std::make_move_iterator(fills.end()));
fill_bboxes_temp.resize(fill_bboxes.size());
fill_bboxes_temp.assign(std::make_move_iterator(fill_bboxes.begin()), std::make_move_iterator(fill_bboxes.end()));
// Move / reorder the ExPolygons and BoundingBoxes back into m_fill_expolygons and m_fill_expolygons_bboxes.
for (size_t old_pos = 0; old_pos < new_positions.size(); ++old_pos) {
fills[new_positions[old_pos]] = std::move(fills_temp[old_pos]);
fill_bboxes[new_positions[old_pos]] = std::move(fill_bboxes_temp[old_pos]);
}
}
} while (sort_region_id != -1);
} else {
@ -956,7 +965,7 @@ void Layer::sort_perimeters_into_islands(
// Check whether the fill expolygons of this island were split into multiple regions.
island.fill_region_id = LayerIsland::fill_region_composite_id;
for (uint32_t fill_idx : fill_range) {
if (const int fill_regon_id = map_expolygon_to_region_and_fill[fill_idx].region_id;
if (const int fill_regon_id = map_expolygon_to_region_and_fill[fill_idx].region_id;
fill_regon_id == -1 || (island.fill_region_id != LayerIsland::fill_region_composite_id && int(island.fill_region_id) != fill_regon_id)) {
island.fill_region_id = LayerIsland::fill_region_composite_id;
break;

View File

@ -377,8 +377,6 @@ public:
return false;
}
void make_perimeters();
// Phony version of make_fills() without parameters for Perl integration only.
void make_fills() { this->make_fills(nullptr, nullptr, nullptr); }
void make_fills(FillAdaptive::Octree *adaptive_fill_octree,
FillAdaptive::Octree *support_fill_octree,
FillLightning::Generator *lightning_generator);

View File

@ -280,6 +280,7 @@ class Linef
public:
Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {}
Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {}
virtual ~Linef() = default;
Vec2d a;
Vec2d b;

View File

@ -425,18 +425,21 @@ bool Model::looks_like_multipart_object() const
{
if (this->objects.size() <= 1)
return false;
double zmin = std::numeric_limits<double>::max();
BoundingBoxf3 tbb;
for (const ModelObject *obj : this->objects) {
if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
return false;
for (const ModelVolume *vol : obj->volumes) {
double zmin_this = vol->mesh().bounding_box().min(2);
if (zmin == std::numeric_limits<double>::max())
zmin = zmin_this;
else if (std::abs(zmin - zmin_this) > EPSILON)
// The volumes don't share zmin.
return true;
}
BoundingBoxf3 bb_this = obj->volumes[0]->mesh().bounding_box();
BoundingBoxf3 tbb_this = obj->instances[0]->transform_bounding_box(bb_this);
if (!tbb.defined)
tbb = tbb_this;
else if (tbb.intersects(tbb_this) || tbb.shares_boundary(tbb_this))
// The volumes has intersects bounding boxes or share some boundary
return true;
}
return false;
}

View File

@ -2252,7 +2252,6 @@ size_t ExtruderFilaments::update_compatible_internal(const PresetWithVendorProfi
const ConfigOption* opt = active_printer.preset.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
bool some_compatible = false;
// Adjust printer preset config to the first extruder from m_extruder_id
Preset printer_preset_adjusted = active_printer.preset;
@ -2280,7 +2279,6 @@ size_t ExtruderFilaments::update_compatible_internal(const PresetWithVendorProfi
const PresetWithVendorProfile this_preset_with_vendor_profile = m_filaments->get_preset_with_vendor_profile(*preset);
bool was_compatible = extr_filament.is_compatible;
extr_filament.is_compatible = is_compatible_with_printer(this_preset_with_vendor_profile, active_printer_adjusted, &config);
some_compatible |= extr_filament.is_compatible;
if (active_print != nullptr)
extr_filament.is_compatible &= is_compatible_with_print(this_preset_with_vendor_profile, *active_print, active_printer_adjusted);
if (!extr_filament.is_compatible && is_selected &&

View File

@ -639,10 +639,6 @@ public:
// If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const;
// For Perl bindings.
PrintObjectPtrs& objects_mutable() { return m_objects; }
PrintRegionPtrs& print_regions_mutable() { return m_print_regions; }
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; }
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.

View File

@ -78,7 +78,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
cfg.opt_string("input_filename_base") + default_ext :
this->placeholder_parser().process(format, 0, &cfg);
if (filename.extension().empty())
filename = boost::filesystem::change_extension(filename, default_ext);
filename.replace_extension(default_ext);
return filename.string();
} catch (std::runtime_error &err) {
throw Slic3r::PlaceholderParserError(_u8L("Failed processing of the output_filename_format template.") + "\n" + err.what());

View File

@ -4889,9 +4889,15 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->cli = "opengl-version";
def->set_default_value(new ConfigOptionString());
def = this->add("opengl-compatibility", coBool);
def->label = L("OpenGL compatibility profile");
def->tooltip = L("Enable OpenGL compatibility profile");
def->cli = "opengl-compatibility";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("opengl-debug", coBool);
def->label = L("OpenGL debug output");
def->tooltip = L("Activate OpenGL debug output on graphic cards which support it");
def->tooltip = L("Activate OpenGL debug output on graphic cards which support it (OpenGL 4.3 or higher)");
def->cli = "opengl-debug";
def->set_default_value(new ConfigOptionBool(false));
#endif // ENABLE_GL_CORE_PROFILE
@ -5066,6 +5072,11 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->label = L("Data directory");
def->tooltip = L("Load and store settings at the given directory. This is useful for maintaining different profiles or including configurations from a network storage.");
def = this->add("threads", coInt);
def->label = L("Maximum number of threads");
def->tooltip = L("Sets the maximum number of threads the slicing process will use. If not defined, slic3r will decide.");
def->min = 1;
def = this->add("loglevel", coInt);
def->label = L("Logging level");
def->tooltip = L("Sets logging sensitivity. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:trace\n"

View File

@ -244,12 +244,16 @@ private:
*/
std::optional<std::reference_wrapper<const Polygons>> getArea(const TreeModelVolumes::RadiusLayerPair &key) const {
std::lock_guard<std::mutex> guard(m_mutex);
if (key.second >= LayerIndex(m_data.size()))
return std::optional<std::reference_wrapper<const Polygons>>{};
const auto &layer = m_data[key.second];
return std::nullopt;
const LayerData &layer = m_data[key.second];
auto it = layer.find(key.first);
return it == layer.end() ?
std::optional<std::reference_wrapper<const Polygons>>{} : std::optional<std::reference_wrapper<const Polygons>>{ it->second };
if (it == layer.end())
return std::nullopt;
return std::optional<std::reference_wrapper<const Polygons>>{it->second};
}
// Get a collision area at a given layer for a radius that is a lower or equial to the key radius.
std::optional<std::pair<coord_t, std::reference_wrapper<const Polygons>>> get_lower_bound_area(const TreeModelVolumes::RadiusLayerPair &key) const {

View File

@ -19,7 +19,6 @@
#include "../BuildVolume.hpp"
#include "../ClipperUtils.hpp"
#include "../EdgeGrid.hpp"
#include "../Fill/Fill.hpp"
#include "../Layer.hpp"
#include "../Print.hpp"
#include "../MultiPoint.hpp"

View File

@ -55,8 +55,6 @@
#define ENABLE_OPENGL_ES 0
// Enable OpenGL core profile context (tested against Mesa 20.1.8 on Windows)
#define ENABLE_GL_CORE_PROFILE (1 && !ENABLE_OPENGL_ES)
// Enable OpenGL debug messages using debug context
#define ENABLE_OPENGL_DEBUG_OPTION (1 && ENABLE_GL_CORE_PROFILE)
// Enable imgui dialog which allows to set the parameters used to export binarized gcode
#define ENABLE_BINARIZED_GCODE_DEBUG_WINDOW 0

View File

@ -235,12 +235,11 @@ void name_tbb_thread_pool_threads_set_locale()
// const size_t nthreads_hw = std::thread::hardware_concurrency();
const size_t nthreads_hw = tbb::this_task_arena::max_concurrency();
size_t nthreads = nthreads_hw;
if (thread_count) {
nthreads = std::min(nthreads_hw, *thread_count);
}
#if 0
// Shiny profiler is not thread safe, thus disable parallelization.
disable_multi_threading();
nthreads = 1;
#endif
enforce_thread_count(nthreads);
size_t nthreads_running(0);
std::condition_variable cv;

View File

@ -21,6 +21,7 @@ namespace boost { namespace filesystem { class directory_entry; }}
namespace Slic3r {
inline std::optional<std::size_t> thread_count;
extern void set_logging_level(unsigned int level);
extern unsigned get_logging_level();
// Format memory allocated, separate thousands by comma.
@ -29,7 +30,7 @@ extern std::string format_memsize_MB(size_t n);
// The string is non-empty if the loglevel >= info (3) or ignore_loglevel==true.
// Latter is used to get the memory info from SysInfoDialog.
extern std::string log_memory_info(bool ignore_loglevel = false);
extern void disable_multi_threading();
extern void enforce_thread_count(std::size_t count);
// Returns the size of physical memory (RAM) in bytes.
extern size_t total_physical_memory();
@ -73,10 +74,6 @@ const std::string& data_dir();
// so the user knows where to search for the debugging output.
std::string debug_out_path(const char *name, ...);
// A special type for strings encoded in the local Windows 8-bit code page.
// This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded.
typedef std::string local_encoded_string;
// Returns next utf8 sequence length. =number of bytes in string, that creates together one utf-8 character.
// Starting at pos. ASCII characters returns 1. Works also if pos is in the middle of the sequence.
extern size_t get_utf8_sequence_length(const std::string& text, size_t pos = 0);

View File

@ -124,23 +124,22 @@ unsigned get_logging_level()
}
// Force set_logging_level(<=error) after loading of the DLL.
// This is currently only needed if libslic3r is loaded as a shared library into Perl interpreter
// to perform unit and integration tests.
// This is used ot disable logging for unit and integration tests.
static struct RunOnInit {
RunOnInit() {
RunOnInit() {
set_logging_level(1);
}
} g_RunOnInit;
void disable_multi_threading()
void enforce_thread_count(const std::size_t count)
{
// Disable parallelization to simplify debugging.
#ifdef TBB_HAS_GLOBAL_CONTROL
{
static tbb::global_control gc(tbb::global_control::max_allowed_parallelism, 1);
static tbb::global_control gc(tbb::global_control::max_allowed_parallelism, count);
}
#else // TBB_HAS_GLOBAL_CONTROL
static tbb::task_scheduler_init *tbb_init = new tbb::task_scheduler_init(1);
static tbb::task_scheduler_init *tbb_init = new tbb::task_scheduler_init(count);
UNUSED(tbb_init);
#endif // TBB_HAS_GLOBAL_CONTROL
}
@ -707,7 +706,7 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
// That may happen when copying on some exotic file system, for example Linux on Chrome.
copy_file_linux(source, target, ec);
#else // __linux__
boost::filesystem::copy_file(source, target, boost::filesystem::copy_option::overwrite_if_exists, ec);
boost::filesystem::copy_file(source, target, boost::filesystem::copy_options::overwrite_existing, ec);
#endif // __linux__
if (ec) {
error_message = ec.message();

View File

@ -2785,8 +2785,10 @@ bool ConfigWizard::priv::on_bnt_finish()
* than last changes wouldn't be updated for filaments/materials.
* SO, do that before check_and_install_missing_materials()
*/
page_filaments->check_and_update_presets();
page_sla_materials->check_and_update_presets();
if (page_filaments)
page_filaments->check_and_update_presets();
if (page_sla_materials)
page_sla_materials->check_and_update_presets();
// there's no need to check that filament is selected if we have only custom printer
if (custom_printer_selected && !any_fff_selected && !any_sla_selected) return true;
@ -2998,7 +3000,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
}
}
}
return pt;
return ptAny;
};
// Prusa printers are considered first, then 3rd party.
if (preferred_pt = get_preferred_printer_technology("PrusaResearch", bundles.prusa_bundle());

View File

@ -2239,7 +2239,7 @@ static std::string get_new_color(const std::string& color)
data->SetChooseFull(1);
data->SetColour(clr);
wxColourDialog dialog(nullptr, data);
wxColourDialog dialog(GUI::wxGetApp().GetTopWindow(), data);
dialog.CenterOnParent();
if (dialog.ShowModal() == wxID_OK)
return dialog.GetColourData().GetColour().GetAsString(wxC2S_HTML_SYNTAX).ToStdString();

View File

@ -138,7 +138,7 @@ void FileGet::priv::get_perform()
if (m_written == 0)
{
boost::filesystem::path dest_path = m_dest_folder / m_filename;
std::string extension = boost::filesystem::extension(dest_path);
std::string extension = dest_path.extension().string();
std::string just_filename = m_filename.substr(0, m_filename.size() - extension.size());
std::string final_filename = just_filename;
// Find unsed filename

View File

@ -4912,8 +4912,9 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
camera.apply_projection(volumes_box, near_z, far_z);
const ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects;
std::vector<ColorRGBA> extruders_colors = get_extruders_colors();
const ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects;
std::vector<ColorRGBA> extruders_colors = get_extruders_colors();
const bool is_enabled_painted_thumbnail = !model_objects.empty() && !extruders_colors.empty();
if (thumbnail_params.transparent_background)
glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f));
@ -4928,8 +4929,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
for (GLVolume *vol : visible_volumes) {
const int obj_idx = vol->object_idx();
const int vol_idx = vol->volume_idx();
const bool render_as_painted = (obj_idx >= 0 && vol_idx >= 0) ?
!model_objects[obj_idx]->volumes[vol_idx]->mmu_segmentation_facets.empty() : false;
const bool render_as_painted = is_enabled_painted_thumbnail && obj_idx >= 0 && vol_idx >= 0 && !model_objects[obj_idx]->volumes[vol_idx]->mmu_segmentation_facets.empty();
GLShaderProgram* shader = wxGetApp().get_shader(render_as_painted ? "mm_gouraud" : "gouraud_light");
if (shader == nullptr)
continue;
@ -4962,7 +4962,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
glsafe(::glFrontFace(GL_CW));
if (render_as_painted) {
const ModelVolume& model_volume = *model_objects[obj_idx]->volumes[vol_idx];
const ModelVolume& model_volume = *model_objects[obj_idx]->volumes[vol_idx];
const size_t extruder_idx = get_extruder_color_idx(model_volume, extruders_count);
TriangleSelectorMmGui ts(model_volume.mesh(), extruders_colors, extruders_colors[extruder_idx]);
ts.deserialize(model_volume.mmu_segmentation_facets.get_data(), true);

View File

@ -49,7 +49,6 @@ extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_
void show_error(wxWindow* parent, const wxString& message, bool monospaced_font = false);
void show_error(wxWindow* parent, const char* message, bool monospaced_font = false);
inline void show_error(wxWindow* parent, const std::string& message, bool monospaced_font = false) { show_error(parent, message.c_str(), monospaced_font); }
void show_error_id(int id, const std::string& message); // For Perl
void show_info(wxWindow* parent, const wxString& message, const wxString& title = wxString());
void show_info(wxWindow* parent, const char* message, const char* title = nullptr);
inline void show_info(wxWindow* parent, const std::string& message,const std::string& title = std::string()) { show_info(parent, message.c_str(), title.c_str()); }

View File

@ -567,7 +567,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
return file_wildcards(file_wildcards_by_type[file_type], custom_extension);
}
wxString sla_wildcards(const char *formatid)
wxString sla_wildcards(const char *formatid, const std::string& custom_extension)
{
const ArchiveEntry *entry = get_archive_entry(formatid);
wxString ret;
@ -587,11 +587,11 @@ wxString sla_wildcards(const char *formatid)
wc.file_extensions.emplace_back(ext);
}
ret = file_wildcards(wc, {});
ret = file_wildcards(wc, custom_extension);
}
if (ret.empty())
ret = file_wildcards(FT_SL1);
ret = file_wildcards(FT_SL1, custom_extension);
return ret;
}
@ -889,12 +889,8 @@ std::string GUI_App::get_gl_info(bool for_github)
wxGLContext* GUI_App::init_glcontext(wxGLCanvas& canvas)
{
#if ENABLE_GL_CORE_PROFILE
#if ENABLE_OPENGL_DEBUG_OPTION
return m_opengl_mgr.init_glcontext(canvas, init_params != nullptr ? init_params->opengl_version : std::make_pair(0, 0),
init_params != nullptr ? init_params->opengl_debug : false);
#else
return m_opengl_mgr.init_glcontext(canvas, init_params != nullptr ? init_params->opengl_version : std::make_pair(0, 0));
#endif // ENABLE_OPENGL_DEBUG_OPTION
init_params != nullptr ? init_params->opengl_compatibiity_profile : false, init_params != nullptr ? init_params->opengl_debug : false);
#else
return m_opengl_mgr.init_glcontext(canvas);
#endif // ENABLE_GL_CORE_PROFILE

View File

@ -86,7 +86,7 @@ enum FileType
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = {});
wxString sla_wildcards(const char *formatid);
wxString sla_wildcards(const char *formatid, const std::string& custom_extension);
enum ConfigMenuIDs {
ConfigMenuWizard,
@ -415,7 +415,6 @@ private:
bool m_datadir_redefined { false };
bool m_wifi_config_dialog_shown { false };
bool m_wifi_config_dialog_was_declined { false };
};
DECLARE_APP(GUI_App)

View File

@ -30,11 +30,7 @@
namespace Slic3r {
namespace GUI {
const std::vector<std::string> OpenGLVersions::core_str = { "3.2", "3.3", "4.0", "4.1", "4.2", "4.3", "4.4", "4.5", "4.6" };
const std::vector<std::string> OpenGLVersions::precore_str = { "2.0", "2.1", "3.0", "3.1" };
const std::vector<std::pair<int, int>> OpenGLVersions::core = { {3,2}, {3,3}, {4,0}, {4,1}, {4,2}, {4,3}, {4,4}, {4,5}, {4,6} };
const std::vector<std::pair<int, int>> OpenGLVersions::precore = { {2,0}, {2,1}, {3,0}, {3,1} };
int GUI_Run(GUI_InitParams &params)
{

View File

@ -14,11 +14,7 @@ namespace GUI {
struct OpenGLVersions
{
static const std::vector<std::string> core_str;
static const std::vector<std::string> precore_str;
static const std::vector<std::pair<int, int>> core;
static const std::vector<std::pair<int, int>> precore;
};
struct GUI_InitParams
@ -33,15 +29,14 @@ struct GUI_InitParams
DynamicPrintConfig extra_config;
std::vector<std::string> input_files;
bool start_as_gcodeviewer;
bool start_downloader;
bool delete_after_load;
bool start_as_gcodeviewer;
bool start_downloader;
bool delete_after_load;
std::string download_url;
#if ENABLE_GL_CORE_PROFILE
std::pair<int, int> opengl_version;
#if ENABLE_OPENGL_DEBUG_OPTION
bool opengl_debug;
#endif // ENABLE_OPENGL_DEBUG_OPTION
std::pair<int, int> opengl_version;
bool opengl_debug;
bool opengl_compatibiity_profile;
#endif // ENABLE_GL_CORE_PROFILE
};

View File

@ -503,7 +503,7 @@ void GalleryDialog::change_thumbnail()
png_path.replace_extension("png");
fs::path current = fs::path(into_u8(input_files.Item(0)));
fs::copy_file(current, png_path, fs::copy_option::overwrite_if_exists);
fs::copy_file(current, png_path, fs::copy_options::overwrite_existing);
}
catch (fs::filesystem_error const& e) {
std::cerr << e.what() << '\n';

View File

@ -3311,7 +3311,7 @@ static void check_objects_after_cut(const ModelObjectPtrs& objects)
for (int obj_idx : err_objects_idxs)
model_names.push_back(objects[obj_idx]->name);
auto fix_and_update_progress = [plater, model_names, &objects](const int obj_idx, int model_idx,
auto fix_and_update_progress = [model_names, &objects](const int obj_idx, int model_idx,
wxProgressDialog& progress_dlg,
std::vector<std::string>& succes_models,
std::vector<std::pair<std::string, std::string>>& failed_models) -> bool

View File

@ -77,6 +77,14 @@ using namespace Slic3r::GUI;
using namespace Slic3r::GUI::Emboss;
namespace {
// TRN - Title in Undo/Redo stack after rotate with text around emboss axe
const std::string rotation_snapshot_name = L("Text rotate");
// NOTE: Translation is made in "m_parent.do_rotate()"
// TRN - Title in Undo/Redo stack after move with text along emboss axe - From surface
const std::string move_snapshot_name = L("Text move");
// NOTE: Translation is made in "m_parent.do_translate()"
template<typename T> struct Limit {
// Limitation for view slider range in GUI
MinMax<T> gui;
@ -88,7 +96,7 @@ static const struct Limits
{
MinMax<double> emboss{0.01, 1e4}; // in mm
MinMax<float> size_in_mm{0.1f, 1000.f}; // in mm
Limit<float> boldness{{-200.f, 200.f}, {-2e4f, 2e4f}}; // in font points
Limit<float> boldness{{-.5f, .5f}, {-5e5f, 5e5f}}; // in font points
Limit<float> skew{{-1.f, 1.f}, {-100.f, 100.f}}; // ration without unit
MinMax<int> char_gap{-20000, 20000}; // in font points
MinMax<int> line_gap{-20000, 20000}; // in font points
@ -372,7 +380,7 @@ bool GLGizmoEmboss::re_emboss(const ModelVolume &text_volume, std::shared_ptr<st
TextLinesModel text_lines;
const Selection &selection = wxGetApp().plater()->canvas3D()->get_selection();
DataBasePtr base = create_emboss_data_base(tc.text, style_manager, text_lines, selection, text_volume.type(), job_cancel);
DataUpdate data{std::move(base), text_volume.id()};
DataUpdate data{std::move(base), text_volume.id(), false};
RaycastManager raycast_manager; // Nothing is cached now, so It need to create raycasters
return start_update_volume(std::move(data), text_volume, selection, raycast_manager);
@ -978,7 +986,7 @@ void GLGizmoEmboss::on_stop_dragging()
m_rotate_gizmo.set_angle(PI/2);
// apply rotation
m_parent.do_rotate(L("Text-Rotate"));
m_parent.do_rotate(rotation_snapshot_name);
m_rotate_start_angle.reset();
volume_transformation_changed();
}
@ -1308,7 +1316,7 @@ namespace {
bool is_text_empty(std::string_view text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; }
} // namespace
bool GLGizmoEmboss::process()
bool GLGizmoEmboss::process(bool make_snapshot)
{
// no volume is selected -> selection from right panel
assert(m_volume != nullptr);
@ -1322,7 +1330,7 @@ bool GLGizmoEmboss::process()
const Selection& selection = m_parent.get_selection();
DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, m_text_lines, selection, m_volume->type(), m_job_cancel);
DataUpdate data{std::move(base), m_volume->id()};
DataUpdate data{std::move(base), m_volume->id(), make_snapshot};
// check valid count of text lines
assert(data.base->text_lines.empty() || data.base->text_lines.size() == get_count_lines(m_text));
@ -2139,7 +2147,9 @@ void fix_transformation(const StyleManager::Style &from, const StyleManager::Sty
// fix rotation
float f_angle = f_angle_opt.value_or(.0f);
float t_angle = t_angle_opt.value_or(.0f);
do_local_z_rotate(canvas, t_angle - f_angle);
do_local_z_rotate(canvas.get_selection(), t_angle - f_angle);
std::string no_snapshot;
canvas.do_rotate(no_snapshot);
}
// fix distance (Z move) when exists difference in styles
@ -2148,7 +2158,9 @@ void fix_transformation(const StyleManager::Style &from, const StyleManager::Sty
if (!is_approx(f_move_opt, t_move_opt)) {
float f_move = f_move_opt.value_or(.0f);
float t_move = t_move_opt.value_or(.0f);
do_local_z_move(canvas, t_move - f_move);
do_local_z_move(canvas.get_selection(), t_move - f_move);
std::string no_snapshot;
canvas.do_move(no_snapshot);
}
}
} // namesapce
@ -2412,6 +2424,10 @@ bool GLGizmoEmboss::revertible(const std::string &name,
ImGui::SameLine(undo_offset); // change cursor postion
if (draw_button(m_icons, IconType::undo)) {
value = *default_value;
// !! Fix to detect change of value after revert of float-slider
m_imgui->get_last_slider_status().deactivated_after_edit = true;
return true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("%s", undo_tooltip.c_str());
@ -2649,7 +2665,6 @@ void GLGizmoEmboss::draw_advanced()
m_imgui->text_colored(ImGuiWrapper::COL_GREY_DARK, ff_property);
#endif // SHOW_FONT_FILE_PROPERTY
bool exist_change = false;
auto &tr = m_gui_cfg->translations;
const StyleManager::Style *stored_style = nullptr;
@ -2741,6 +2756,7 @@ void GLGizmoEmboss::draw_advanced()
auto def_char_gap = stored_style ?
&stored_style->prop.char_gap : nullptr;
bool exist_change = false;
int half_ascent = font_info.ascent / 2;
int min_char_gap = -half_ascent;
int max_char_gap = half_ascent;
@ -2756,13 +2772,16 @@ void GLGizmoEmboss::draw_advanced()
exist_change = true;
}
}
bool last_change = false;
if (m_imgui->get_last_slider_status().deactivated_after_edit)
last_change = true;
// input gap between lines
bool is_multiline = m_text_lines.get_lines().size() > 1;
bool is_multiline = get_count_lines(m_volume->text_configuration->text) > 1; // TODO: cache count lines
m_imgui->disabled_begin(!is_multiline);
auto def_line_gap = stored_style ?
&stored_style->prop.line_gap : nullptr;
int min_line_gap = -half_ascent;
int min_line_gap = -half_ascent;
int max_line_gap = half_ascent;
if (rev_slider(tr.line_gap, current_prop.line_gap, def_line_gap, _u8L("Revert gap between lines"),
min_line_gap, max_line_gap, units_fmt, _L("Distance between lines"))){
@ -2777,18 +2796,24 @@ void GLGizmoEmboss::draw_advanced()
exist_change = true;
}
}
if (m_imgui->get_last_slider_status().deactivated_after_edit)
last_change = true;
m_imgui->disabled_end(); // !is_multiline
// input boldness
auto def_boldness = stored_style ?
&stored_style->prop.boldness : nullptr;
int min_boldness = static_cast<int>(font_info.ascent * limits.boldness.gui.min);
int max_boldness = static_cast<int>(font_info.ascent * limits.boldness.gui.max);
if (rev_slider(tr.boldness, current_prop.boldness, def_boldness, _u8L("Undo boldness"),
limits.boldness.gui.min, limits.boldness.gui.max, units_fmt, _L("Tiny / Wide glyphs"))){
min_boldness, max_boldness, units_fmt, _L("Tiny / Wide glyphs"))){
const std::optional<float> &volume_boldness = m_volume->text_configuration->style.prop.boldness;
if (!apply(current_prop.boldness, limits.boldness.values) ||
!volume_boldness.has_value() || volume_boldness != current_prop.boldness)
exist_change = true;
}
if (m_imgui->get_last_slider_status().deactivated_after_edit)
last_change = true;
// input italic
auto def_skew = stored_style ?
@ -2800,6 +2825,8 @@ void GLGizmoEmboss::draw_advanced()
!volume_skew.has_value() ||volume_skew != current_prop.skew)
exist_change = true;
}
if (m_imgui->get_last_slider_status().deactivated_after_edit)
last_change = true;
// input surface distance
bool allowe_surface_distance = !use_surface && !m_volume->is_the_only_one_part();
@ -2839,11 +2866,19 @@ void GLGizmoEmboss::draw_advanced()
if (is_moved){
if (font_prop.per_glyph){
process();
process(false);
} else {
do_local_z_move(m_parent, distance.value_or(.0f) - prev_distance);
}
do_local_z_move(m_parent.get_selection(), distance.value_or(.0f) - prev_distance);
}
}
// Apply move to model(backend)
if (m_imgui->get_last_slider_status().deactivated_after_edit) {
m_parent.do_move(move_snapshot_name);
if (font_prop.per_glyph)
process();
}
m_imgui->disabled_end(); // allowe_surface_distance
// slider for Clock-wise angle in degress
@ -2865,7 +2900,7 @@ void GLGizmoEmboss::draw_advanced()
Geometry::to_range_pi_pi(angle_rad);
double diff_angle = angle_rad - angle;
do_local_z_rotate(m_parent, diff_angle);
do_local_z_rotate(m_parent.get_selection(), diff_angle);
// calc angle after rotation
const Selection &selection = m_parent.get_selection();
@ -2880,6 +2915,15 @@ void GLGizmoEmboss::draw_advanced()
// recalculate for surface cut
if (use_surface || font_prop.per_glyph)
process(false);
}
// Apply rotation on model (backend)
if (m_imgui->get_last_slider_status().deactivated_after_edit) {
m_parent.do_rotate(rotation_snapshot_name);
// recalculate for surface cut
if (use_surface || font_prop.per_glyph)
process();
}
@ -2913,6 +2957,7 @@ void GLGizmoEmboss::draw_advanced()
if (i == 0) current_prop.collection_number.reset();
else current_prop.collection_number = i;
exist_change = true;
last_change = true;
}
ImGui::PopID();
}
@ -2922,13 +2967,13 @@ void GLGizmoEmboss::draw_advanced()
}
}
if (exist_change) {
if (exist_change || last_change) {
m_style_manager.clear_glyphs_cache();
if (m_style_manager.get_font_prop().per_glyph)
if (font_prop.per_glyph)
reinit_text_lines();
else
m_text_lines.reset();
process();
process(last_change);
}
if (ImGui::Button(_u8L("Set text to face camera").c_str())) {
@ -3582,7 +3627,7 @@ GuiCfg create_gui_configuration()
float space = line_height_with_spacing - line_height;
const ImGuiStyle &style = ImGui::GetStyle();
cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x;
cfg.max_style_name_width = ImGui::CalcTextSize("Maximal style name..").x;
cfg.icon_width = static_cast<unsigned int>(std::ceil(line_height));
// make size pair number
@ -3701,11 +3746,11 @@ GuiCfg create_gui_configuration()
ImVec2(cfg.minimal_window_size_with_advance.x,
cfg.minimal_window_size_with_advance.y + input_height);
int max_style_image_width = static_cast<int>(std::round(cfg.max_style_name_width/2 - 2 * style.FramePadding.x));
int max_style_image_width = static_cast<int>(std::round(cfg.max_style_name_width - 2 * style.FramePadding.x));
int max_style_image_height = static_cast<int>(std::round(1.5 * input_height));
cfg.max_style_image_size = Vec2i(max_style_image_width, max_style_image_height);
cfg.face_name_size = Vec2i(cfg.input_width, line_height_with_spacing);
cfg.face_name_texture_offset_x = cfg.face_name_size.x() + space;
cfg.face_name_texture_offset_x = cfg.face_name_size.x() + style.WindowPadding.x + space;
return cfg;
}
} // namespace

View File

@ -114,7 +114,7 @@ private:
void reset_volume();
// create volume from text - main functionality
bool process();
bool process(bool make_snapshot = true);
void close();
void draw_window();
void draw_text_input();

Some files were not shown because too many files have changed in this diff Show More