Merge branch 'master' into tm_arrange_selection

This commit is contained in:
tamasmeszaros 2019-07-17 17:25:31 +02:00
commit a695dec51a
185 changed files with 9235 additions and 5047 deletions

View File

@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 75
ColumnLimit: 78
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true

View File

@ -169,8 +169,19 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
add_compile_options(-Werror=return-type)
#removes LOTS of extraneous Eigen warnings
# add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
#removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1)
#if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1)
# add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
#endif()
#GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
# We will turn the warning of for GCC for now:
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_compile_options(-Wno-unknown-pragmas)
endif()
if (SLIC3R_ASAN)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
@ -344,6 +355,10 @@ if (NOT GLEW_FOUND)
endif ()
include_directories(${GLEW_INCLUDE_DIRS})
# Find the Cereal serialization library
add_library(cereal INTERFACE)
target_include_directories(cereal INTERFACE include)
# l10n
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization")
add_custom_target(pot

4
deps/CMakeLists.txt vendored
View File

@ -89,6 +89,7 @@ if (MSVC)
dep_libcurl
dep_wxwidgets
dep_gtest
dep_cereal
dep_nlopt
# dep_qhull # Experimental
dep_zlib # on Windows we still need zlib
@ -103,9 +104,10 @@ else()
dep_libcurl
dep_wxwidgets
dep_gtest
dep_cereal
dep_nlopt
dep_qhull
dep_libigl
# dep_libigl # Not working, static build has different Eigen
)
endif()

View File

@ -19,6 +19,16 @@ ExternalProject_Add(dep_gtest
CMAKE_ARGS -DBUILD_GMOCK=OFF ${DEP_CMAKE_OPTS} -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
)
ExternalProject_Add(dep_cereal
EXCLUDE_FROM_ALL 1
URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz"
# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
CMAKE_ARGS
-DJUST_INSTALL_CEREAL=on
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
${DEP_CMAKE_OPTS}
)
ExternalProject_Add(dep_nlopt
EXCLUDE_FROM_ALL 1
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"

View File

@ -115,6 +115,20 @@ if (${DEP_DEBUG})
endif ()
ExternalProject_Add(dep_cereal
EXCLUDE_FROM_ALL 1
URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz"
# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
CMAKE_ARGS
-DJUST_INSTALL_CEREAL=on
"-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local"
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
INSTALL_COMMAND ""
)
ExternalProject_Add(dep_nlopt
EXCLUDE_FROM_ALL 1
URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz"

View File

@ -20,6 +20,9 @@ You can also customize the bundle output path using the `-DDESTDIR=<some path>`
**Warning**: Once the dependency bundle is installed in a destdir, the destdir cannot be moved elsewhere.
(This is because wxWidgets hardcodes the installation path.)
FIXME The Cereal serialization library needs a tiny patch on some old OSX clang installations
https://github.com/USCiLab/cereal/issues/339#issuecomment-246166717
### Building PrusaSlicer

View File

@ -50,7 +50,6 @@ use Slic3r::Point;
use Slic3r::Polygon;
use Slic3r::Polyline;
use Slic3r::Print::Object;
use Slic3r::Print::Simple;
use Slic3r::Surface;
our $build = eval "use Slic3r::Build; 1";

View File

@ -1,104 +0,0 @@
# A simple wrapper to quickly print a single model without a GUI.
# Used by the command line slic3r.pl, by command line utilities pdf-slic3s.pl and view-toolpaths.pl,
# and by the quick slice menu of the Slic3r GUI.
#
# It creates and owns an instance of Slic3r::Print to perform the slicing
# and it accepts an instance of Slic3r::Model from the outside.
package Slic3r::Print::Simple;
use Moo;
use Slic3r::Geometry qw(X Y);
has '_print' => (
is => 'ro',
default => sub { Slic3r::Print->new },
handles => [qw(apply_config_perl_tests_only extruders output_filepath
total_used_filament total_extruded_volume
placeholder_parser process)],
);
has 'duplicate' => (
is => 'rw',
default => sub { 1 },
);
has 'scale' => (
is => 'rw',
default => sub { 1 },
);
has 'rotate' => (
is => 'rw',
default => sub { 0 },
);
has 'duplicate_grid' => (
is => 'rw',
default => sub { [1,1] },
);
has 'print_center' => (
is => 'rw',
default => sub { Slic3r::Pointf->new(100,100) },
);
has 'dont_arrange' => (
is => 'rw',
default => sub { 0 },
);
has 'output_file' => (
is => 'rw',
);
sub set_model {
# $model is of type Slic3r::Model
my ($self, $model) = @_;
# make method idempotent so that the object is reusable
$self->_print->clear_objects;
# make sure all objects have at least one defined instance
my $need_arrange = $model->add_default_instances && ! $self->dont_arrange;
# apply scaling and rotation supplied from command line if any
foreach my $instance (map @{$_->instances}, @{$model->objects}) {
$instance->set_scaling_factor($instance->scaling_factor * $self->scale);
$instance->set_rotation($instance->rotation + $self->rotate);
}
if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) {
$model->duplicate_objects_grid($self->duplicate_grid->[X], $self->duplicate_grid->[Y], $self->_print->config->duplicate_distance);
} elsif ($need_arrange) {
$model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance);
} elsif ($self->duplicate > 1) {
# if all input objects have defined position(s) apply duplication to the whole model
$model->duplicate($self->duplicate, $self->_print->config->min_object_distance);
}
$_->translate(0,0,-$_->bounding_box->z_min) for @{$model->objects};
$model->center_instances_around_point($self->print_center) if (! $self->dont_arrange);
foreach my $model_object (@{$model->objects}) {
$self->_print->auto_assign_extruders($model_object);
$self->_print->add_model_object($model_object);
}
}
sub export_gcode {
my ($self) = @_;
$self->_print->validate;
$self->_print->export_gcode($self->output_file // '');
}
sub export_png {
my ($self) = @_;
$self->_before_export;
$self->_print->export_png(output_file => $self->output_file);
$self->_after_export;
}
1;

View File

@ -146,60 +146,66 @@ sub mesh {
}
sub model {
my ($model_name, %params) = @_;
my $input_file = "${model_name}.stl";
my $mesh = mesh($model_name, %params);
# $mesh->write_ascii("out/$input_file");
my ($model_names, %params) = @_;
$model_names = [ $model_names ] if ! ref($model_names);
my $model = Slic3r::Model->new;
my $object = $model->add_object(input_file => $input_file);
$model->set_material($model_name);
$object->add_volume(mesh => $mesh, material_id => $model_name);
$object->add_instance(
offset => Slic3r::Pointf->new(0,0),
# 3D full transform
rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
# old transform
# rotation => $params{rotation} // 0,
# scaling_factor => $params{scale} // 1,
);
for my $model_name (@$model_names) {
my $input_file = "${model_name}.stl";
my $mesh = mesh($model_name, %params);
# $mesh->write_ascii("out/$input_file");
my $object = $model->add_object(input_file => $input_file);
$model->set_material($model_name);
$object->add_volume(mesh => $mesh, material_id => $model_name);
$object->add_instance(
offset => Slic3r::Pointf->new(0,0),
# 3D full transform
rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
# old transform
# rotation => $params{rotation} // 0,
# scaling_factor => $params{scale} // 1,
);
}
return $model;
}
sub init_print {
my ($models, %params) = @_;
my $model;
if (ref($models) eq 'ARRAY') {
$model = model($models, %params);
} elsif (ref($models)) {
$model = $models;
} else {
$model = model([$models], %params);
}
my $config = Slic3r::Config->new;
$config->apply($params{config}) if $params{config};
$config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE};
my $print = Slic3r::Print->new;
$print->apply_config_perl_tests_only($config);
$models = [$models] if ref($models) ne 'ARRAY';
$models = [ map { ref($_) ? $_ : model($_, %params) } @$models ];
for my $model (@$models) {
die "Unknown model in test" if !defined $model;
if (defined $params{duplicate} && $params{duplicate} > 1) {
$model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
}
$model->arrange_objects($print->config->min_object_distance);
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
foreach my $model_object (@{$model->objects}) {
$print->auto_assign_extruders($model_object);
$print->add_model_object($model_object);
}
die "Unknown model in test" if !defined $model;
if (defined $params{duplicate} && $params{duplicate} > 1) {
$model->duplicate($params{duplicate} // 1, $config->min_object_distance);
}
# Call apply_config_perl_tests_only one more time, so that the layer height profiles are updated over all PrintObjects.
$print->apply_config_perl_tests_only($config);
$model->arrange_objects($config->min_object_distance);
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
foreach my $model_object (@{$model->objects}) {
$model_object->ensure_on_bed;
$print->auto_assign_extruders($model_object);
}
$print->apply($model, $config);
$print->validate;
# We return a proxy object in order to keep $models alive as required by the Print API.
return Slic3r::Test::Print->new(
print => $print,
models => $models,
print => $print,
model => $model,
);
}
@ -250,7 +256,7 @@ sub add_facet {
package Slic3r::Test::Print;
use Moo;
has 'print' => (is => 'ro', required => 1, handles => [qw(process apply_config_perl_tests_only)]);
has 'models' => (is => 'ro', required => 1);
has 'print' => (is => 'ro', required => 1, handles => [qw(process apply)]);
has 'model' => (is => 'ro', required => 1);
1;

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

12
resources/icons/redo.svg Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="redo">
<path fill="none" stroke="#ED6B21" stroke-width="2" stroke-linecap="round" stroke-miterlimit="10" d="M13.39,11
c-0.91,1.78-2.76,3-4.89,3C5.46,14,3,11.54,3,8.5C3,5.46,5.46,3,8.5,3C8.67,3,8.84,3.01,9,3.03"/>
<polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
9,1 9,5 12,3 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 734 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="redo">
<path fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" d="M13.39,11
c-0.91,1.78-2.76,3-4.89,3C5.46,14,3,11.54,3,8.5C3,5.46,5.46,3,8.5,3C8.67,3,8.84,3.01,9,3.03"/>
<polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
9,1 9,5 12,3 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 734 B

BIN
resources/icons/row.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
resources/icons/table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
<g id="undo">
<path fill="none" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-miterlimit="10" d="M3,11
c0.91,1.78,2.76,3,4.89,3c3.04,0,5.5-2.46,5.5-5.5c0-3.04-2.46-5.5-5.5-5.5c-0.17,0-0.34,0.01-0.5,0.03"/>
<polygon fill="#ED6B21" stroke="#ED6B21" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points="
7.39,1 7.39,5 4.39,3 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 746 B

View File

@ -15,7 +15,8 @@ const std::string USAGE_STR = {
namespace Slic3r { namespace sla {
Contour3D create_base_pool(const ExPolygons &ground_layer,
Contour3D create_base_pool(const Polygons &ground_layer,
const Polygons &holes = {},
const PoolConfig& cfg = PoolConfig());
Contour3D walls(const Polygon& floor_plate, const Polygon& ceiling,
@ -42,37 +43,28 @@ int main(const int argc, const char *argv[]) {
model.ReadSTLFile(argv[1]);
model.align_to_origin();
ExPolygons ground_slice;
sla::Contour3D mesh;
// TriangleMesh basepool;
Polygons ground_slice;
sla::base_plate(model, ground_slice, 0.1f);
if(ground_slice.empty()) return EXIT_FAILURE;
// ExPolygon bottom_plate = ground_slice.front();
// ExPolygon top_plate = bottom_plate;
// sla::offset(top_plate, coord_t(3.0/SCALING_FACTOR));
// sla::offset(bottom_plate, coord_t(1.0/SCALING_FACTOR));
Polygon gndfirst; gndfirst = ground_slice.front();
sla::offset_with_breakstick_holes(gndfirst, 0.5, 10, 0.3);
sla::Contour3D mesh;
bench.start();
// TriangleMesh pool;
sla::PoolConfig cfg;
cfg.min_wall_height_mm = 0;
cfg.edge_radius_mm = 0.2;
mesh = sla::create_base_pool(ground_slice, cfg);
// mesh.merge(triangulate_expolygon_3d(top_plate, 3.0, false));
// mesh.merge(triangulate_expolygon_3d(bottom_plate, 0.0, true));
// mesh = sla::walls(bottom_plate.contour, top_plate.contour, 0, 3, 2.0, [](){});
cfg.edge_radius_mm = 0;
mesh = sla::create_base_pool(ground_slice, {}, cfg);
bench.stop();
cout << "Base pool creation time: " << std::setprecision(10)
<< bench.getElapsedSec() << " seconds." << endl;
// auto point = []()
for(auto& trind : mesh.indices) {
Vec3d p0 = mesh.points[size_t(trind[0])];
Vec3d p1 = mesh.points[size_t(trind[1])];

View File

@ -75,7 +75,7 @@ if (NOT MSVC)
set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
endif ()
target_link_libraries(PrusaSlicer libslic3r)
target_link_libraries(PrusaSlicer libslic3r cereal)
if (APPLE)
# add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)

View File

@ -132,7 +132,7 @@ struct HashTableEdges {
~HashTableEdges() {
#ifndef NDEBUG
for (int i = 0; i < this->M; ++ i)
for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i])
for (HashEdge *temp = this->heads[i]; temp != this->tail; temp = temp->next)
++ this->freed;
this->tail = nullptr;
#endif /* NDEBUG */

View File

@ -41,6 +41,7 @@
static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
{
unsigned char buf[32];
(void)p;
/* Signature byte reads are always 3 bytes. */
@ -83,9 +84,9 @@ static int arduino_read_sig_bytes(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m)
static int prusa_init_external_flash(PROGRAMMER * pgm)
{
// Note: send/receive as in _the firmare_ send & receives
const char entry_magic_send [] = "start\n";
const char entry_magic_receive[] = "w25x20cl_enter\n";
const char entry_magic_cfm [] = "w25x20cl_cfm\n";
const char entry_magic_send[] = "start\n";
const unsigned char entry_magic_receive[] = "w25x20cl_enter\n";
const char entry_magic_cfm[] = "w25x20cl_cfm\n";
const size_t buffer_len = 32; // Should be large enough for the above messages
int res;
@ -94,7 +95,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
// 1. receive the "start" command
recv_size = sizeof(entry_magic_send) - 1;
res = serial_recv(&pgm->fd, buffer, recv_size);
res = serial_recv(&pgm->fd, (unsigned char *)buffer, recv_size);
if (res < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
@ -111,7 +112,7 @@ static int prusa_init_external_flash(PROGRAMMER * pgm)
// 3. Receive the entry confirmation command
recv_size = sizeof(entry_magic_cfm) - 1;
res = serial_recv(&pgm->fd, buffer, recv_size);
res = serial_recv(&pgm->fd, (unsigned char *)buffer, recv_size);
if (res < 0) {
avrdude_message(MSG_INFO, "%s: prusa_init_external_flash(): MK3 printer did not boot up on time or serial communication failed\n", progname);
return -1;
@ -142,7 +143,7 @@ static int arduino_open(PROGRAMMER * pgm, char * port)
// Sometimes there may be line noise generating input on the printer's USB-to-serial IC
// Here we try to clean its input buffer with a sequence of newlines (a minimum of 9 is needed):
const char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n";
const unsigned char cleanup_newlines[] = "\n\n\n\n\n\n\n\n\n\n";
if (serial_send(&pgm->fd, cleanup_newlines, sizeof(cleanup_newlines) - 1) < 0) {
return -1;
}

View File

@ -341,7 +341,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
avr_tpi_setup_rw(pgm, mem, 0, TPI_NVMCMD_NO_OPERATION);
/* load bytes */
for (lastaddr = i = 0; i < mem->size; i++) {
for (lastaddr = i = 0; i < (unsigned)mem->size; i++) {
RETURN_IF_CANCEL();
if (vmem == NULL ||
(vmem->tags[i] & TAG_ALLOCATED) != 0)
@ -374,7 +374,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
/* quickly scan number of pages to be written to first */
for (pageaddr = 0, npages = 0;
pageaddr < mem->size;
pageaddr < (unsigned)mem->size;
pageaddr += mem->page_size) {
/* check whether this page must be read */
for (i = pageaddr;
@ -391,7 +391,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
}
for (pageaddr = 0, failure = 0, nread = 0;
!failure && pageaddr < mem->size;
!failure && pageaddr < (unsigned)mem->size;
pageaddr += mem->page_size) {
RETURN_IF_CANCEL();
/* check whether this page must be read */
@ -437,7 +437,7 @@ int avr_read(PROGRAMMER * pgm, AVRPART * p, char * memtype,
}
}
for (i=0; i < mem->size; i++) {
for (i = 0; i < (unsigned)mem->size; i++) {
RETURN_IF_CANCEL();
if (vmem == NULL ||
(vmem->tags[i] & TAG_ALLOCATED) != 0)
@ -634,18 +634,18 @@ int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
writeop = mem->op[AVR_OP_WRITE_HI];
else
writeop = mem->op[AVR_OP_WRITE_LO];
caddr = addr / 2;
caddr = (unsigned short)(addr / 2);
}
else if (mem->paged && mem->op[AVR_OP_LOADPAGE_LO]) {
if (addr & 0x01)
writeop = mem->op[AVR_OP_LOADPAGE_HI];
else
writeop = mem->op[AVR_OP_LOADPAGE_LO];
caddr = addr / 2;
caddr = (unsigned short)(addr / 2);
}
else {
writeop = mem->op[AVR_OP_WRITE];
caddr = addr;
caddr = (unsigned short)addr;
}
if (writeop == NULL) {
@ -723,7 +723,7 @@ int avr_write_byte_default(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
gettimeofday (&tv, NULL);
prog_time = (tv.tv_sec * 1000000) + tv.tv_usec;
} while ((r != data) &&
((prog_time-start_time) < mem->max_write_delay));
((prog_time - start_time) < (unsigned long)mem->max_write_delay));
}
/*
@ -878,7 +878,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
}
/* write words, low byte first */
for (lastaddr = i = 0; i < wsize; i += 2) {
for (lastaddr = i = 0; i < (unsigned)wsize; i += 2) {
RETURN_IF_CANCEL();
if ((m->tags[i] & TAG_ALLOCATED) != 0 ||
(m->tags[i + 1] & TAG_ALLOCATED) != 0) {
@ -915,7 +915,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
/* quickly scan number of pages to be written to first */
for (pageaddr = 0, npages = 0;
pageaddr < wsize;
pageaddr < (unsigned)wsize;
pageaddr += m->page_size) {
/* check whether this page must be written to */
for (i = pageaddr;
@ -928,7 +928,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
}
for (pageaddr = 0, failure = 0, nwritten = 0;
!failure && pageaddr < wsize;
!failure && pageaddr < (unsigned)wsize;
pageaddr += m->page_size) {
RETURN_IF_CANCEL();
/* check whether this page must be written to */
@ -968,7 +968,7 @@ int avr_write(PROGRAMMER * pgm, AVRPART * p, char * memtype, int size,
page_tainted = 0;
flush_page = 0;
for (i=0; i<wsize; i++) {
for (i = 0; i < (unsigned)wsize; i++) {
RETURN_IF_CANCEL();
data = m->buf[i];
report_progress(i, wsize, NULL);

View File

@ -676,7 +676,7 @@ static int avr910_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
avr910_set_addr(pgm, addr / rd_size);
while (addr < max_addr) {
if ((max_addr - addr) < blocksize) {
if ((max_addr - addr) < (unsigned)blocksize) {
blocksize = max_addr - addr;
}
cmd[1] = (blocksize >> 8) & 0xff;

View File

@ -1,5 +1,5 @@
/* WARN: This file is auto-generated from `avrdude-slic3r.conf` */
unsigned char avrdude_slic3r_conf[] = {
const unsigned char avrdude_slic3r_conf[] = {
0x0a, 0x23, 0x0a, 0x23, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
0x20, 0x61, 0x20, 0x62, 0x61, 0x73, 0x69, 0x63, 0x20, 0x6d, 0x69, 0x6e,
0x69, 0x6d, 0x61, 0x6c, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20,
@ -1184,5 +1184,5 @@ unsigned char avrdude_slic3r_conf[] = {
0x20, 0x20, 0x3b, 0x0a, 0x0a, 0x0a,
0, 0
};
size_t avrdude_slic3r_conf_size = 14178;
size_t avrdude_slic3r_conf_size_yy = 14180;
const size_t avrdude_slic3r_conf_size = 14178;
const size_t avrdude_slic3r_conf_size_yy = 14180;

View File

@ -93,7 +93,7 @@ void AvrDude::priv::unset_handlers()
int AvrDude::priv::run_one(const std::vector<std::string> &args) {
std::vector<char*> c_args {{ const_cast<char*>(PACKAGE) }};
std::vector<char*> c_args { const_cast<char*>(PACKAGE) };
std::string command_line { PACKAGE };
for (const auto &arg : args) {
@ -105,7 +105,7 @@ int AvrDude::priv::run_one(const std::vector<std::string> &args) {
HandlerGuard guard(*this);
message_fn(command_line.c_str(), command_line.size());
message_fn(command_line.c_str(), (unsigned)command_line.size());
const auto res = ::avrdude_main(static_cast<int>(c_args.size()), c_args.data());
@ -200,7 +200,7 @@ AvrDude::Ptr AvrDude::run()
auto &message_fn = self->p->message_fn;
if (message_fn) {
message_fn(msg, sizeof(msg));
message_fn(what, std::strlen(what));
message_fn(what, (unsigned)std::strlen(what));
message_fn("\n", 1);
}

View File

@ -64,6 +64,8 @@ int avrdude_main(int argc, char * argv []);
#include <windows.h>
#include <unistd.h>
#define strdup _strdup
#ifdef UNICODE
#error "UNICODE should not be defined for avrdude bits on Windows"
#endif

View File

@ -358,7 +358,7 @@ AVRMEM * avr_locate_mem(AVRPART * p, char * desc)
int matches;
int l;
l = strlen(desc);
l = (int)strlen(desc);
matches = 0;
match = NULL;
for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
@ -662,7 +662,7 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
prefix);
px = prefix;
i = strlen(prefix) + 5;
i = (int)strlen(prefix) + 5;
buf = (char *)malloc(i);
if (buf == NULL) {
/* ugh, this is not important enough to bail, just ignore it */

View File

@ -128,7 +128,7 @@ static int buspirate_recv_bin(struct programmer_t *pgm, unsigned char *buf, size
avrdude_message(MSG_DEBUG, "%s: buspirate_recv_bin():\n", progname);
dump_mem(MSG_DEBUG, buf, len);
return len;
return (int)len;
}
static int buspirate_expect_bin(struct programmer_t *pgm,
@ -249,7 +249,7 @@ static int buspirate_send(struct programmer_t *pgm, const char *str)
static int buspirate_is_prompt(const char *str)
{
int strlen_str = strlen(str);
int strlen_str = (int)strlen(str);
/* Prompt ends with '>' or '> '
* all other input probably ends with '\n' */
return (str[strlen_str - 1] == '>' || str[strlen_str - 2] == '>');

View File

@ -675,7 +675,7 @@ static int butterfly_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
butterfly_set_addr(pgm, addr / rd_size);
}
while (addr < max_addr) {
if ((max_addr - addr) < blocksize) {
if ((max_addr - addr) < (unsigned)blocksize) {
blocksize = max_addr - addr;
};
cmd[1] = (blocksize >> 8) & 0xff;

View File

@ -21,7 +21,7 @@ int main(int argc, char const *argv[])
}
std::cout << "/* WARN: This file is auto-generated from `" << filename << "` */" << std::endl;
std::cout << "unsigned char " << symbol << "[] = {";
std::cout << "const unsigned char " << symbol << "[] = {";
char c;
std::cout << std::hex;
@ -34,8 +34,8 @@ int main(int argc, char const *argv[])
std::cout << "\n 0, 0\n};\n";
std::cout << std::dec;
std::cout << "size_t " << symbol << "_size = " << size << ";" << std::endl;
std::cout << "size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
std::cout << "const size_t " << symbol << "_size = " << size << ";" << std::endl;
std::cout << "const size_t " << symbol << "_size_yy = " << size + 2 << ";" << std::endl;
return 0;
}

View File

@ -240,7 +240,7 @@ TOKEN * string(char * text)
return NULL; /* yyerror already called */
}
len = strlen(text);
len = (int)strlen(text);
tkn->value.type = V_STR;
tkn->value.string = (char *) malloc(len+1);
@ -351,7 +351,7 @@ int read_config(const char * file)
}
typedef struct yy_buffer_state *YY_BUFFER_STATE;
extern YY_BUFFER_STATE yy_scan_bytes(char *base, size_t size);
extern YY_BUFFER_STATE yy_scan_bytes(const char *base, size_t size);
extern void yy_delete_buffer(YY_BUFFER_STATE b);
int read_config_builtin()
@ -363,7 +363,7 @@ int read_config_builtin()
// Note: Can't use yy_scan_buffer, it's buggy (?), leads to fread from a null FILE*
// and so unfortunatelly we have to use the copying variant here
YY_BUFFER_STATE buffer = yy_scan_bytes(avrdude_slic3r_conf, avrdude_slic3r_conf_size);
YY_BUFFER_STATE buffer = yy_scan_bytes((const char *)avrdude_slic3r_conf, avrdude_slic3r_conf_size);
if (buffer == NULL) {
avrdude_message(MSG_INFO, "%s: read_config_builtin: Failed to initialize parsing buffer\n", progname);
return -1;

View File

@ -3640,7 +3640,7 @@ static int parse_cmdbits(OPCODE * op)
break;
}
len = strlen(s);
len = (int)strlen(s);
if (len == 0) {
yyerror("invalid bit specifier \"\"");

View File

@ -1493,7 +1493,7 @@ static int parse_cmdbits(OPCODE * op)
break;
}
len = strlen(s);
len = (int)strlen(s);
if (len == 0) {
yyerror("invalid bit specifier \"\"");

View File

@ -264,7 +264,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
unsigned char cksum;
int rc;
len = strlen(rec);
len = (int)strlen(rec);
offset = 1;
cksum = 0;
@ -274,7 +274,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
ihex->reclen = strtoul(buf, &e, 16);
ihex->reclen = (unsigned char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@ -294,7 +294,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
ihex->rectyp = strtoul(buf, &e, 16);
ihex->rectyp = (unsigned char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@ -308,7 +308,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
ihex->data[j] = strtoul(buf, &e, 16);
ihex->data[j] = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
cksum += ihex->data[j];
@ -320,7 +320,7 @@ static int ihex_readrec(struct ihexrec * ihex, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
ihex->cksum = strtoul(buf, &e, 16);
ihex->cksum = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@ -361,7 +361,7 @@ static int ihex2b(char * infile, FILE * inf,
while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) {
lineno++;
len = strlen(buffer);
len = (int)strlen(buffer);
if (buffer[len-1] == '\n')
buffer[--len] = 0;
if (buffer[0] != ':')
@ -388,7 +388,7 @@ static int ihex2b(char * infile, FILE * inf,
return -1;
}
nextaddr = ihex.loadofs + baseaddr - fileoffset;
if (nextaddr + ihex.reclen > bufsize) {
if (nextaddr + ihex.reclen > (unsigned)bufsize) {
avrdude_message(MSG_INFO, "%s: ERROR: address 0x%04x out of range at line %d of %s\n",
progname, nextaddr+ihex.reclen, lineno, infile);
return -1;
@ -502,10 +502,11 @@ static int b2srec(unsigned char * inbuf, int bufsize,
cksum += n + addr_width + 1;
for (i=addr_width; i>0; i--)
for (i = addr_width; i>0; i--) {
cksum += (nextaddr >> (i-1) * 8) & 0xff;
}
for (i=nextaddr; i<nextaddr + n; i++) {
for (unsigned i = nextaddr; i < nextaddr + n; i++) {
fprintf(outf, "%02X", buf[i]);
cksum += buf[i];
}
@ -562,7 +563,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
unsigned char cksum;
int rc;
len = strlen(rec);
len = (int)strlen(rec);
offset = 1;
cksum = 0;
addr_width = 2;
@ -582,7 +583,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
srec->reclen = strtoul(buf, &e, 16);
srec->reclen = (char)strtoul(buf, &e, 16);
cksum += srec->reclen;
srec->reclen -= (addr_width+1);
if (e == buf || *e != 0)
@ -594,7 +595,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<addr_width*2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
srec->loadofs = strtoull(buf, &e, 16);
srec->loadofs = strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@ -608,7 +609,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
srec->data[j] = strtoul(buf, &e, 16);
srec->data[j] = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
cksum += srec->data[j];
@ -620,7 +621,7 @@ static int srec_readrec(struct ihexrec * srec, char * rec)
for (i=0; i<2; i++)
buf[i] = rec[offset++];
buf[i] = 0;
srec->cksum = strtoul(buf, &e, 16);
srec->cksum = (char)strtoul(buf, &e, 16);
if (e == buf || *e != 0)
return -1;
@ -650,7 +651,7 @@ static int srec2b(char * infile, FILE * inf,
while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) {
lineno++;
len = strlen(buffer);
len = (int)strlen(buffer);
if (buffer[len-1] == '\n')
buffer[--len] = 0;
if (buffer[0] != 0x53)
@ -729,7 +730,7 @@ static int srec2b(char * infile, FILE * inf,
return -1;
}
nextaddr -= fileoffset;
if (nextaddr + srec.reclen > bufsize) {
if (nextaddr + srec.reclen > (unsigned)bufsize) {
avrdude_message(MSG_INFO, msg, progname, nextaddr+srec.reclen, "",
lineno, infile);
return -1;
@ -1143,12 +1144,12 @@ static int fileio_rbin(struct fioparms * fio,
switch (fio->op) {
case FIO_READ:
rc = fread(buf, 1, size, f);
rc = (int)fread(buf, 1, size, f);
if (rc > 0)
memset(mem->tags, TAG_ALLOCATED, rc);
break;
case FIO_WRITE:
rc = fwrite(buf, 1, size, f);
rc = (int)fwrite(buf, 1, size, f);
break;
default:
avrdude_message(MSG_INFO, "%s: fileio: invalid operation=%d\n",
@ -1190,7 +1191,7 @@ static int fileio_imm(struct fioparms * fio,
progname, p);
return -1;
}
mem->buf[loc] = b;
mem->buf[loc] = (char)b;
mem->tags[loc++] = TAG_ALLOCATED;
p = strtok(NULL, " ,");
rc = loc;
@ -1452,7 +1453,7 @@ static int fmt_autodetect(char * fname, unsigned section)
}
buf[MAX_LINE_LEN-1] = 0;
len = strlen((char *)buf);
len = (int)strlen((char *)buf);
if (buf[len-1] == '\n')
buf[--len] = 0;

View File

@ -444,7 +444,7 @@ lcreat ( void * liststruct, int elements )
l->poolsize = DEFAULT_POOLSIZE;
}
else {
l->poolsize = elements*sizeof(LISTNODE)+sizeof(NODEPOOL);
l->poolsize = (short)(elements*sizeof(LISTNODE)+sizeof(NODEPOOL));
}
l->n_ln_pool = (l->poolsize-sizeof(NODEPOOL))/sizeof(LISTNODE);
@ -803,7 +803,7 @@ lget_n ( LISTID lid, unsigned int n )
CKLMAGIC(l);
if ((n<1)||(n>lsize(l))) {
if ((n < 1) || (n > (unsigned)lsize(l))) {
return NULL;
}
@ -844,7 +844,7 @@ lget_ln ( LISTID lid, unsigned int n )
CKLMAGIC(l);
if ((n<1)||(n>lsize(l))) {
if ((n < 1) || (n > (unsigned)lsize(l))) {
return NULL;
}
@ -952,7 +952,7 @@ lins_n ( LISTID lid, void * data_ptr, unsigned int n )
CKLMAGIC(l);
if ((n<1)||(n>(l->num+1))) {
if ((n < 1) || (n > (unsigned)(l->num+1))) {
return -1;
}
@ -1193,7 +1193,7 @@ lrmv_n ( LISTID lid, unsigned int n )
CKLMAGIC(l);
if ((n<1)||(n>l->num)) {
if ((n < 1) || (n > (unsigned)l->num)) {
return NULL;
}

View File

@ -107,7 +107,7 @@ int avrdude_message(const int msglvl, const char *format, ...)
if (rc > 0 && rc < MSGBUFFER_SIZE) {
avrdude_message_handler(msgbuffer, rc, avrdude_message_handler_user_p);
} else {
avrdude_message_handler(format_error, strlen(format_error), avrdude_message_handler_user_p);
avrdude_message_handler(format_error, (unsigned)strlen(format_error), avrdude_message_handler_user_p);
}
}
@ -567,7 +567,7 @@ int avrdude_main(int argc, char * argv [])
// #endif
len = strlen(progname) + 2;
len = (int)strlen(progname) + 2;
for (i=0; i<len; i++)
progbuf[i] = ' ';
progbuf[i] = 0;
@ -601,7 +601,7 @@ int avrdude_main(int argc, char * argv [])
bitclock = strtod(optarg, &e);
if (*e != 0) {
/* trailing unit of measure present */
int suffixlen = strlen(e);
size_t suffixlen = strlen(e);
switch (suffixlen) {
case 2:
if ((e[0] != 'h' && e[0] != 'H') || e[1] != 'z')

View File

@ -217,7 +217,7 @@ const char * pinmask_to_str(const pinmask_t * const pinmask) {
* @param[in] size the number of entries in checklist
* @returns 0 if all pin definitions are valid, -1 otherwise
*/
int pins_check(const struct programmer_t * const pgm, const struct pin_checklist_t * const checklist, const int size, bool output) {
int pins_check(const struct programmer_t *const pgm, const struct pin_checklist_t *const checklist, const int size, const bool output) {
static const struct pindef_t no_valid_pins = {{0}, {0}}; // default value if check list does not contain anything else
int rv = 0; // return value
int pinname; // loop counter through pinnames

View File

@ -292,7 +292,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp)
if (hComPort == INVALID_HANDLE_VALUE) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_open(): can't open device \"%s\": %s\n", progname, port, error);
free(error);
free((char *)error);
return -1;
}
@ -460,10 +460,10 @@ static int ser_send(union filedescriptor *fd, const unsigned char * buf, size_t
serial_w32SetTimeOut(hComPort,500);
if (!WriteFile(hComPort, buf, buflen, &written, NULL)) {
if (!WriteFile(hComPort, buf, (DWORD)buflen, &written, NULL)) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_send(): write error: %s\n", progname, error);
free(error);
free((char *)error);
return -1;
}
@ -576,10 +576,10 @@ static int ser_recv(union filedescriptor *fd, unsigned char * buf, size_t buflen
serial_w32SetTimeOut(hComPort, serial_recv_timeout);
if (!ReadFile(hComPort, buf, buflen, &read, NULL)) {
if (!ReadFile(hComPort, buf, (DWORD)buflen, &read, NULL)) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_recv(): read error: %s\n", progname, error);
free(error);
free((char *)error);
return -1;
}
@ -642,7 +642,7 @@ static int ser_drain(union filedescriptor *fd, int display)
if (!readres) {
const char *error = last_error_string(0);
avrdude_message(MSG_INFO, "%s: ser_drain(): read error: %s\n", progname, error);
free(error);
free((char *)error);
return -1;
}

View File

@ -308,8 +308,8 @@ static int serbb_open(PROGRAMMER *pgm, char *port)
progname, port);
return -1;
}
avrdude_message(MSG_DEBUG, "%s: ser_open(): opened comm port \"%s\", handle 0x%x\n",
progname, port, (int)hComPort);
avrdude_message(MSG_DEBUG, "%s: ser_open(): opened comm port \"%s\", handle %p\n",
progname, port, (void *)hComPort);
pgm->fd.pfd = (void *)hComPort;
@ -326,8 +326,8 @@ static void serbb_close(PROGRAMMER *pgm)
pgm->setpin(pgm, PIN_AVR_RESET, 1);
CloseHandle (hComPort);
}
avrdude_message(MSG_DEBUG, "%s: ser_close(): closed comm port handle 0x%x\n",
progname, (int)hComPort);
avrdude_message(MSG_DEBUG, "%s: ser_close(): closed comm port handle %p\n",
progname, (void *)hComPort);
hComPort = INVALID_HANDLE_VALUE;
}

View File

@ -504,7 +504,7 @@ static int stk500_initialize(PROGRAMMER * pgm, AVRPART * p)
}
else {
buf[9] = 0xff;
buf[10] = 0xff;
buf[10] = 0xff;
buf[13] = 0;
buf[14] = 0;
buf[17] = 0;
@ -821,7 +821,7 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
break;
}
for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2 : 1); ++ prusa3d_semicolon_workaround_round) {
for (prusa3d_semicolon_workaround_round = 0; prusa3d_semicolon_workaround_round < (has_semicolon ? 2u : 1u); prusa3d_semicolon_workaround_round++) {
/* build command block and avoid multiple send commands as it leads to a crash
of the silabs usb serial driver on mac os x */
i = 0;
@ -834,7 +834,7 @@ static int stk500_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
buf[i++] = block_size & 0x0f;
buf[i++] = memtype;
if (has_semicolon) {
for (j = 0; j < block_size; ++i, ++ j) {
for (j = 0; j < (unsigned)block_size; ++i, ++ j) {
buf[i] = m->buf[addr + j];
if (buf[i] == ';')
buf[i] |= (prusa3d_semicolon_workaround_round ? 0xf0 : 0x0f);
@ -1088,7 +1088,7 @@ static int stk500_set_sck_period(PROGRAMMER * pgm, double v)
min = 8.0 / STK500_XTAL;
max = 255 * min;
dur = v / min + 0.5;
dur = (int)(v / min + 0.5);
if (v < min) {
dur = 1;

View File

@ -130,58 +130,58 @@ struct jtagispentry
#define SZ_SPI_MULTI (USHRT_MAX - 1)
};
static const struct jtagispentry jtagispcmds[] = {
/* generic */
{ CMD_SET_PARAMETER, 2 },
{ CMD_GET_PARAMETER, 3 },
{ CMD_OSCCAL, 2 },
{ CMD_LOAD_ADDRESS, 2 },
/* ISP mode */
{ CMD_ENTER_PROGMODE_ISP, 2 },
{ CMD_LEAVE_PROGMODE_ISP, 2 },
{ CMD_CHIP_ERASE_ISP, 2 },
{ CMD_PROGRAM_FLASH_ISP, 2 },
{ CMD_READ_FLASH_ISP, SZ_READ_FLASH_EE },
{ CMD_PROGRAM_EEPROM_ISP, 2 },
{ CMD_READ_EEPROM_ISP, SZ_READ_FLASH_EE },
{ CMD_PROGRAM_FUSE_ISP, 3 },
{ CMD_READ_FUSE_ISP, 4 },
{ CMD_PROGRAM_LOCK_ISP, 3 },
{ CMD_READ_LOCK_ISP, 4 },
{ CMD_READ_SIGNATURE_ISP, 4 },
{ CMD_READ_OSCCAL_ISP, 4 },
{ CMD_SPI_MULTI, SZ_SPI_MULTI },
/* all HV modes */
{ CMD_SET_CONTROL_STACK, 2 },
/* HVSP mode */
{ CMD_ENTER_PROGMODE_HVSP, 2 },
{ CMD_LEAVE_PROGMODE_HVSP, 2 },
{ CMD_CHIP_ERASE_HVSP, 2 },
{ CMD_PROGRAM_FLASH_HVSP, 2 },
{ CMD_READ_FLASH_HVSP, SZ_READ_FLASH_EE },
{ CMD_PROGRAM_EEPROM_HVSP, 2 },
{ CMD_READ_EEPROM_HVSP, SZ_READ_FLASH_EE },
{ CMD_PROGRAM_FUSE_HVSP, 2 },
{ CMD_READ_FUSE_HVSP, 3 },
{ CMD_PROGRAM_LOCK_HVSP, 2 },
{ CMD_READ_LOCK_HVSP, 3 },
{ CMD_READ_SIGNATURE_HVSP, 3 },
{ CMD_READ_OSCCAL_HVSP, 3 },
/* PP mode */
{ CMD_ENTER_PROGMODE_PP, 2 },
{ CMD_LEAVE_PROGMODE_PP, 2 },
{ CMD_CHIP_ERASE_PP, 2 },
{ CMD_PROGRAM_FLASH_PP, 2 },
{ CMD_READ_FLASH_PP, SZ_READ_FLASH_EE },
{ CMD_PROGRAM_EEPROM_PP, 2 },
{ CMD_READ_EEPROM_PP, SZ_READ_FLASH_EE },
{ CMD_PROGRAM_FUSE_PP, 2 },
{ CMD_READ_FUSE_PP, 3 },
{ CMD_PROGRAM_LOCK_PP, 2 },
{ CMD_READ_LOCK_PP, 3 },
{ CMD_READ_SIGNATURE_PP, 3 },
{ CMD_READ_OSCCAL_PP, 3 },
};
// static const struct jtagispentry jtagispcmds[] = {
// /* generic */
// { CMD_SET_PARAMETER, 2 },
// { CMD_GET_PARAMETER, 3 },
// { CMD_OSCCAL, 2 },
// { CMD_LOAD_ADDRESS, 2 },
// /* ISP mode */
// { CMD_ENTER_PROGMODE_ISP, 2 },
// { CMD_LEAVE_PROGMODE_ISP, 2 },
// { CMD_CHIP_ERASE_ISP, 2 },
// { CMD_PROGRAM_FLASH_ISP, 2 },
// { CMD_READ_FLASH_ISP, SZ_READ_FLASH_EE },
// { CMD_PROGRAM_EEPROM_ISP, 2 },
// { CMD_READ_EEPROM_ISP, SZ_READ_FLASH_EE },
// { CMD_PROGRAM_FUSE_ISP, 3 },
// { CMD_READ_FUSE_ISP, 4 },
// { CMD_PROGRAM_LOCK_ISP, 3 },
// { CMD_READ_LOCK_ISP, 4 },
// { CMD_READ_SIGNATURE_ISP, 4 },
// { CMD_READ_OSCCAL_ISP, 4 },
// { CMD_SPI_MULTI, SZ_SPI_MULTI },
// /* all HV modes */
// { CMD_SET_CONTROL_STACK, 2 },
// /* HVSP mode */
// { CMD_ENTER_PROGMODE_HVSP, 2 },
// { CMD_LEAVE_PROGMODE_HVSP, 2 },
// { CMD_CHIP_ERASE_HVSP, 2 },
// { CMD_PROGRAM_FLASH_HVSP, 2 },
// { CMD_READ_FLASH_HVSP, SZ_READ_FLASH_EE },
// { CMD_PROGRAM_EEPROM_HVSP, 2 },
// { CMD_READ_EEPROM_HVSP, SZ_READ_FLASH_EE },
// { CMD_PROGRAM_FUSE_HVSP, 2 },
// { CMD_READ_FUSE_HVSP, 3 },
// { CMD_PROGRAM_LOCK_HVSP, 2 },
// { CMD_READ_LOCK_HVSP, 3 },
// { CMD_READ_SIGNATURE_HVSP, 3 },
// { CMD_READ_OSCCAL_HVSP, 3 },
// /* PP mode */
// { CMD_ENTER_PROGMODE_PP, 2 },
// { CMD_LEAVE_PROGMODE_PP, 2 },
// { CMD_CHIP_ERASE_PP, 2 },
// { CMD_PROGRAM_FLASH_PP, 2 },
// { CMD_READ_FLASH_PP, SZ_READ_FLASH_EE },
// { CMD_PROGRAM_EEPROM_PP, 2 },
// { CMD_READ_EEPROM_PP, SZ_READ_FLASH_EE },
// { CMD_PROGRAM_FUSE_PP, 2 },
// { CMD_READ_FUSE_PP, 3 },
// { CMD_PROGRAM_LOCK_PP, 2 },
// { CMD_READ_LOCK_PP, 3 },
// { CMD_READ_SIGNATURE_PP, 3 },
// { CMD_READ_OSCCAL_PP, 3 },
// };
/*
* From XML file:
@ -379,15 +379,15 @@ static void stk500v2_jtag3_teardown(PROGRAMMER * pgm)
}
static unsigned short
b2_to_u16(unsigned char *b)
{
unsigned short l;
l = b[0];
l += (unsigned)b[1] << 8;
// static unsigned short
// b2_to_u16(unsigned char *b)
// {
// unsigned short l;
// l = b[0];
// l += (unsigned)b[1] << 8;
return l;
}
// return l;
// }
static int stk500v2_send_mk2(PROGRAMMER * pgm, unsigned char * data, size_t len)
{
@ -399,16 +399,16 @@ static int stk500v2_send_mk2(PROGRAMMER * pgm, unsigned char * data, size_t len)
return 0;
}
static unsigned short get_jtagisp_return_size(unsigned char cmd)
{
int i;
// static unsigned short get_jtagisp_return_size(unsigned char cmd)
// {
// int i;
for (i = 0; i < sizeof jtagispcmds / sizeof jtagispcmds[0]; i++)
if (jtagispcmds[i].cmd == cmd)
return jtagispcmds[i].size;
// for (i = 0; i < sizeof jtagispcmds / sizeof jtagispcmds[0]; i++)
// if (jtagispcmds[i].cmd == cmd)
// return jtagispcmds[i].size;
return 0;
}
// return 0;
// }
/*
* Send the data as a JTAG ICE mkII encapsulated ISP packet.
@ -504,7 +504,7 @@ static int stk500v2_send(PROGRAMMER * pgm, unsigned char * data, size_t len)
buf[0] = MESSAGE_START;
buf[1] = PDATA(pgm)->command_sequence;
buf[2] = len / 256;
buf[2] = (char)(len / 256);
buf[3] = len % 256;
buf[4] = TOKEN;
memcpy(buf+5, data, len);
@ -1128,7 +1128,8 @@ static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p)
{
unsigned char buf[16];
char msg[100]; /* see remarks above about size needed */
int rv, tries;
int rv;
// int tries;
PDATA(pgm)->lastpart = p;
@ -1143,7 +1144,7 @@ static int stk500v2_program_enable(PROGRAMMER * pgm, AVRPART * p)
/* Activate AVR-style (low active) RESET */
stk500v2_setparm_real(pgm, PARAM_RESET_POLARITY, 0x01);
tries = 0;
// tries = 0;
// retry:
buf[0] = CMD_ENTER_PROGMODE_ISP;
buf[1] = p->timeout;
@ -1882,7 +1883,7 @@ static int stk500hv_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
if (stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)) < 0)
return -1;
} else {
buf[1] = addr;
buf[1] = (char)addr;
}
avrdude_message(MSG_NOTICE2, "%s: stk500hv_read_byte(): Sending read memory command: ",
@ -2137,7 +2138,7 @@ static int stk500hv_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
if (stk500v2_loadaddr(pgm, use_ext_addr | (paddr >> addrshift)) < 0)
return -1;
} else {
buf[1] = addr;
buf[1] = (char)addr;
buf[2] = data;
if (mode == PPMODE) {
buf[3] = pulsewidth;
@ -2298,7 +2299,7 @@ static int stk500v2_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
unsigned int page_size,
unsigned int addr, unsigned int n_bytes)
{
static int page = 0;
// static int page = 0;
unsigned int block_size, last_addr, addrshift, use_ext_addr;
unsigned int maxaddr = addr + n_bytes;
unsigned char commandbuf[10];
@ -2833,10 +2834,10 @@ static int stk500v2_set_fosc(PROGRAMMER * pgm, double v)
progname, v, unit, STK500V2_XTAL / 2e6);
fosc = STK500V2_XTAL / 2;
} else
fosc = (unsigned)v;
fosc = (int)v;
for (idx = 0; idx < sizeof(ps) / sizeof(ps[0]); idx++) {
if (fosc >= STK500V2_XTAL / (256 * ps[idx] * 2)) {
if (fosc >= (int)(STK500V2_XTAL / (256 * ps[idx] * 2))) {
/* this prescaler value can handle our frequency */
prescale = idx + 1;
cmatch = (unsigned)(STK500V2_XTAL / (2 * fosc * ps[idx])) - 1;
@ -3065,8 +3066,8 @@ static int stk600_set_fosc(PROGRAMMER * pgm, double v)
{
unsigned int oct, dac;
oct = 1.443 * log(v / 1039.0);
dac = 2048 - (2078.0 * pow(2, (double)(10 + oct))) / v;
oct = (unsigned)(1.443 * log(v / 1039.0));
dac = (unsigned)(2048.0 - (2078.0 * pow(2, (double)(10 + oct))) / v);
return stk500v2_setparm2(pgm, PARAM2_CLOCK_CONF, (oct << 12) | (dac << 2));
}
@ -3075,7 +3076,7 @@ static int stk600_set_sck_period(PROGRAMMER * pgm, double v)
{
unsigned int sck;
sck = ceil((16e6 / (2 * 1.0 / v)) - 1);
sck = (unsigned)ceil((16e6 / (2 * 1.0 / v)) - 1);
if (sck >= 4096)
sck = 4095;
@ -3093,7 +3094,7 @@ static int stk500v2_jtag3_set_sck_period(PROGRAMMER * pgm, double v)
else if (v > 1E-3)
sck = 1;
else
sck = 1.0 / (1000.0 * v);
sck = (unsigned)(1.0 / (1000.0 * v));
value[0] = CMD_SET_SCK;
value[1] = sck & 0xff;
@ -3143,7 +3144,7 @@ static int stk500v2_setparm_real(PROGRAMMER * pgm, unsigned char parm, unsigned
static int stk500v2_setparm(PROGRAMMER * pgm, unsigned char parm, unsigned char value)
{
unsigned char current_value;
unsigned char current_value = 0;
int res;
res = stk500v2_getparm(pgm, parm, &current_value);
@ -3214,8 +3215,15 @@ static const char *stk600_get_cardname(const struct carddata *table,
static void stk500v2_display(PROGRAMMER * pgm, const char * p)
{
unsigned char maj, min, hdw, topcard, maj_s1, min_s1, maj_s2, min_s2;
unsigned int rev;
unsigned char maj = 0;
unsigned char min = 0;
unsigned char hdw = 0;
unsigned char topcard = 0;
unsigned char maj_s1 = 0;
unsigned char min_s1 = 0;
unsigned char maj_s2 = 0;
unsigned char min_s2 = 0;
unsigned int rev = 0;
const char *topcard_name, *pgmname;
switch (PDATA(pgm)->pgmtype) {
@ -3294,13 +3302,20 @@ f_to_kHz_MHz(double f, const char **unit)
static void stk500v2_print_parms1(PROGRAMMER * pgm, const char * p)
{
unsigned char vtarget, vadjust, osc_pscale, osc_cmatch, sck_duration =0; //XXX 0 is not correct, check caller
unsigned int sck_stk600, clock_conf, dac, oct, varef;
unsigned char vtarget_jtag[4];
unsigned char vtarget = 0;
unsigned char vadjust = 0;
unsigned char sck_duration = 0;
unsigned char osc_pscale = 0;
unsigned char osc_cmatch = 0;
unsigned varef = 0;
unsigned sck_stk600 = 0;
unsigned clock_conf = 0;
unsigned dac, oct;
// unsigned char vtarget_jtag[4];
int prescale;
double f;
const char *unit;
void *mycookie;
// void *mycookie;
if (PDATA(pgm)->pgmtype == PGMTYPE_JTAGICE_MKII) {
return;
@ -3963,10 +3978,10 @@ static int stk600_xprog_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
b[0] = XPRG_CMD_WRITE_MEM;
b[1] = memcode;
b[2] = 0; /* pagemode: non-paged write */
b[3] = addr >> 24;
b[4] = addr >> 16;
b[5] = addr >> 8;
b[6] = addr;
b[3] = (char)(addr >> 24);
b[4] = (char)(addr >> 16);
b[5] = (char)(addr >> 8);
b[6] = (char)addr;
b[7] = 0;
b[8] = write_size;
b[9] = data;
@ -4011,10 +4026,10 @@ static int stk600_xprog_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
addr += mem->offset;
b[0] = XPRG_CMD_READ_MEM;
b[2] = addr >> 24;
b[3] = addr >> 16;
b[4] = addr >> 8;
b[5] = addr;
b[2] = (char)(addr >> 24);
b[3] = (char)(addr >> 16);
b[4] = (char)(addr >> 8);
b[5] = (char)addr;
b[6] = 0;
b[7] = 1;
if (stk600_xprog_command(pgm, b, 8, 3) < 0) {

View File

@ -281,7 +281,7 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
maxsize = mem->size;
if (addr >= maxsize) {
if (addr >= (unsigned long)maxsize) {
if (argc == 2) {
/* wrap around */
addr = 0;
@ -294,7 +294,7 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
}
/* trim len if nessary to not read past the end of memory */
if ((addr + len) > maxsize)
if ((addr + len) > (unsigned long)maxsize)
len = maxsize - addr;
buf = malloc(len);
@ -303,7 +303,7 @@ static int cmd_dump(PROGRAMMER * pgm, struct avrpart * p,
return -1;
}
for (i=0; i<len; i++) {
for (i = 0; i < (unsigned long)len; i++) {
rc = pgm->read_byte(pgm, p, mem, addr+i, &buf[i]);
if (rc != 0) {
avrdude_message(MSG_INFO, "error reading %s address 0x%05lx of part %s\n",
@ -364,7 +364,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
return -1;
}
if (addr > maxsize) {
if (addr > (unsigned long)maxsize) {
avrdude_message(MSG_INFO, "%s (write): address 0x%05lx is out of range for %s memory\n",
progname, addr, memtype);
return -1;
@ -373,7 +373,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
/* number of bytes to write at the specified address */
len = argc - 3;
if ((addr + len) > maxsize) {
if ((addr + len) > (unsigned long)maxsize) {
avrdude_message(MSG_INFO, "%s (write): selected address and # bytes exceed "
"range for %s memory\n",
progname, memtype);
@ -386,8 +386,8 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
return -1;
}
for (i=3; i<argc; i++) {
buf[i-3] = strtoul(argv[i], &e, 0);
for (i = 3; i < (unsigned long)argc; i++) {
buf[i-3] = (char)strtoul(argv[i], &e, 0);
if (*e || (e == argv[i])) {
avrdude_message(MSG_INFO, "%s (write): can't parse byte \"%s\"\n",
progname, argv[i]);
@ -397,7 +397,7 @@ static int cmd_write(PROGRAMMER * pgm, struct avrpart * p,
}
pgm->err_led(pgm, OFF);
for (werror=0, i=0; i<len; i++) {
for (werror = 0, i = 0; i < (unsigned long)len; i++) {
rc = avr_write_byte(pgm, p, mem, addr+i, buf[i]);
if (rc) {
@ -462,7 +462,7 @@ static int cmd_send(PROGRAMMER * pgm, struct avrpart * p,
/* load command bytes */
for (i=1; i<argc; i++) {
cmd[i-1] = strtoul(argv[i], &e, 0);
cmd[i-1] = (char)strtoul(argv[i], &e, 0);
if (*e || (e == argv[i])) {
avrdude_message(MSG_INFO, "%s (send): can't parse byte \"%s\"\n",
progname, argv[i]);
@ -789,7 +789,7 @@ static int tokenize(char * s, char *** argv)
char * nbuf;
char ** av;
slen = strlen(s);
slen = (int)strlen(s);
/*
* initialize allow for 20 arguments, use realloc to grow this if
@ -812,7 +812,7 @@ static int tokenize(char * s, char *** argv)
nexttok(r, &q, &r);
strcpy(nbuf, q);
bufv[n] = nbuf;
len = strlen(q);
len = (int)strlen(q);
l += len + 1;
nbuf += len + 1;
nbuf[0] = 0;
@ -841,7 +841,7 @@ static int tokenize(char * s, char *** argv)
q = (char *)&av[n+1];
memcpy(q, buf, l);
for (i=0; i<n; i++) {
offset = bufv[i] - buf;
offset = (int)(bufv[i] - buf);
av[i] = q + offset;
}
av[i] = NULL;
@ -862,7 +862,7 @@ static int do_cmd(PROGRAMMER * pgm, struct avrpart * p,
int hold;
int len;
len = strlen(argv[0]);
len = (int)strlen(argv[0]);
hold = -1;
for (i=0; i<NCMDS; i++) {
if (strcasecmp(argv[0], cmd[i].name) == 0) {

View File

@ -161,4 +161,12 @@ inline bool empty(const BoundingBox3Base<VT> &bb)
} // namespace Slic3r
// Serialization through the Cereal library
namespace cereal {
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBox &bb) { archive(bb.min, bb.max, bb.defined); }
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBox3 &bb) { archive(bb.min, bb.max, bb.defined); }
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBoxf &bb) { archive(bb.min, bb.max, bb.defined); }
template<class Archive> void serialize(Archive& archive, Slic3r::BoundingBoxf3 &bb) { archive(bb.min, bb.max, bb.defined); }
}
#endif

View File

@ -81,9 +81,8 @@ add_library(libslic3r STATIC
GCode/SpiralVase.hpp
GCode/ToolOrdering.cpp
GCode/ToolOrdering.hpp
GCode/WipeTower.cpp
GCode/WipeTower.hpp
GCode/WipeTowerPrusaMM.cpp
GCode/WipeTowerPrusaMM.hpp
GCode.cpp
GCode.hpp
GCodeReader.cpp
@ -114,6 +113,8 @@ add_library(libslic3r STATIC
MultiPoint.cpp
MultiPoint.hpp
MutablePriorityQueue.hpp
ObjectID.cpp
ObjectID.hpp
PerimeterGenerator.cpp
PerimeterGenerator.hpp
PlaceholderParser.cpp
@ -189,6 +190,7 @@ target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNE
target_link_libraries(libslic3r
libnest2d
admesh
cereal
libigl
miniz
boost_libs

View File

@ -209,6 +209,51 @@ std::vector<std::string> ConfigOptionDef::cli_args(const std::string &key) const
return args;
}
ConfigOption* ConfigOptionDef::create_empty_option() const
{
switch (this->type) {
case coFloat: return new ConfigOptionFloat();
case coFloats: return new ConfigOptionFloats();
case coInt: return new ConfigOptionInt();
case coInts: return new ConfigOptionInts();
case coString: return new ConfigOptionString();
case coStrings: return new ConfigOptionStrings();
case coPercent: return new ConfigOptionPercent();
case coPercents: return new ConfigOptionPercents();
case coFloatOrPercent: return new ConfigOptionFloatOrPercent();
case coPoint: return new ConfigOptionPoint();
case coPoints: return new ConfigOptionPoints();
case coPoint3: return new ConfigOptionPoint3();
// case coPoint3s: return new ConfigOptionPoint3s();
case coBool: return new ConfigOptionBool();
case coBools: return new ConfigOptionBools();
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label);
}
}
ConfigOption* ConfigOptionDef::create_default_option() const
{
if (this->default_value)
return (this->default_value->type() == coEnum) ?
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) :
this->default_value->clone();
return this->create_empty_option();
}
// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread!
ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type)
{
static size_t serialization_key_ordinal_last = 0;
ConfigOptionDef *opt = &this->options[opt_key];
opt->opt_key = opt_key;
opt->type = type;
opt->serialization_key_ordinal = ++ serialization_key_ordinal_last;
this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt;
return opt;
}
std::string ConfigOptionDef::nocli = "~~~noCLI";
std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function<bool(const ConfigOptionDef &)> filter) const
@ -358,7 +403,7 @@ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
return equal;
}
std::string ConfigBase::serialize(const t_config_option_key &opt_key) const
std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const
{
const ConfigOption* opt = this->option(opt_key);
assert(opt != nullptr);
@ -469,7 +514,7 @@ void ConfigBase::setenv_() const
for (size_t i = 0; i < envname.size(); ++i)
envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i];
boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1);
boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1);
}
}
@ -593,16 +638,16 @@ void ConfigBase::save(const std::string &file) const
c.open(file, std::ios::out | std::ios::trunc);
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
for (const std::string &opt_key : this->keys())
c << opt_key << " = " << this->serialize(opt_key) << std::endl;
c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl;
c.close();
}
bool DynamicConfig::operator==(const DynamicConfig &rhs) const
{
t_options_map::const_iterator it1 = this->options.begin();
t_options_map::const_iterator it1_end = this->options.end();
t_options_map::const_iterator it2 = rhs.options.begin();
t_options_map::const_iterator it2_end = rhs.options.end();
auto it1 = this->options.begin();
auto it1_end = this->options.end();
auto it2 = rhs.options.begin();
auto it2_end = rhs.options.end();
for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2)
if (it1->first != it2->first || *it1->second != *it2->second)
// key or value differ
@ -612,10 +657,10 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const
ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create)
{
t_options_map::iterator it = options.find(opt_key);
auto it = options.find(opt_key);
if (it != options.end())
// Option was found.
return it->second;
return it->second.get();
if (! create)
// Option was not found and a new option shall not be created.
return nullptr;
@ -628,34 +673,8 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
// Let the parent decide what to do if the opt_key is not defined by this->def().
return nullptr;
ConfigOption *opt = nullptr;
if (optdef->default_value) {
opt = (optdef->default_value->type() == coEnum) ?
// Special case: For a DynamicConfig, convert a templated enum to a generic enum.
new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) :
optdef->default_value->clone();
} else {
switch (optdef->type) {
case coFloat: opt = new ConfigOptionFloat(); break;
case coFloats: opt = new ConfigOptionFloats(); break;
case coInt: opt = new ConfigOptionInt(); break;
case coInts: opt = new ConfigOptionInts(); break;
case coString: opt = new ConfigOptionString(); break;
case coStrings: opt = new ConfigOptionStrings(); break;
case coPercent: opt = new ConfigOptionPercent(); break;
case coPercents: opt = new ConfigOptionPercents(); break;
case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break;
case coPoint: opt = new ConfigOptionPoint(); break;
case coPoints: opt = new ConfigOptionPoints(); break;
case coPoint3: opt = new ConfigOptionPoint3(); break;
// case coPoint3s: opt = new ConfigOptionPoint3s(); break;
case coBool: opt = new ConfigOptionBool(); break;
case coBools: opt = new ConfigOptionBools(); break;
case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break;
default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key);
}
}
this->options[opt_key] = opt;
ConfigOption *opt = optdef->create_default_option();
this->options.emplace_hint(it, opt_key, std::unique_ptr<ConfigOption>(opt));
return opt;
}
@ -732,18 +751,18 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra,
}
// Store the option value.
const bool existing = this->has(opt_key);
if (keys != nullptr && !existing) {
if (keys != nullptr && ! existing) {
// Save the order of detected keys.
keys->push_back(opt_key);
}
ConfigOption *opt_base = this->option(opt_key, true);
ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
if (opt_vector) {
if (! existing)
// remove the default values
opt_vector->clear();
// Vector values will be chained. Repeated use of a parameter will append the parameter or parameters
// to the end of the value.
if (!existing)
// remove the default values
opt_vector->deserialize("", true);
if (opt_base->type() == coBools)
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else
@ -802,3 +821,64 @@ t_config_option_keys StaticConfig::keys() const
}
}
#include <cereal/types/polymorphic.hpp>
CEREAL_REGISTER_TYPE(Slic3r::ConfigOption)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<double>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<int>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<std::string>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle<bool>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVectorBase)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<double>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<int>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<std::string>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<Slic3r::Vec2d>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector<unsigned char>)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloat)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloats)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInt)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInts)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionString)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionStrings)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBool)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBools)
CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionEnumGeneric)
CEREAL_REGISTER_TYPE(Slic3r::ConfigBase)
CEREAL_REGISTER_TYPE(Slic3r::DynamicConfig)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<double>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<int>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<std::string>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<Slic3r::Vec2d>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<Slic3r::Vec3d>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle<bool>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionVectorBase)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<double>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<int>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<std::string>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<Slic3r::Vec2d>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector<unsigned char>)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<double>, Slic3r::ConfigOptionFloat)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<double>, Slic3r::ConfigOptionFloats)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<int>, Slic3r::ConfigOptionInt)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<int>, Slic3r::ConfigOptionInts)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<std::string>, Slic3r::ConfigOptionString)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<std::string>, Slic3r::ConfigOptionStrings)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionPercent)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec2d>, Slic3r::ConfigOptionPoint)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<Slic3r::Vec2d>, Slic3r::ConfigOptionPoints)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<Slic3r::Vec3d>, Slic3r::ConfigOptionPoint3)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle<bool>, Slic3r::ConfigOptionBool)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector<unsigned char>, Slic3r::ConfigOptionBools)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionInt, Slic3r::ConfigOptionEnumGeneric)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigBase, Slic3r::DynamicConfig)

View File

@ -18,6 +18,9 @@
#include <boost/format.hpp>
#include <boost/property_tree/ptree.hpp>
#include <cereal/access.hpp>
#include <cereal/types/base_class.hpp>
namespace Slic3r {
// Name of the configuration option.
@ -152,6 +155,10 @@ public:
bool operator==(const T &rhs) const { return this->value == rhs; }
bool operator!=(const T &rhs) const { return this->value != rhs; }
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->value); }
};
// Value of a vector valued option (bools, ints, floats, strings, points)
@ -167,8 +174,10 @@ public:
// Set a single vector item from either a scalar option or the first value of a vector option.vector of ConfigOptions.
// This function is useful to split values from multiple extrder / filament settings into separate configurations.
virtual void set_at(const ConfigOption *rhs, size_t i, size_t j) = 0;
// Resize the vector of values, copy the newly added values from opt_default if provided.
virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0;
// Clear the values vector.
virtual void clear() = 0;
// Get size of this vector.
virtual size_t size() const = 0;
@ -277,6 +286,8 @@ public:
}
}
// Clear the values vector.
void clear() override { this->values.clear(); }
size_t size() const override { return this->values.size(); }
bool empty() const override { return this->values.empty(); }
@ -290,6 +301,10 @@ public:
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; }
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; }
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->values); }
};
class ConfigOptionFloat : public ConfigOptionSingle<double>
@ -324,6 +339,10 @@ public:
this->set(opt);
return *this;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<double>>(this)); }
};
class ConfigOptionFloats : public ConfigOptionVector<double>
@ -382,6 +401,10 @@ public:
this->set(opt);
return *this;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<double>>(this)); }
};
class ConfigOptionInt : public ConfigOptionSingle<int>
@ -418,6 +441,10 @@ public:
this->set(opt);
return *this;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<int>>(this)); }
};
class ConfigOptionInts : public ConfigOptionVector<int>
@ -468,6 +495,10 @@ public:
}
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
};
class ConfigOptionString : public ConfigOptionSingle<std::string>
@ -492,6 +523,10 @@ public:
UNUSED(append);
return unescape_string_cstyle(str, this->value);
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<std::string>>(this)); }
};
// semicolon-separated strings
@ -526,6 +561,10 @@ public:
this->values.clear();
return unescape_strings_cstyle(str, this->values);
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<std::string>>(this)); }
};
class ConfigOptionPercent : public ConfigOptionFloat
@ -558,6 +597,10 @@ public:
iss >> this->value;
return !iss.fail();
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloat>(this)); }
};
class ConfigOptionPercents : public ConfigOptionFloats
@ -612,6 +655,10 @@ public:
}
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionFloats>(this)); }
};
class ConfigOptionFloatOrPercent : public ConfigOptionPercent
@ -661,6 +708,10 @@ public:
iss >> this->value;
return !iss.fail();
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
};
class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
@ -691,6 +742,10 @@ public:
return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec2d>>(this)); }
};
class ConfigOptionPoints : public ConfigOptionVector<Vec2d>
@ -750,8 +805,21 @@ public:
}
return true;
}
};
private:
friend class cereal::access;
template<class Archive> void save(Archive& archive) const {
size_t cnt = this->values.size();
archive(cnt);
archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt);
}
template<class Archive> void load(Archive& archive) {
size_t cnt;
archive(cnt);
this->values.assign(cnt, Vec2d());
archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt);
}
};
class ConfigOptionPoint3 : public ConfigOptionSingle<Vec3d>
{
@ -783,6 +851,10 @@ public:
return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 ||
sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<Vec3d>>(this)); }
};
class ConfigOptionBool : public ConfigOptionSingle<bool>
@ -809,6 +881,10 @@ public:
this->value = (str.compare("1") == 0);
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionSingle<bool>>(this)); }
};
class ConfigOptionBools : public ConfigOptionVector<unsigned char>
@ -864,6 +940,10 @@ public:
}
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionVector<unsigned char>>(this)); }
};
// Map from an enum integer value to an enum name.
@ -1002,19 +1082,73 @@ public:
this->value = it->second;
return true;
}
private:
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) { ar(cereal::base_class<ConfigOptionInt>(this)); }
};
// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
class ConfigOptionDef
{
public:
// Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
t_config_option_key opt_key;
// What type? bool, int, string etc.
ConfigOptionType type = coNone;
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
Slic3r::clonable_ptr<const ConfigOption> default_value;
void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
template<typename T>
const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr<const ConfigOption>(ptr); }
template<typename T> const T* get_default_value() const { return static_cast<const T*>(this->default_value.get()); }
// Create an empty option to be used as a base for deserialization of DynamicConfig.
ConfigOption* create_empty_option() const;
// Create a default option to be inserted into a DynamicConfig.
ConfigOption* create_default_option() const;
template<class Archive> ConfigOption* load_option_from_archive(Archive &archive) const {
switch (this->type) {
case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; }
case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; }
case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; }
case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; }
case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; }
case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; }
case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; }
case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; }
case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; }
case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; }
case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; }
case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; }
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
}
}
template<class Archive> ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const {
switch (this->type) {
case coFloat: archive(*static_cast<const ConfigOptionFloat*>(opt)); break;
case coFloats: archive(*static_cast<const ConfigOptionFloats*>(opt)); break;
case coInt: archive(*static_cast<const ConfigOptionInt*>(opt)); break;
case coInts: archive(*static_cast<const ConfigOptionInts*>(opt)); break;
case coString: archive(*static_cast<const ConfigOptionString*>(opt)); break;
case coStrings: archive(*static_cast<const ConfigOptionStrings*>(opt)); break;
case coPercent: archive(*static_cast<const ConfigOptionPercent*>(opt)); break;
case coPercents: archive(*static_cast<const ConfigOptionPercents*>(opt)); break;
case coFloatOrPercent: archive(*static_cast<const ConfigOptionFloatOrPercent*>(opt)); break;
case coPoint: archive(*static_cast<const ConfigOptionPoint*>(opt)); break;
case coPoints: archive(*static_cast<const ConfigOptionPoints*>(opt)); break;
case coPoint3: archive(*static_cast<const ConfigOptionPoint3*>(opt)); break;
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
}
// Make the compiler happy, shut up the warnings.
return nullptr;
}
// Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
@ -1084,6 +1218,9 @@ public:
return false;
}
// 0 is an invalid key.
size_t serialization_key_ordinal = 0;
// Returns the alternative CLI arguments for the given option.
// If there are no cli arguments defined, use the key and replace underscores with dashes.
std::vector<std::string> cli_args(const std::string &key) const;
@ -1103,7 +1240,8 @@ typedef std::map<t_config_option_key, ConfigOptionDef> t_optiondef_map;
class ConfigDef
{
public:
t_optiondef_map options;
t_optiondef_map options;
std::map<size_t, const ConfigOptionDef*> by_serialization_key_ordinal;
bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; }
const ConfigOptionDef* get(const t_config_option_key &opt_key) const {
@ -1124,11 +1262,7 @@ public:
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
protected:
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) {
ConfigOptionDef* opt = &this->options[opt_key];
opt->type = type;
return opt;
}
ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type);
};
// An abstract configuration store.
@ -1197,7 +1331,7 @@ public:
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
t_config_option_keys diff(const ConfigBase &other) const;
t_config_option_keys equal(const ConfigBase &other) const;
std::string serialize(const t_config_option_key &opt_key) const;
std::string opt_serialize(const t_config_option_key &opt_key) const;
// Set a configuration value from a string, it will call an overridable handle_legacy()
// to resolve renamed and removed configuration keys.
bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
@ -1235,7 +1369,7 @@ public:
assert(this->def() == nullptr || this->def() == rhs.def());
this->clear();
for (const auto &kvp : rhs.options)
this->options[kvp.first] = kvp.second->clone();
this->options[kvp.first].reset(kvp.second->clone());
return *this;
}
@ -1258,15 +1392,13 @@ public:
for (const auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end())
this->options[kvp.first] = kvp.second->clone();
this->options[kvp.first].reset(kvp.second->clone());
else {
assert(it->second->type() == kvp.second->type());
if (it->second->type() == kvp.second->type())
*it->second = *kvp.second;
else {
delete it->second;
it->second = kvp.second->clone();
}
else
it->second.reset(kvp.second->clone());
}
}
return *this;
@ -1277,14 +1409,13 @@ public:
DynamicConfig& operator+=(DynamicConfig &&rhs)
{
assert(this->def() == nullptr || this->def() == rhs.def());
for (const auto &kvp : rhs.options) {
for (auto &kvp : rhs.options) {
auto it = this->options.find(kvp.first);
if (it == this->options.end()) {
this->options[kvp.first] = kvp.second;
this->options.insert(std::make_pair(kvp.first, std::move(kvp.second)));
} else {
assert(it->second->type() == kvp.second->type());
delete it->second;
it->second = kvp.second;
it->second = std::move(kvp.second);
}
}
rhs.options.clear();
@ -1301,8 +1432,6 @@ public:
void clear()
{
for (auto &opt : this->options)
delete opt.second;
this->options.clear();
}
@ -1311,7 +1440,6 @@ public:
auto it = this->options.find(opt_key);
if (it == this->options.end())
return false;
delete it->second;
this->options.erase(it);
return true;
}
@ -1336,11 +1464,10 @@ public:
{
auto it = this->options.find(opt_key);
if (it == this->options.end()) {
this->options[opt_key] = opt;
this->options[opt_key].reset(opt);
return true;
} else {
delete it->second;
it->second = opt;
it->second.reset(opt);
return false;
}
}
@ -1370,12 +1497,15 @@ public:
void read_cli(const std::vector<std::string> &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr);
typedef std::map<t_config_option_key,ConfigOption*> t_options_map;
t_options_map::const_iterator cbegin() const { return options.cbegin(); }
t_options_map::const_iterator cend() const { return options.cend(); }
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cbegin() const { return options.cbegin(); }
std::map<t_config_option_key, std::unique_ptr<ConfigOption>>::const_iterator cend() const { return options.cend(); }
size_t size() const { return options.size(); }
private:
t_options_map options;
std::map<t_config_option_key, std::unique_ptr<ConfigOption>> options;
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(options); }
};
/// Configuration store with a static definition of configuration values.

View File

@ -146,10 +146,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
coord_t iy = p1(1) / m_resolution;
coord_t ixb = p2(0) / m_resolution;
coord_t iyb = p2(1) / m_resolution;
assert(ix >= 0 && ix < m_cols);
assert(iy >= 0 && iy < m_rows);
assert(ixb >= 0 && ixb < m_cols);
assert(iyb >= 0 && iyb < m_rows);
assert(ix >= 0 && size_t(ix) < m_cols);
assert(iy >= 0 && size_t(iy) < m_rows);
assert(ixb >= 0 && size_t(ixb) < m_cols);
assert(iyb >= 0 && size_t(iyb) < m_rows);
// Account for the end points.
++ m_cells[iy*m_cols+ix].end;
if (ix == ixb && iy == iyb)
@ -290,10 +290,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
coord_t iy = p1(1) / m_resolution;
coord_t ixb = p2(0) / m_resolution;
coord_t iyb = p2(1) / m_resolution;
assert(ix >= 0 && ix < m_cols);
assert(iy >= 0 && iy < m_rows);
assert(ixb >= 0 && ixb < m_cols);
assert(iyb >= 0 && iyb < m_rows);
assert(ix >= 0 && size_t(ix) < m_cols);
assert(iy >= 0 && size_t(iy) < m_rows);
assert(ixb >= 0 && size_t(ixb) < m_cols);
assert(iyb >= 0 && size_t(iyb) < m_rows);
// Account for the end points.
m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair<size_t, size_t>(i, j);
if (ix == ixb && iy == iyb)
@ -775,11 +775,11 @@ void EdgeGrid::Grid::calculate_sdf()
// For each corner of this cell and its 1 ring neighbours:
for (int corner_y = -1; corner_y < 3; ++ corner_y) {
coord_t corner_r = r + corner_y;
if (corner_r < 0 || corner_r >= nrows)
if (corner_r < 0 || (size_t)corner_r >= nrows)
continue;
for (int corner_x = -1; corner_x < 3; ++ corner_x) {
coord_t corner_c = c + corner_x;
if (corner_c < 0 || corner_c >= ncols)
if (corner_c < 0 || (size_t)corner_c >= ncols)
continue;
float &d_min = m_signed_distance_field[corner_r * ncols + corner_c];
Slic3r::Point pt(m_bbox.min(0) + corner_c * m_resolution, m_bbox.min(1) + corner_r * m_resolution);
@ -1137,9 +1137,9 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu
return false;
bbox.max(0) /= m_resolution;
bbox.max(1) /= m_resolution;
if (bbox.max(0) >= m_cols)
if ((size_t)bbox.max(0) >= m_cols)
bbox.max(0) = m_cols - 1;
if (bbox.max(1) >= m_rows)
if ((size_t)bbox.max(1) >= m_rows)
bbox.max(1) = m_rows - 1;
// Lower boundary, round to grid and test validity.
bbox.min(0) -= search_radius;

View File

@ -78,8 +78,8 @@ protected:
#endif
bool cell_inside_or_crossing(int r, int c) const
{
if (r < 0 || r >= m_rows ||
c < 0 || c >= m_cols)
if (r < 0 || (size_t)r >= m_rows ||
c < 0 || (size_t)c >= m_cols)
// The cell is outside the domain. Hoping that the contours were correctly oriented, so
// there is a CCW outmost contour so the out of domain cells are outside.
return false;

View File

@ -660,7 +660,7 @@ void gcode_spread_points(
for (ExtrusionPoints::const_iterator it = points.begin(); it != points.end(); ++ it) {
const V2f &center = it->center;
const float radius = it->radius;
const float radius2 = radius * radius;
//const float radius2 = radius * radius;
const float height_target = it->height;
B2f bbox(center - V2f(radius, radius), center + V2f(radius, radius));
B2i bboxi(
@ -774,8 +774,8 @@ void gcode_spread_points(
}
}
#endif
float area_circle_total2 = float(M_PI) * sqr(radius);
float area_err = fabs(area_circle_total2 - area_circle_total) / area_circle_total2;
// float area_circle_total2 = float(M_PI) * sqr(radius);
// float area_err = fabs(area_circle_total2 - area_circle_total) / area_circle_total2;
// printf("area_circle_total: %f, %f, %f\n", area_circle_total, area_circle_total2, area_err);
float volume_full = float(M_PI) * sqr(radius) * height_target;
// if (true) {
@ -905,8 +905,8 @@ void ExtrusionSimulator::set_image_size(const Point &image_size)
// printf("Allocating image data, allocated\n");
//FIXME fill the image with red vertical lines.
for (size_t r = 0; r < image_size.y(); ++ r) {
for (size_t c = 0; c < image_size.x(); c += 2) {
for (size_t r = 0; r < size_t(image_size.y()); ++ r) {
for (size_t c = 0; c < size_t(image_size.x()); c += 2) {
// Color red
pimpl->image_data[r * image_size.x() * 4 + c * 4] = 255;
// Opacity full
@ -958,7 +958,7 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const
float scalex = float(viewport.size().x()) / float(bbox.size().x());
float scaley = float(viewport.size().y()) / float(bbox.size().y());
float w = scale_(path.width) * scalex;
float h = scale_(path.height) * scalex;
//float h = scale_(path.height) * scalex;
w = scale_(path.mm3_per_mm / path.height) * scalex;
// printf("scalex: %f, scaley: %f\n", scalex, scaley);
// printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y);
@ -993,8 +993,8 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation
for (int r = 0; r < sz.y(); ++r) {
for (int c = 0; c < sz.x(); ++c) {
float p = 0;
for (int j = 0; j < pimpl->bitmap_oversampled; ++ j) {
for (int i = 0; i < pimpl->bitmap_oversampled; ++ i) {
for (unsigned int j = 0; j < pimpl->bitmap_oversampled; ++ j) {
for (unsigned int i = 0; i < pimpl->bitmap_oversampled; ++ i) {
if (pimpl->bitmap[r * pimpl->bitmap_oversampled + j][c * pimpl->bitmap_oversampled + i])
p += 1.f;
}

View File

@ -126,7 +126,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
Polygons surfaces_polygons = to_polygons(surfaces);
Polygons collapsed = diff(
surfaces_polygons,
offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2),
offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2),
true);
Polygons to_subtract;
to_subtract.reserve(collapsed.size() + number_polygons(surfaces));
@ -137,7 +137,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
surfaces_append(
surfaces,
intersection_ex(
offset(collapsed, distance_between_surfaces),
offset(collapsed, (float)distance_between_surfaces),
to_subtract,
true),
stInternalSolid);
@ -219,14 +219,14 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
f->z = layerm.layer()->print_z;
f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
// Maximum length of the perimeter segment linking two infill lines.
f->link_max_length = scale_(link_max_length);
f->link_max_length = (coord_t)scale_(link_max_length);
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER;
f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
// f->layer_height = h;
// apply half spacing using this flow's own spacing and generate infill
FillParams params;
params.density = 0.01 * density;
params.density = float(0.01 * density);
// params.dont_adjust = true;
params.dont_adjust = false;
Polylines polylines = f->fill_surface(&surface, params);
@ -240,7 +240,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// so we can safely ignore the slight variation that might have
// been applied to $f->flow_spacing
} else {
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow());
flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow());
}
// Save into layer.

View File

@ -130,14 +130,14 @@ static inline Point hilbert_n_to_xy(const size_t n)
}
}
int state = (ndigits & 1) ? 4 : 0;
int dirstate = (ndigits & 1) ? 0 : 4;
// int dirstate = (ndigits & 1) ? 0 : 4;
coord_t x = 0;
coord_t y = 0;
for (int i = (int)ndigits - 1; i >= 0; -- i) {
int digit = (n >> (i * 2)) & 3;
state += digit;
if (digit != 3)
dirstate = state; // lowest non-3 digit
// if (digit != 3)
// dirstate = state; // lowest non-3 digit
x |= digit_to_x[state] << i;
y |= digit_to_y[state] << i;
state = next_state[state];

View File

@ -287,7 +287,8 @@ public:
assert(aoffset1 < 0);
assert(aoffset2 < 0);
assert(aoffset2 < aoffset1);
bool sticks_removed = remove_sticks(polygons_src);
// bool sticks_removed =
remove_sticks(polygons_src);
// if (sticks_removed) printf("Sticks removed!\n");
polygons_outer = offset(polygons_src, aoffset1,
ClipperLib::jtMiter,
@ -481,7 +482,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
{
// This routine will propose a connecting line even if the connecting perimeter segment intersects
// iVertical line multiple times before reaching iIntersectionOther.
if (iIntersectionOther == -1)
if (iIntersectionOther == size_t(-1))
return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
const SegmentedIntersectionLine &il_this = segs[iVerticalLine];
@ -858,8 +859,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
if (il > ir)
// No vertical line intersects this segment.
continue;
assert(il >= 0 && il < segs.size());
assert(ir >= 0 && ir < segs.size());
assert(il >= 0 && size_t(il) < segs.size());
assert(ir >= 0 && size_t(ir) < segs.size());
for (int i = il; i <= ir; ++ i) {
coord_t this_x = segs[i].pos;
assert(this_x == i * line_spacing + x0);
@ -1159,8 +1160,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
int iSegAbove = -1;
int iSegBelow = -1;
{
SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
// Does the perimeter intersect the current vertical line above intrsctn?
for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i)
// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) {

View File

@ -849,7 +849,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical
{
// This routine will propose a connecting line even if the connecting perimeter segment intersects
// iVertical line multiple times before reaching iIntersectionOther.
if (iIntersectionOther == -1)
if (iIntersectionOther == size_t(-1))
return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED;
assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0));
const SegmentedIntersectionLine &il_this = segs[iVerticalLine];
@ -1284,8 +1284,8 @@ static bool fill_hatching_segments_legacy(
int iSegAbove = -1;
int iSegBelow = -1;
{
SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ?
// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW;
// Does the perimeter intersect the current vertical line above intrsctn?
for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i)
// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) {

View File

@ -11,10 +11,16 @@
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/foreach.hpp>
namespace pt = boost::property_tree;
#include <expat.h>
#include <Eigen/Dense>
#include "miniz_extension.hpp"
@ -33,6 +39,7 @@ const std::string RELATIONSHIPS_FILE = "_rels/.rels";
const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
const std::string LAYER_CONFIG_RANGES_FILE = "Metadata/Prusa_Slicer_layer_config_ranges.xml";
const std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
const char* MODEL_TAG = "model";
@ -331,6 +338,7 @@ namespace Slic3r {
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
typedef std::map<int, t_layer_config_ranges> IdToLayerConfigRangesMap;
typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
// Version of the 3mf file
@ -347,6 +355,7 @@ namespace Slic3r {
CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata;
IdToLayerHeightsProfileMap m_layer_heights_profiles;
IdToLayerConfigRangesMap m_layer_config_ranges;
IdToSlaSupportPointsMap m_sla_support_points;
std::string m_curr_metadata_name;
std::string m_curr_characters;
@ -365,6 +374,7 @@ namespace Slic3r {
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename);
@ -476,6 +486,7 @@ namespace Slic3r {
m_curr_config.volume_id = -1;
m_objects_metadata.clear();
m_layer_heights_profiles.clear();
m_layer_config_ranges.clear();
m_sla_support_points.clear();
m_curr_metadata_name.clear();
m_curr_characters.clear();
@ -546,9 +557,14 @@ namespace Slic3r {
if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE))
{
// extract slic3r lazer heights profile file
// extract slic3r layer heights profile file
_extract_layer_heights_profile_config_from_archive(archive, stat);
}
if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE))
{
// extract slic3r layer config ranges file
_extract_layer_config_ranges_from_archive(archive, stat);
}
else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE))
{
// extract sla support points file
@ -592,6 +608,11 @@ namespace Slic3r {
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
model_object->layer_height_profile = obj_layer_heights_profile->second;
// m_layer_config_ranges are indexed by a 1 based model object index.
IdToLayerConfigRangesMap::iterator obj_layer_config_ranges = m_layer_config_ranges.find(object.second + 1);
if (obj_layer_config_ranges != m_layer_config_ranges.end())
model_object->layer_config_ranges = obj_layer_config_ranges->second;
// m_sla_support_points are indexed by a 1 based model object index.
IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.second + 1);
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty()) {
@ -675,7 +696,7 @@ namespace Slic3r {
if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1))
{
char error_buf[1024];
::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser));
::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
add_error(error_buf);
return false;
}
@ -769,6 +790,66 @@ namespace Slic3r {
}
}
void _3MF_Importer::_extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
{
if (stat.m_uncomp_size > 0)
{
std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0) {
add_error("Error while reading layer config ranges data to buffer");
return;
}
std::istringstream iss(buffer); // wrap returned xml to istringstream
pt::ptree objects_tree;
pt::read_xml(iss, objects_tree);
for (const auto& object : objects_tree.get_child("objects"))
{
pt::ptree object_tree = object.second;
int obj_idx = object_tree.get<int>("<xmlattr>.id", -1);
if (obj_idx <= 0) {
add_error("Found invalid object id");
continue;
}
IdToLayerConfigRangesMap::iterator object_item = m_layer_config_ranges.find(obj_idx);
if (object_item != m_layer_config_ranges.end()) {
add_error("Found duplicated layer config range");
continue;
}
t_layer_config_ranges config_ranges;
for (const auto& range : object_tree)
{
if (range.first != "range")
continue;
pt::ptree range_tree = range.second;
double min_z = range_tree.get<double>("<xmlattr>.min_z");
double max_z = range_tree.get<double>("<xmlattr>.max_z");
// get Z range information
DynamicPrintConfig& config = config_ranges[{ min_z, max_z }];
for (const auto& option : range_tree)
{
if (option.first != "option")
continue;
std::string opt_key = option.second.get<std::string>("<xmlattr>.opt_key");
std::string value = option.second.data();
config.set_deserialize(opt_key, value);
}
}
if (!config_ranges.empty())
m_layer_config_ranges.insert(IdToLayerConfigRangesMap::value_type(obj_idx, config_ranges));
}
}
}
void _3MF_Importer::_extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
{
if (stat.m_uncomp_size > 0)
@ -895,7 +976,7 @@ namespace Slic3r {
if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1))
{
char error_buf[1024];
::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser));
::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser));
add_error(error_buf);
return false;
}
@ -1452,7 +1533,7 @@ namespace Slic3r {
object->second.metadata.emplace_back(key, value);
else if (type == VOLUME_TYPE)
{
if (m_curr_config.volume_id < object->second.volumes.size())
if (size_t(m_curr_config.volume_id) < object->second.volumes.size())
object->second.volumes[m_curr_config.volume_id].metadata.emplace_back(key, value);
}
else
@ -1624,6 +1705,7 @@ namespace Slic3r {
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, const IdToObjectDataMap &objects_data);
@ -1684,6 +1766,16 @@ namespace Slic3r {
return false;
}
// Adds layer config ranges file ("Metadata/Slic3r_PE_layer_config_ranges.txt").
// All layer height profiles of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
if (!_add_layer_config_ranges_file_to_archive(archive, model))
{
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
// Adds sla support points file ("Metadata/Slic3r_PE_sla_support_points.txt").
// All sla support points of all ModelObjects are stored here, indexed by 1 based index of the ModelObject in Model.
// The index differes from the index of an object ID of an object instance of a 3MF file!
@ -1895,7 +1987,7 @@ namespace Slic3r {
return false;
}
vertices_count += its.vertices.size();
vertices_count += (int)its.vertices.size();
const Transform3d& matrix = volume->get_matrix();
@ -1925,7 +2017,7 @@ namespace Slic3r {
// updates triangle offsets
volume_it->second.first_triangle_id = triangles_count;
triangles_count += its.indices.size();
triangles_count += (int)its.indices.size();
volume_it->second.last_triangle_id = triangles_count - 1;
for (size_t i = 0; i < its.indices.size(); ++ i)
@ -2013,6 +2105,70 @@ namespace Slic3r {
return true;
}
bool _3MF_Exporter::_add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::string out = "";
pt::ptree tree;
unsigned int object_cnt = 0;
for (const ModelObject* object : model.objects)
{
object_cnt++;
const t_layer_config_ranges& ranges = object->layer_config_ranges;
if (!ranges.empty())
{
pt::ptree& obj_tree = tree.add("objects.object","");
obj_tree.put("<xmlattr>.id", object_cnt);
// Store the layer config ranges.
for (const auto& range : ranges)
{
pt::ptree& range_tree = obj_tree.add("range", "");
// store minX and maxZ
range_tree.put("<xmlattr>.min_z", range.first.first);
range_tree.put("<xmlattr>.max_z", range.first.second);
// store range configuration
const DynamicPrintConfig& config = range.second;
for (const std::string& opt_key : config.keys())
{
pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key));
opt_tree.put("<xmlattr>.opt_key", opt_key);
}
}
}
}
if (!tree.empty())
{
std::ostringstream oss;
boost::property_tree::write_xml(oss, tree);
out = oss.str();
// Post processing("beautification") of the output string for a better preview
boost::replace_all(out, "><object", ">\n <object");
boost::replace_all(out, "><range", ">\n <range");
boost::replace_all(out, "><option", ">\n <option");
boost::replace_all(out, "></range>", ">\n </range>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty())
{
if (!mz_zip_writer_add_mem(&archive, LAYER_CONFIG_RANGES_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
{
add_error("Unable to add layer heights profile file to archive");
return false;
}
}
return true;
}
bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
{
std::string out = "";
@ -2060,7 +2216,7 @@ namespace Slic3r {
for (const std::string &key : config.keys())
if (key != "compatible_printers")
out += "; " + key + " = " + config.serialize(key) + "\n";
out += "; " + key + " = " + config.opt_serialize(key) + "\n";
if (!out.empty())
{
@ -2094,7 +2250,7 @@ namespace Slic3r {
// stores object's config data
for (const std::string& key : obj->config.keys())
{
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.serialize(key) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n";
}
for (const ModelVolume* volume : obj_metadata.second.object->volumes)
@ -2124,7 +2280,7 @@ namespace Slic3r {
// stores volume's config data
for (const std::string& key : volume->config.keys())
{
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.serialize(key) << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
stream << " </" << VOLUME_TAG << ">\n";

View File

@ -106,6 +106,9 @@ struct AMFParserContext
// amf/material/metadata
NODE_TYPE_OBJECT, // amf/object
// amf/object/metadata
NODE_TYPE_LAYER_CONFIG, // amf/object/layer_config_ranges
NODE_TYPE_RANGE, // amf/object/layer_config_ranges/range
// amf/object/layer_config_ranges/range/metadata
NODE_TYPE_MESH, // amf/object/mesh
NODE_TYPE_VERTICES, // amf/object/mesh/vertices
NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex
@ -189,7 +192,7 @@ struct AMFParserContext
};
// Version of the amf file
unsigned int m_version;
unsigned int m_version;
// Current Expat XML parser instance.
XML_Parser m_parser;
// Model to receive objects extracted from an AMF file.
@ -260,7 +263,9 @@ void AMFParserContext::startElement(const char *name, const char **atts)
m_value[0] = get_attribute(atts, "type");
node_type_new = NODE_TYPE_METADATA;
}
} else if (strcmp(name, "mesh") == 0) {
} else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT)
node_type_new = NODE_TYPE_LAYER_CONFIG;
else if (strcmp(name, "mesh") == 0) {
if (m_path[1] == NODE_TYPE_OBJECT)
node_type_new = NODE_TYPE_MESH;
} else if (strcmp(name, "instance") == 0) {
@ -317,6 +322,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
else if (strcmp(name, "mirrorz") == 0)
node_type_new = NODE_TYPE_MIRRORZ;
}
else if (m_path[2] == NODE_TYPE_LAYER_CONFIG && strcmp(name, "range") == 0) {
assert(m_object);
node_type_new = NODE_TYPE_RANGE;
}
break;
case 4:
if (m_path[3] == NODE_TYPE_VERTICES) {
@ -334,6 +343,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
} else if (strcmp(name, "triangle") == 0)
node_type_new = NODE_TYPE_TRIANGLE;
}
else if (m_path[3] == NODE_TYPE_RANGE && strcmp(name, "metadata") == 0) {
m_value[0] = get_attribute(atts, "type");
node_type_new = NODE_TYPE_METADATA;
}
break;
case 5:
if (strcmp(name, "coordinates") == 0) {
@ -571,8 +584,13 @@ void AMFParserContext::endElement(const char * /* name */)
config = &m_material->config;
else if (m_path[1] == NODE_TYPE_OBJECT && m_object)
config = &m_object->config;
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
}
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
config = &m_volume->config;
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_RANGE && m_object && !m_object->layer_config_ranges.empty()) {
auto it = --m_object->layer_config_ranges.end();
config = &it->second;
}
if (config)
config->set_deserialize(opt_key, m_value[1]);
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
@ -598,7 +616,7 @@ void AMFParserContext::endElement(const char * /* name */)
if (end != nullptr)
*end = 0;
point(coord_idx) = atof(p);
point(coord_idx) = float(atof(p));
if (++coord_idx == 5) {
m_object->sla_support_points.push_back(sla::SupportPoint(point));
coord_idx = 0;
@ -609,6 +627,16 @@ void AMFParserContext::endElement(const char * /* name */)
}
m_object->sla_points_status = sla::PointsStatus::UserModified;
}
else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE &&
m_object && strcmp(opt_key, "layer_height_range") == 0) {
// Parse object's layer_height_range, a semicolon separated doubles.
char* p = const_cast<char*>(m_value[1].c_str());
char* end = strchr(p, ';');
*end = 0;
const t_layer_height_range range = {double(atof(p)), double(atof(end + 1))};
m_object->layer_config_ranges[range];
}
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
if (strcmp(opt_key, "modifier") == 0) {
// Is this volume a modifier volume?
@ -694,8 +722,8 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
}
int done = feof(pFile);
if (XML_Parse(parser, buff, len, done) == XML_STATUS_ERROR) {
printf("AMF parser: Parse error at line %ul:\n%s\n",
XML_GetCurrentLineNumber(parser),
printf("AMF parser: Parse error at line %d:\n%s\n",
(int)XML_GetCurrentLineNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser)));
break;
}
@ -753,7 +781,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
if (!XML_ParseBuffer(parser, (int)stat.m_uncomp_size, 1))
{
printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser));
printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), (int)XML_GetCurrentLineNumber(parser));
close_zip_reader(&archive);
return false;
}
@ -873,7 +901,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
std::string str_config = "\n";
for (const std::string &key : config->keys())
if (key != "compatible_printers")
str_config += "; " + key + " = " + config->serialize(key) + "\n";
str_config += "; " + key + " = " + config->opt_serialize(key) + "\n";
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(str_config) << "</metadata>\n";
}
@ -885,7 +913,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
for (const auto &attr : material.second->attributes)
stream << " <metadata type=\"" << attr.first << "\">" << attr.second << "</metadata>\n";
for (const std::string &key : material.second->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.serialize(key) << "</metadata>\n";
stream << " <metadata type=\"slic3r." << key << "\">" << material.second->config.opt_serialize(key) << "</metadata>\n";
stream << " </material>\n";
}
std::string instances;
@ -893,7 +921,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
ModelObject *object = model->objects[object_id];
stream << " <object id=\"" << object_id << "\">\n";
for (const std::string &key : object->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.opt_serialize(key) << "</metadata>\n";
if (!object->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
const std::vector<double> &layer_height_profile = object->layer_height_profile;
@ -905,7 +933,30 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << ";" << layer_height_profile[i];
stream << "\n </metadata>\n";
}
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
// Export layer height ranges including the layer range specific config overrides.
const t_layer_config_ranges& config_ranges = object->layer_config_ranges;
if (!config_ranges.empty())
{
// Store the layer config range as a single semicolon separated list.
stream << " <layer_config_ranges>\n";
size_t layer_counter = 0;
for (auto range : config_ranges) {
stream << " <range id=\"" << layer_counter << "\">\n";
stream << " <metadata type=\"slic3r.layer_height_range\">";
stream << range.first.first << ";" << range.first.second << "</metadata>\n";
for (const std::string& key : range.second.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << range.second.opt_serialize(key) << "</metadata>\n";
stream << " </range>\n";
layer_counter++;
}
stream << " </layer_config_ranges>\n";
}
const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
if (!sla_support_points.empty()) {
@ -941,7 +992,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " </coordinates>\n";
stream << " </vertex>\n";
}
num_vertices += its.vertices.size();
num_vertices += (int)its.vertices.size();
}
stream << " </vertices>\n";
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
@ -952,14 +1003,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
else
stream << " <volume materialid=\"" << volume->material_id() << "\">\n";
for (const std::string &key : volume->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.opt_serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
const indexed_triangle_set &its = volume->mesh().its;
for (size_t i = 0; i < (int)its.indices.size(); ++i) {
for (size_t i = 0; i < its.indices.size(); ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)
stream << " <v" << j + 1 << ">" << its.indices[i][j] + vertices_offset << "</v" << j + 1 << ">\n";

View File

@ -176,8 +176,7 @@ static bool obj_parseline(const char *line, ObjData &data)
EATWS();
if (*line == 0)
return false;
// number of vertices of this face
int n = 0;
// current vertex to be parsed
ObjVertex vertex;
char *endptr = 0;
@ -266,7 +265,6 @@ static bool obj_parseline(const char *line, ObjData &data)
{
// o [object name]
EATWS();
const char *name = line;
while (*line != ' ' && *line != '\t' && *line != 0)
++ line;
// copy name to line.

View File

@ -4,7 +4,7 @@
#include "EdgeGrid.hpp"
#include "Geometry.hpp"
#include "GCode/PrintExtents.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include "GCode/WipeTower.hpp"
#include "Utils.hpp"
#include <algorithm>
@ -162,9 +162,9 @@ std::string Wipe::wipe(GCode &gcodegen, bool toolchange)
return gcode;
}
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt)
{
return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1)));
return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1)));
}
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
@ -174,47 +174,97 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// Toolchangeresult.gcode assumes the wipe tower corner is at the origin
// We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
float alpha = m_wipe_tower_rotation/180.f * float(M_PI);
WipeTower::xy start_pos = tcr.start_pos;
WipeTower::xy end_pos = tcr.end_pos;
start_pos.rotate(alpha);
start_pos.translate(m_wipe_tower_pos);
end_pos.rotate(alpha);
end_pos.translate(m_wipe_tower_pos);
std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
Vec2f start_pos = tcr.start_pos;
Vec2f end_pos = tcr.end_pos;
if (!tcr.priming) {
start_pos = Eigen::Rotation2Df(alpha) * start_pos;
start_pos += m_wipe_tower_pos;
end_pos = Eigen::Rotation2Df(alpha) * end_pos;
end_pos += m_wipe_tower_pos;
}
std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
// Disable linear advance for the wipe tower operations.
gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
// Move over the wipe tower.
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
gcode += gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to(
wipe_tower_point_to_object_point(gcodegen, start_pos),
erMixed,
"Travel to a Wipe Tower");
gcode += gcodegen.unretract();
// Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back.
gcode += tcr_rotated_gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
gcodegen.writer().toolchange(new_extruder_id);
if (!tcr.priming) {
// Move over the wipe tower.
// Retract for a tool change, using the toolchange retract value and setting the priming extra length.
gcode += gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to(
wipe_tower_point_to_object_point(gcodegen, start_pos),
erMixed,
"Travel to a Wipe Tower");
gcode += gcodegen.unretract();
}
// Process the end filament gcode.
std::string end_filament_gcode_str;
if (gcodegen.writer().extruder() != nullptr) {
// Process the custom end_filament_gcode in case of single_extruder_multi_material.
unsigned int old_extruder_id = gcodegen.writer().extruder()->id();
const std::string &end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id);
if (gcodegen.writer().extruder() != nullptr && ! end_filament_gcode.empty()) {
end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
check_add_eol(end_filament_gcode_str);
}
}
// Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament.
// Otherwise, leave control to the user completely.
std::string toolchange_gcode_str;
if (true /*gcodegen.writer().extruder() != nullptr*/) {
const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value;
if (!toolchange_gcode.empty()) {
DynamicConfig config;
int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1;
config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id));
config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id));
config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z));
toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config);
check_add_eol(toolchange_gcode_str);
}
std::string toolchange_command;
if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)))
toolchange_command = gcodegen.writer().toolchange(new_extruder_id);
if (toolchange_gcode.empty())
toolchange_gcode_str = toolchange_command;
else {
// We have informed the m_writer about the current extruder_id, we can ignore the generated G-code.
}
}
gcodegen.placeholder_parser().set("current_extruder", new_extruder_id);
// Always append the filament start G-code even if the extruder did not switch,
// because the wipe tower resets the linear advance and we want it to be re-enabled.
// Process the start filament gcode.
std::string start_filament_gcode_str;
const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id);
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only.
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id));
gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
check_add_eol(gcode);
start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config);
check_add_eol(start_filament_gcode_str);
}
// Insert the end filament, toolchange, and start filament gcode into the generated gcode.
DynamicConfig config;
config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str));
config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str));
config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str));
std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config);
unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode);
gcode += tcr_gcode;
check_add_eol(toolchange_gcode_str);
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y));
gcodegen.writer().travel_to_xy(end_pos.cast<double>());
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Prepare a future wipe.
@ -224,8 +274,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
end_pos.y)));
Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left,
end_pos.y())));
}
// Let the planner know we are traveling between objects.
@ -235,14 +285,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const
{
std::istringstream gcode_str(gcode_original);
std::string gcode_out;
std::string line;
WipeTower::xy pos = start_pos;
WipeTower::xy transformed_pos;
WipeTower::xy old_pos(-1000.1f, -1000.1f);
Vec2f pos = start_pos;
Vec2f transformed_pos;
Vec2f old_pos(-1000.1f, -1000.1f);
while (gcode_str) {
std::getline(gcode_str, line); // we read the gcode line by line
@ -253,25 +303,25 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
char ch = 0;
while (line_str >> ch) {
if (ch == 'X')
line_str >> pos.x;
line_str >> pos.x();
else
if (ch == 'Y')
line_str >> pos.y;
line_str >> pos.y();
else
line_out << ch;
}
transformed_pos = pos;
transformed_pos.rotate(angle);
transformed_pos.translate(translation);
transformed_pos = Eigen::Rotation2Df(angle) * transformed_pos;
transformed_pos += translation;
if (transformed_pos != old_pos) {
line = line_out.str();
char buf[2048] = "G1";
if (transformed_pos.x != old_pos.x)
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
if (transformed_pos.y != old_pos.y)
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
if (transformed_pos.x() != old_pos.x())
sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x());
if (transformed_pos.y() != old_pos.y())
sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y());
line.replace(line.find("G1 "), 3, buf);
old_pos = transformed_pos;
@ -288,27 +338,36 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
assert(m_layer_idx == 0);
std::string gcode;
if (&m_priming != nullptr && ! m_priming.extrusions.empty()) {
if (&m_priming != nullptr) {
// Disable linear advance for the wipe tower operations.
gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
// Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back.
gcode += m_priming.gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
unsigned int current_extruder_id = m_priming.extrusions.back().tool;
gcodegen.writer().toolchange(current_extruder_id);
gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
//gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n"));
for (const WipeTower::ToolChangeResult& tcr : m_priming) {
if (!tcr.extrusions.empty())
gcode += append_tcr(gcodegen, tcr, tcr.new_tool);
// Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back.
//gcode += tcr.gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
// unsigned int current_extruder_id = tcr.extrusions.back().tool;
// gcodegen.writer().toolchange(current_extruder_id);
// gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
}
// A phony move to the end position at the wipe tower.
gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
/* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
// Start the wipe at the current position.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
WipeTower::xy((std::abs(m_left - m_priming.end_pos.x) < std::abs(m_right - m_priming.end_pos.x)) ? m_right : m_left,
m_priming.end_pos.y)));
WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left,
m_priming.back().end_pos.y)));*/
}
return gcode;
}
@ -316,10 +375,10 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer)
{
std::string gcode;
assert(m_layer_idx >= 0 && m_layer_idx <= m_tool_changes.size());
assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size());
if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
if (m_layer_idx < m_tool_changes.size()) {
assert(m_tool_change_idx < m_tool_changes[m_layer_idx].size());
if (m_layer_idx < (int)m_tool_changes.size()) {
assert(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size());
gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id);
}
m_brim_done = true;
@ -586,6 +645,9 @@ void GCode::_do_export(Print &print, FILE *file)
}
m_analyzer.set_extruder_offsets(extruder_offsets);
// tell analyzer about the gcode flavor
m_analyzer.set_gcode_flavor(print.config().gcode_flavor);
// resets analyzer's tracking data
m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm;
m_last_width = GCodeAnalyzer::Default_Width;
@ -804,24 +866,16 @@ void GCode::_do_export(Print &print, FILE *file)
// Write the custom start G-code
_writeln(file, start_gcode);
// Process filament-specific gcode in extruder order.
if (print.config().single_extruder_multi_material) {
if (has_wipe_tower) {
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
} else {
// Only initialize the initial extruder.
// Process filament-specific gcode.
/* if (has_wipe_tower) {
// Wipe tower will control the extruder switching, it will call the start_filament_gcode.
} else {
DynamicConfig config;
config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id)));
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
}
} else {
DynamicConfig config;
for (const std::string &start_gcode : print.config().start_filament_gcode.values) {
int extruder_id = (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front());
config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id));
_writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, extruder_id, &config));
}
_writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config));
}
*/
this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true);
print.throw_if_canceled();
@ -1057,6 +1111,10 @@ void GCode::_do_export(Print &print, FILE *file)
print.m_print_statistics.clear();
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
print.m_print_statistics.estimated_normal_color_print_times = m_normal_time_estimator.get_color_times_dhms();
if (m_silent_time_estimator_enabled)
print.m_print_statistics.estimated_silent_color_print_times = m_silent_time_estimator.get_color_times_dhms();
std::vector<Extruder> extruders = m_writer.extruders();
if (! extruders.empty()) {
std::pair<std::string, unsigned int> out_filament_used_mm ("; filament used [mm] = ", 0);
@ -1253,7 +1311,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c
int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id);
if (temp_by_gcode >= 0 && temp_by_gcode < 1000)
temp = temp_by_gcode;
m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id);
m_writer.set_temperature(temp, wait, first_printing_extruder_id);
} else {
// Custom G-code does not set the extruder temperature. Do it now.
if (print.config().single_extruder_multi_material.value) {
@ -1344,11 +1402,11 @@ void GCode::process_layer(
// Check whether it is possible to apply the spiral vase logic for this layer.
// Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) {
bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= print.config().skirt_height.value && ! print.has_infinite_skirt());
bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
if (enable) {
for (const LayerRegion *layer_region : layer.regions())
if (layer_region->region()->config().bottom_solid_layers.value > layer.id() ||
layer_region->perimeters.items_count() > 1 ||
if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() ||
layer_region->perimeters.items_count() > 1u ||
layer_region->fills.items_count() > 0) {
enable = false;
break;
@ -1405,7 +1463,9 @@ void GCode::process_layer(
m_colorprint_heights.erase(m_colorprint_heights.begin());
colorprint_change = true;
}
if (colorprint_change && print.extruders().size()==1)
// we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
if (colorprint_change && print./*extruders()*/config().nozzle_diameter.size()==1)
gcode += "M600\n";
@ -1414,11 +1474,11 @@ void GCode::process_layer(
bool extrude_skirt =
! print.skirt().entities.empty() &&
// Not enough skirt layers printed yet.
(m_skirt_done.size() < print.config().skirt_height.value || print.has_infinite_skirt()) &&
(m_skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) &&
// This print_z has not been extruded yet
(m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON &&
// and this layer is the 1st layer, or it is an object layer, or it is a raft layer.
(first_layer || object_layer != nullptr || support_layer->id() < m_config.raft_layers.value);
(first_layer || object_layer != nullptr || support_layer->id() < (size_t)m_config.raft_layers.value);
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder;
coordf_t skirt_height = 0.;
if (extrude_skirt) {
@ -1749,7 +1809,7 @@ void GCode::append_full_config(const Print& print, std::string& str)
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
str += "; " + key + " = " + cfg->opt_serialize(key) + "\n";
}
const DynamicConfig &full_config = print.placeholder_parser().config();
for (const char *key : {
@ -2067,19 +2127,18 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// Retrieve the last start position for this object.
float last_pos_weight = 1.f;
switch (seam_position) {
case spAligned:
if (seam_position == spAligned) {
// Seam is aligned to the seam at the preceding layer.
if (m_layer != NULL && m_seam_position.count(m_layer->object()) > 0) {
last_pos = m_seam_position[m_layer->object()];
last_pos_weight = 1.f;
}
break;
case spRear:
}
else if (seam_position == spRear) {
last_pos = m_layer->object()->bounding_box().center();
last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius());
last_pos_weight = 5.f;
break;
}
// Insert a projection of last_pos into the polygon.
@ -2088,7 +2147,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
Points::iterator it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r);
last_pos_proj_idx = it - polygon.points.begin();
}
Point last_pos_proj = polygon.points[last_pos_proj_idx];
// Parametrize the polygon by its length.
std::vector<float> lengths = polygon_parameter_by_length(polygon);
@ -2098,7 +2157,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces.
const float penaltyConvexVertex = 1.f;
const float penaltyFlatSurface = 5.f;
const float penaltySeam = 1.3f;
const float penaltyOverhangHalf = 10.f;
// Penalty for visible seams.
for (size_t i = 0; i < polygon.points.size(); ++ i) {
@ -2143,7 +2201,11 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// Signed distance is positive outside the object, negative inside the object.
// The point is considered at an overhang, if it is more than nozzle radius
// outside of the lower layer contour.
bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
#ifdef NDEBUG // to suppress unused variable warning in release mode
(*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
#else
bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist);
#endif
// If the approximate Signed Distance Field was initialized over lower_layer_edge_grid,
// then the signed distnace shall always be known.
assert(found);
@ -2554,7 +2616,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
gcode += buf;
}
if (m_last_mm3_per_mm != path.mm3_per_mm)
if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm))
{
m_last_mm3_per_mm = path.mm3_per_mm;
@ -2732,38 +2794,57 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
m_wipe.reset_path();
if (m_writer.extruder() != nullptr) {
// Process the custom end_filament_gcode in case of single_extruder_multi_material.
// Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower
// so it should not be injected twice.
unsigned int old_extruder_id = m_writer.extruder()->id();
const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id);
if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) {
if (! end_filament_gcode.empty()) {
gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id);
check_add_eol(gcode);
}
}
m_placeholder_parser.set("current_extruder", extruder_id);
if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) {
// Process the custom toolchange_gcode.
DynamicConfig config;
config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id()));
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config);
check_add_eol(gcode);
}
// If ooze prevention is enabled, park current extruder in the nearest
// standby point and set it to the standby temperature.
if (m_ooze_prevention.enable && m_writer.extruder() != nullptr)
gcode += m_ooze_prevention.pre_toolchange(*this);
// Append the toolchange command.
gcode += m_writer.toolchange(extruder_id);
// Append the filament start G-code for single_extruder_multi_material.
const std::string& toolchange_gcode = m_config.toolchange_gcode.value;
// Process the custom toolchange_gcode. If it is empty, insert just a Tn command.
if (!toolchange_gcode.empty()) {
DynamicConfig config;
config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1 )));
config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id));
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
config.set_key_value("layer_z", new ConfigOptionFloat(print_z));
gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config);
check_add_eol(gcode);
}
// We inform the writer about what is happening, but we may not use the resulting gcode.
std::string toolchange_command = m_writer.toolchange(extruder_id);
if (toolchange_gcode.empty())
gcode += toolchange_command;
else {
// user provided his own toolchange gcode, no need to do anything
}
// Set the temperature if the wipe tower didn't (not needed for non-single extruder MM)
if (m_config.single_extruder_multi_material && !m_config.wipe_tower) {
int temp = (m_layer_index == 0 ? m_config.first_layer_temperature.get_at(extruder_id) :
m_config.temperature.get_at(extruder_id));
gcode += m_writer.set_temperature(temp, false);
}
m_placeholder_parser.set("current_extruder", extruder_id);
// Append the filament start G-code.
const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id);
if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the active filament only.
if (! start_filament_gcode.empty()) {
// Process the start_filament_gcode for the new filament.
gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id);
check_add_eol(gcode);
}

View File

@ -83,7 +83,7 @@ class WipeTowerIntegration {
public:
WipeTowerIntegration(
const PrintConfig &print_config,
const WipeTower::ToolChangeResult &priming,
const std::vector<WipeTower::ToolChangeResult> &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const WipeTower::ToolChangeResult &final_purge) :
m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
@ -108,15 +108,15 @@ private:
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
// Postprocesses gcode: rotates and moves all G1 extrusions and returns result
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const;
// Left / right edges of the wipe tower, for the planning of wipe moves.
const float m_left;
const float m_right;
const WipeTower::xy m_wipe_tower_pos;
const Vec2f m_wipe_tower_pos;
const float m_wipe_tower_rotation;
// Reference to cached values at the Printer class.
const WipeTower::ToolChangeResult &m_priming;
const std::vector<WipeTower::ToolChangeResult> &m_priming;
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
const WipeTower::ToolChangeResult &m_final_purge;
// Current layer index.

View File

@ -106,6 +106,11 @@ void GCodeAnalyzer::set_extruder_offsets(const GCodeAnalyzer::ExtruderOffsetsMap
m_extruder_offsets = extruder_offsets;
}
void GCodeAnalyzer::set_gcode_flavor(const GCodeFlavor& flavor)
{
m_gcode_flavor = flavor;
}
void GCodeAnalyzer::reset()
{
_set_units(Millimeters);
@ -249,6 +254,14 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
_processM83(line);
break;
}
case 108:
case 135:
{
// these are used by MakerWare and Sailfish firmwares
// for tool changing - we can process it in one place
_processM108orM135(line);
break;
}
}
break;
@ -426,9 +439,27 @@ void GCodeAnalyzer::_processM600(const GCodeReader::GCodeLine& line)
_set_cp_color_id(m_state.cur_cp_color_id);
}
void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
void GCodeAnalyzer::_processM108orM135(const GCodeReader::GCodeLine& line)
{
// These M-codes are used by MakerWare and Sailfish to change active tool.
// They have to be processed otherwise toolchanges will be unrecognised
// by the analyzer - see https://github.com/prusa3d/PrusaSlicer/issues/2566
size_t code = ::atoi(&(line.cmd()[1]));
if ((code == 108 && m_gcode_flavor == gcfSailfish)
|| (code == 135 && m_gcode_flavor == gcfMakerWare)) {
std::string cmd = line.raw();
size_t T_pos = cmd.find("T");
if (T_pos != std::string::npos) {
cmd = cmd.substr(T_pos);
_processT(cmd);
}
}
}
void GCodeAnalyzer::_processT(const std::string& cmd)
{
std::string cmd = line.cmd();
if (cmd.length() > 1)
{
unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
@ -442,6 +473,11 @@ void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
}
}
void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
{
_processT(line.cmd());
}
bool GCodeAnalyzer::_process_tags(const GCodeReader::GCodeLine& line)
{
std::string comment = line.comment();

View File

@ -106,6 +106,7 @@ private:
GCodeReader m_parser;
TypeToMovesMap m_moves_map;
ExtruderOffsetsMap m_extruder_offsets;
GCodeFlavor m_gcode_flavor;
// The output of process_layer()
std::string m_process_output;
@ -115,6 +116,8 @@ public:
void set_extruder_offsets(const ExtruderOffsetsMap& extruder_offsets);
void set_gcode_flavor(const GCodeFlavor& flavor);
// Reinitialize the analyzer
void reset();
@ -164,10 +167,14 @@ private:
// Set extruder to relative mode
void _processM83(const GCodeReader::GCodeLine& line);
// Set tool (MakerWare and Sailfish flavor)
void _processM108orM135(const GCodeReader::GCodeLine& line);
// Set color change
void _processM600(const GCodeReader::GCodeLine& line);
// Processes T line (Select Tool)
void _processT(const std::string& command);
void _processT(const GCodeReader::GCodeLine& line);
// Processes the tags

View File

@ -672,7 +672,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder)
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
if (layer_id >= EXTRUDER_CONFIG(disable_fan_first_layers)) {
if (layer_id >= (size_t)EXTRUDER_CONFIG(disable_fan_first_layers)) {
int max_fan_speed = EXTRUDER_CONFIG(max_fan_speed);
float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time));
float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time));

View File

@ -404,6 +404,8 @@ std::string GCodePreviewData::get_legend_title() const
return L("Tool");
case Extrusion::ColorPrint:
return L("Color Print");
case Extrusion::Num_View_Types:
break; // just to supress warning about non-handled value
}
return "";
@ -466,7 +468,7 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
}
case Extrusion::Tool:
{
unsigned int tools_colors_count = tool_colors.size() / 4;
unsigned int tools_colors_count = (unsigned int)tool_colors.size() / 4;
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
@ -491,20 +493,25 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.emplace_back(Slic3r::I18N::translate(L("Default print color")), color);
break;
}
std::string id_str = std::to_string(i + 1) + ": ";
if (i == 0) {
items.emplace_back((boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("up to %.2f mm"))) % cp_values[0].first).str(), color);
break;
}
if (i == color_print_cnt) {
items.emplace_back((boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i-1].second).str(), color);
items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("above %.2f mm"))) % cp_values[i - 1].second).str(), color);
continue;
}
// items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1] % cp_values[i]).str(), color);
items.emplace_back((boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i-1].second % cp_values[i].first).str(), color);
items.emplace_back(id_str + (boost::format(Slic3r::I18N::translate(L("%.2f - %.2f mm"))) % cp_values[i - 1].second% cp_values[i].first).str(), color);
}
break;
}
case Extrusion::Num_View_Types:
break; // just to supress warning about non-handled value
}
return items;

View File

@ -149,8 +149,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
Vec2d delta = 0.5 * Vec2d(e.width, e.width);
Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y);
Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y);
Vec2d p1 = trafo * (&e - 1)->pos.cast<double>();
Vec2d p2 = trafo * e.pos.cast<double>();
bbox.merge(p1.cwiseMin(p2) - delta);
bbox.merge(p1.cwiseMax(p2) + delta);
}
@ -165,18 +165,19 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
{
BoundingBoxf bbox;
if (print.wipe_tower_data().priming != nullptr) {
const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming;
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y);
Vec2d p2(e.pos.x, e.pos.y);
bbox.merge(p1);
coordf_t radius = 0.5 * e.width;
bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
for (const WipeTower::ToolChangeResult &tcr : *print.wipe_tower_data().priming) {
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
const Vec2d& p1 = (&e - 1)->pos.cast<double>();
const Vec2d& p2 = e.pos.cast<double>();
bbox.merge(p1);
coordf_t radius = 0.5 * e.width;
bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
}
}
}
}

View File

@ -24,7 +24,7 @@ std::string SpiralVase::process_layer(const std::string &gcode)
// Get total XY length for this layer by summing all extrusion moves.
float total_layer_length = 0;
float layer_height = 0;
float z;
float z = 0.f;
bool set_z = false;
{

View File

@ -324,9 +324,8 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
m_layer_tools[j].has_wipe_tower = true;
} else {
LayerTools &lt_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new);
LayerTools &lt_prev = m_layer_tools[j - 1];
LayerTools &lt_next = m_layer_tools[j + 1];
assert(! lt_prev.extruders.empty() && ! lt_next.extruders.empty());
assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty());
// FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now.
// If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might
// still be worth looking into it more and decide if it is a bug or an obsolete assert.
@ -495,9 +494,6 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (!is_overriddable(*fill, print.config(), *object, region))
continue;
// What extruder would this normally be printed with?
unsigned int correct_extruder = Print::get_extruder(*fill, region);
if (volume_to_wipe<=0)
continue;

View File

@ -1,95 +1,30 @@
#ifndef slic3r_WipeTower_hpp_
#define slic3r_WipeTower_hpp_
#ifndef WipeTower_
#define WipeTower_
#include <math.h>
#include <utility>
#include <cmath>
#include <string>
#include <vector>
#include <sstream>
#include <utility>
#include <algorithm>
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r
{
// A pure virtual WipeTower definition.
class WipeTowerWriter;
class WipeTower
{
public:
// Internal point class, to make the wipe tower independent from other slic3r modules.
// This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r.
struct xy
struct Extrusion
{
xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
// Rotate the point around center of the wipe tower about given angle (in degrees)
xy rotate(float width, float depth, float angle) const {
xy out(0,0);
float temp_x = x - width / 2.f;
float temp_y = y - depth / 2.f;
angle *= float(M_PI/180.);
out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
return out;
}
// Rotate the point around origin about given angle in degrees
void rotate(float angle) {
float temp_x = x * cos(angle) - y * sin(angle);
y = x * sin(angle) + y * cos(angle);
x = temp_x;
}
void translate(const xy& vect) {
x += vect.x;
y += vect.y;
}
float x;
float y;
};
WipeTower() {}
virtual ~WipeTower() {}
// Return the wipe tower position.
virtual const xy& position() const = 0;
// Return the wipe tower width.
virtual float width() const = 0;
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
virtual bool finished() const = 0;
// Switch to a next layer.
virtual void set_layer(
// Print height of this layer.
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
// Maximum number of tool changes on this layer or the layers below.
size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
bool is_first_layer,
// Is this the last layer of the wipe tower?
bool is_last_layer) = 0;
enum Purpose {
PURPOSE_MOVE_TO_TOWER,
PURPOSE_EXTRUDE,
PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE,
};
// Extrusion path of the wipe tower, for 3D preview of the generated tool paths.
struct Extrusion
{
Extrusion(const xy &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {}
// End position of this extrusion.
xy pos;
Vec2f pos;
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
float width;
@ -108,10 +43,10 @@ public:
std::vector<Extrusion> extrusions;
// Initial position, at which the wipe tower starts its action.
// At this position the extruder is loaded and there is no Z-hop applied.
xy start_pos;
Vec2f start_pos;
// Last point, at which the normal G-code generator of Slic3r shall continue.
// At this position the extruder is loaded and there is no Z-hop applied.
xy end_pos;
Vec2f end_pos;
// Time elapsed over this tool change.
// This is useful not only for the print time estimation, but also for the control of layer cooling.
float elapsed_time;
@ -119,50 +54,375 @@ public:
// Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
bool priming;
// Initial tool
int initial_tool;
// New tool
int new_tool;
// Sum the total length of the extrusion.
float total_extrusion_length_in_plane() {
float e_length = 0.f;
for (size_t i = 1; i < this->extrusions.size(); ++ i) {
const Extrusion &e = this->extrusions[i];
if (e.width > 0) {
xy v = e.pos - (&e - 1)->pos;
e_length += sqrt(v.x*v.x+v.y*v.y);
Vec2f v = e.pos - (&e - 1)->pos;
e_length += v.norm();
}
}
return e_length;
}
};
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTower(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
m_semm(semm),
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
m_z_pos(0.f),
m_is_first_layer(false),
m_gcode_flavor(flavor),
m_bridging(bridging),
m_current_tool(initial_tool),
wipe_volumes(wiping_matrix)
{
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
if (m_semm) {
m_cooling_tube_retraction = cooling_tube_retraction;
m_cooling_tube_length = cooling_tube_length;
m_parking_pos_retraction = parking_pos_retraction;
m_extra_loading_move = extra_loading_move;
m_set_extruder_trimpot = set_extruder_trimpot;
}
}
virtual ~WipeTower() {}
// Set the extruder properties.
void set_extruder(size_t idx, std::string material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed, float nozzle_diameter)
{
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
m_filpar[idx].material = material;
m_filpar[idx].temperature = temp;
m_filpar[idx].first_layer_temperature = first_layer_temp;
// If this is a single extruder MM printer, we will use all the SE-specific config values.
// Otherwise, the defaults will be used to turn off the SE stuff.
if (m_semm) {
m_filpar[idx].loading_speed = loading_speed;
m_filpar[idx].loading_speed_start = loading_speed_start;
m_filpar[idx].unloading_speed = unloading_speed;
m_filpar[idx].unloading_speed_start = unloading_speed_start;
m_filpar[idx].delay = delay;
m_filpar[idx].cooling_moves = cooling_moves;
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
m_filpar[idx].cooling_final_speed = cooling_final_speed;
}
if (max_volumetric_speed != 0.f)
m_filpar[idx].max_e_speed = (max_volumetric_speed / Filament_Area);
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
std::stringstream stream{m_semm ? ramming_parameters : std::string()};
float speed = 0.f;
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
m_filpar[idx].ramming_line_width_multiplicator /= 100;
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
// Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z.
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<ToolChangeResult>> &result);
float get_depth() const { return m_wipe_tower_depth; }
// Switch to a next layer.
void set_layer(
// Print height of this layer.
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
// Maximum number of tool changes on this layer or the layers below.
size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
bool is_first_layer,
// Is this the last layer of the waste tower?
bool is_last_layer)
{
m_z_pos = print_z;
m_layer_height = layer_height;
m_is_first_layer = is_first_layer;
m_print_brim = is_first_layer;
m_depth_traversed = 0.f;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
if (is_first_layer) {
this->m_num_layer_changes = 0;
this->m_num_tool_changes = 0;
}
else
++ m_num_layer_changes;
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
m_extrusion_flow = extrusion_flow(layer_height);
// Advance m_layer_info iterator, making sure we got it right
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
++m_layer_info;
}
// Return the wipe tower position.
const Vec2f& position() const { return m_wipe_tower_pos; }
// Return the wipe tower width.
float width() const { return m_wipe_tower_width; }
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
bool finished() const { return m_max_color_changes == 0; }
// Returns gcode to prime the nozzles at the front edge of the print bed.
virtual ToolChangeResult prime(
std::vector<ToolChangeResult> prime(
// print_z of the first layer.
float first_layer_height,
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower) = 0;
bool last_wipe_inside_wipe_tower);
// Returns gcode for toolchange and the end position.
// if new_tool == -1, just unload the current filament over the wipe tower.
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0;
// Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first.
ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
virtual ToolChangeResult finish_layer() = 0;
ToolChangeResult finish_layer();
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
// the wipe tower has been completely covered by the tool change extrusions,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const = 0;
// Is the current layer finished?
bool layer_finished() const {
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
}
// Returns used filament length per extruder:
virtual std::vector<float> get_used_filament() const = 0;
std::vector<float> get_used_filament() const { return m_used_filament_length; }
int get_number_of_toolchanges() const { return m_num_tool_changes; }
// Returns total number of toolchanges:
virtual int get_number_of_toolchanges() const = 0;
struct FilamentParameters {
std::string material = "PLA";
int temperature = 0;
int first_layer_temperature = 0;
float loading_speed = 0.f;
float loading_speed_start = 0.f;
float unloading_speed = 0.f;
float unloading_speed_start = 0.f;
float delay = 0.f ;
int cooling_moves = 0;
float cooling_initial_speed = 0.f;
float cooling_final_speed = 0.f;
float ramming_line_width_multiplicator = 0.f;
float ramming_step_multiplicator = 0.f;
float max_e_speed = std::numeric_limits<float>::max();
std::vector<float> ramming_speed;
float nozzle_diameter;
};
private:
WipeTower();
enum wipe_shape // A fill-in direction
{
SHAPE_NORMAL = 1,
SHAPE_REVERSED = -1
};
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
const float WT_EPSILON = 1e-3f;
bool m_semm = true; // Are we using a single extruder multimaterial printer?
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
float m_internal_rotation = 0.f;
float m_y_shift = 0.f; // y shift passed to writer
float m_z_pos = 0.f; // Current Z position.
float m_layer_height = 0.f; // Current layer height.
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_set_extruder_trimpot = false;
bool m_adhesion = true;
GCodeFlavor m_gcode_flavor;
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
// Extruder specific parameters.
std::vector<FilamentParameters> m_filpar;
// State of the wipe tower generator.
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
bool m_print_brim = true;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
unsigned int m_current_tool = 0;
const std::vector<std::vector<float>> wipe_volumes;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
if ( layer_height < 0 )
return m_extrusion_flow;
return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
}
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates depth for all layers and propagates them downwards
void plan_tower();
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
struct box_coordinates
{
box_coordinates(float left, float bottom, float width, float height) :
ld(left , bottom ),
lu(left , bottom + height),
rd(left + width, bottom ),
ru(left + width, bottom + height) {}
box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
void translate(const Vec2f &shift) {
ld += shift; lu += shift;
rd += shift; ru += shift;
}
void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
void expand(const float offset) {
ld += Vec2f(- offset, - offset);
lu += Vec2f(- offset, offset);
rd += Vec2f( offset, - offset);
ru += Vec2f( offset, offset);
}
void expand(const float offset_x, const float offset_y) {
ld += Vec2f(- offset_x, - offset_y);
lu += Vec2f(- offset_x, offset_y);
rd += Vec2f( offset_x, - offset_y);
ru += Vec2f( offset_x, offset_y);
}
Vec2f ld; // left down
Vec2f lu; // left upper
Vec2f rd; // right lower
Vec2f ru; // right upper
};
// to store information about tool changes for a given layer
struct WipeTowerInfo{
struct ToolChange {
unsigned int old_tool;
unsigned int new_tool;
float required_depth;
float ramming_depth;
float first_wipe_line;
float wipe_volume;
ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
};
float z; // z position of the layer
float height; // layer height
float depth; // depth of the layer based on all layers above
float extra_spacing;
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
std::vector<ToolChange> tool_changes;
WipeTowerInfo(float z_par, float layer_height_par)
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
};
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
// Stores information about used filament length per extruder:
std::vector<float> m_used_filament_length;
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
const std::string& current_material,
const int new_temperature);
void toolchange_Change(
WipeTowerWriter &writer,
const unsigned int new_tool,
const std::string& new_material);
void toolchange_Load(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box);
void toolchange_Wipe(
WipeTowerWriter &writer,
const box_coordinates &cleaning_box,
float wipe_volume);
};
}; // namespace Slic3r
#endif /* slic3r_WipeTower_hpp_ */
#endif // WipeTowerPrusaMM_hpp_

View File

@ -1,388 +0,0 @@
#ifndef WipeTowerPrusaMM_hpp_
#define WipeTowerPrusaMM_hpp_
#include <cmath>
#include <string>
#include <sstream>
#include <utility>
#include <algorithm>
#include "WipeTower.hpp"
#include "PrintConfig.hpp"
namespace Slic3r
{
namespace PrusaMultiMaterial {
class Writer;
};
class WipeTowerPrusaMM : public WipeTower
{
public:
enum material_type
{
INVALID = -1,
PLA = 0, // E:210C B:55C
ABS = 1, // E:255C B:100C
PET = 2, // E:240C B:90C
HIPS = 3, // E:220C B:100C
FLEX = 4, // E:245C B:80C
SCAFF = 5, // E:215C B:55C
EDGE = 6, // E:240C B:80C
NGEN = 7, // E:230C B:80C
PVA = 8, // E:210C B:80C
PC = 9
};
// Parse material name into material_type.
static material_type parse_material(const char *name);
static std::string to_string(material_type material);
// x -- x coordinates of wipe tower in mm ( left bottom corner )
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move,
float bridging, bool set_extruder_trimpot, GCodeFlavor flavor,
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
m_z_pos(0.f),
m_is_first_layer(false),
m_cooling_tube_retraction(cooling_tube_retraction),
m_cooling_tube_length(cooling_tube_length),
m_parking_pos_retraction(parking_pos_retraction),
m_extra_loading_move(extra_loading_move),
m_bridging(bridging),
m_set_extruder_trimpot(set_extruder_trimpot),
m_gcode_flavor(flavor),
m_current_tool(initial_tool),
wipe_volumes(wiping_matrix)
{}
virtual ~WipeTowerPrusaMM() {}
// Set the extruder properties.
void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
{
//while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
m_filpar.push_back(FilamentParameters());
m_filpar[idx].material = material;
if (material == FLEX || material == SCAFF || material == PVA) {
// MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials.
// Therefore it does not make sense to use the new M220 B and M220 R (backup / restore).
m_retain_speed_override = false;
}
m_filpar[idx].temperature = temp;
m_filpar[idx].first_layer_temperature = first_layer_temp;
m_filpar[idx].loading_speed = loading_speed;
m_filpar[idx].loading_speed_start = loading_speed_start;
m_filpar[idx].unloading_speed = unloading_speed;
m_filpar[idx].unloading_speed_start = unloading_speed_start;
m_filpar[idx].delay = delay;
m_filpar[idx].cooling_moves = cooling_moves;
m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
m_filpar[idx].cooling_final_speed = cooling_final_speed;
m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
std::stringstream stream{ramming_parameters};
float speed = 0.f;
stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
m_filpar[idx].ramming_line_width_multiplicator /= 100;
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
// Appends into internal structure m_plan containing info about the future wipe tower
// to be used before building begins. The entries must be added ordered in z.
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
float get_depth() const { return m_wipe_tower_depth; }
// Switch to a next layer.
virtual void set_layer(
// Print height of this layer.
float print_z,
// Layer height, used to calculate extrusion the rate.
float layer_height,
// Maximum number of tool changes on this layer or the layers below.
size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
bool is_first_layer,
// Is this the last layer of the waste tower?
bool is_last_layer)
{
m_z_pos = print_z;
m_layer_height = layer_height;
m_is_first_layer = is_first_layer;
m_print_brim = is_first_layer;
m_depth_traversed = 0.f;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
if (is_first_layer) {
this->m_num_layer_changes = 0;
this->m_num_tool_changes = 0;
}
else
++ m_num_layer_changes;
// Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
m_extrusion_flow = extrusion_flow(layer_height);
// Advance m_layer_info iterator, making sure we got it right
while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
++m_layer_info;
}
// Return the wipe tower position.
virtual const xy& position() const { return m_wipe_tower_pos; }
// Return the wipe tower width.
virtual float width() const { return m_wipe_tower_width; }
// The wipe tower is finished, there should be no more tool changes or wipe tower prints.
virtual bool finished() const { return m_max_color_changes == 0; }
// Returns gcode to prime the nozzles at the front edge of the print bed.
virtual ToolChangeResult prime(
// print_z of the first layer.
float first_layer_height,
// Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object.
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
bool last_wipe_inside_wipe_tower);
// Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first.
virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
virtual ToolChangeResult finish_layer();
// Is the current layer finished?
virtual bool layer_finished() const {
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
}
virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
private:
WipeTowerPrusaMM();
enum wipe_shape // A fill-in direction
{
SHAPE_NORMAL = 1,
SHAPE_REVERSED = -1
};
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
const float Filament_Area = float(M_PI * 1.75f * 1.75f / 4.f); // filament area in mm^2
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
const float WT_EPSILON = 1e-3f;
xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
float m_wipe_tower_width; // Width of the wipe tower.
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
float m_internal_rotation = 0.f;
float m_y_shift = 0.f; // y shift passed to writer
float m_z_pos = 0.f; // Current Z position.
float m_layer_height = 0.f; // Current layer height.
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
// G-code generator parameters.
float m_cooling_tube_retraction = 0.f;
float m_cooling_tube_length = 0.f;
float m_parking_pos_retraction = 0.f;
float m_extra_loading_move = 0.f;
float m_bridging = 0.f;
bool m_set_extruder_trimpot = false;
bool m_retain_speed_override = true;
bool m_adhesion = true;
GCodeFlavor m_gcode_flavor;
float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
struct FilamentParameters {
material_type material = PLA;
int temperature = 0;
int first_layer_temperature = 0;
float loading_speed = 0.f;
float loading_speed_start = 0.f;
float unloading_speed = 0.f;
float unloading_speed_start = 0.f;
float delay = 0.f ;
int cooling_moves = 0;
float cooling_initial_speed = 0.f;
float cooling_final_speed = 0.f;
float ramming_line_width_multiplicator = 0.f;
float ramming_step_multiplicator = 0.f;
std::vector<float> ramming_speed;
float nozzle_diameter;
};
// Extruder specific parameters.
std::vector<FilamentParameters> m_filpar;
// State of the wipe tower generator.
unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
bool m_print_brim = true;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
unsigned int m_current_tool = 0;
const std::vector<std::vector<float>> wipe_volumes;
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
bool m_left_to_right = true;
float m_extra_spacing = 1.f;
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
if ( layer_height < 0 )
return m_extrusion_flow;
return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / Filament_Area;
}
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));
}
// Calculates depth for all layers and propagates them downwards
void plan_tower();
// Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
void make_wipe_tower_square();
// Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
void save_on_last_wipe();
struct box_coordinates
{
box_coordinates(float left, float bottom, float width, float height) :
ld(left , bottom ),
lu(left , bottom + height),
rd(left + width, bottom ),
ru(left + width, bottom + height) {}
box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {}
void translate(const xy &shift) {
ld += shift; lu += shift;
rd += shift; ru += shift;
}
void translate(const float dx, const float dy) { translate(xy(dx, dy)); }
void expand(const float offset) {
ld += xy(- offset, - offset);
lu += xy(- offset, offset);
rd += xy( offset, - offset);
ru += xy( offset, offset);
}
void expand(const float offset_x, const float offset_y) {
ld += xy(- offset_x, - offset_y);
lu += xy(- offset_x, offset_y);
rd += xy( offset_x, - offset_y);
ru += xy( offset_x, offset_y);
}
xy ld; // left down
xy lu; // left upper
xy rd; // right lower
xy ru; // right upper
};
// to store information about tool changes for a given layer
struct WipeTowerInfo{
struct ToolChange {
unsigned int old_tool;
unsigned int new_tool;
float required_depth;
float ramming_depth;
float first_wipe_line;
float wipe_volume;
ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
: old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
};
float z; // z position of the layer
float height; // layer height
float depth; // depth of the layer based on all layers above
float extra_spacing;
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
std::vector<ToolChange> tool_changes;
WipeTowerInfo(float z_par, float layer_height_par)
: z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
};
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
// Stores information about used filament length per extruder:
std::vector<float> m_used_filament_length;
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
const material_type current_material,
const int new_temperature);
void toolchange_Change(
PrusaMultiMaterial::Writer &writer,
const unsigned int new_tool,
material_type new_material);
void toolchange_Load(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box);
void toolchange_Wipe(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
float wipe_volume);
};
}; // namespace Slic3r
#endif /* WipeTowerPrusaMM_hpp_ */

View File

@ -174,7 +174,7 @@ namespace Slic3r {
const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; SILENT_LAST_M73_OUTPUT_PLACEHOLDER";
GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
: _mode(mode)
: m_mode(mode)
{
reset();
set_default();
@ -183,7 +183,7 @@ namespace Slic3r {
void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
{
PROFILE_FUNC();
_parser.parse_line(gcode_line,
m_parser.parse_line(gcode_line,
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); });
}
@ -196,7 +196,7 @@ namespace Slic3r {
{ this->_process_gcode_line(reader, line); };
for (; *ptr != 0;) {
gline.reset();
ptr = _parser.parse_line(ptr, gline, action);
ptr = m_parser.parse_line(ptr, gline, action);
}
}
@ -206,10 +206,13 @@ namespace Slic3r {
if (start_from_beginning)
{
_reset_time();
_last_st_synchronized_block_id = -1;
m_last_st_synchronized_block_id = -1;
}
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -219,12 +222,15 @@ namespace Slic3r {
{
reset();
_parser.parse_buffer(gcode,
m_parser.parse_buffer(gcode,
[this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); });
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -234,9 +240,12 @@ namespace Slic3r {
{
reset();
_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
m_parser.parse_file(file, boost::bind(&GCodeTimeEstimator::_process_gcode_line, this, _1, _2));
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -249,9 +258,12 @@ namespace Slic3r {
auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
{ this->_process_gcode_line(reader, line); };
for (const std::string& line : gcode_lines)
_parser.parse_line(line, action);
m_parser.parse_line(line, action);
_calculate_time();
if (m_needs_color_times && (m_color_time_cache != 0.0f))
m_color_times.push_back(m_color_time_cache);
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
@ -270,7 +282,7 @@ namespace Slic3r {
throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n"));
std::string time_mask;
switch (_mode)
switch (m_mode)
{
default:
case Normal:
@ -291,7 +303,7 @@ namespace Slic3r {
// buffer line to export only when greater than 64K to reduce writing calls
std::string export_line;
char time_line[64];
G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin();
G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin();
while (std::getline(in, gcode_line))
{
if (!in.good())
@ -301,15 +313,15 @@ namespace Slic3r {
}
// replaces placeholders for initial line M73 with the real lines
if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
{
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str());
gcode_line = time_line;
}
// replaces placeholders for final line M73 with the real lines
else if (((_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
((_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) ||
((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)))
{
sprintf(time_line, time_mask.c_str(), "100", "0");
gcode_line = time_line;
@ -319,27 +331,27 @@ namespace Slic3r {
// add remaining time lines where needed
_parser.parse_line(gcode_line,
m_parser.parse_line(gcode_line,
[this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
{
if (line.cmd_is("G1"))
{
++g1_lines_count;
assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count);
assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count);
const Block *block = nullptr;
if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) {
if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size())
block = &_blocks[it_line_id->second];
if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) {
if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size())
block = &m_blocks[it_line_id->second];
++it_line_id;
}
if (block != nullptr && block->elapsed_time != -1.0f) {
float block_remaining_time = _time - block->elapsed_time;
float block_remaining_time = m_time - block->elapsed_time;
if (std::abs(last_recorded_time - block_remaining_time) > interval)
{
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
gcode_line += time_line;
last_recorded_time = block_remaining_time;
@ -387,240 +399,240 @@ namespace Slic3r {
void GCodeTimeEstimator::set_axis_position(EAxis axis, float position)
{
_state.axis[axis].position = position;
m_state.axis[axis].position = position;
}
void GCodeTimeEstimator::set_axis_max_feedrate(EAxis axis, float feedrate_mm_sec)
{
_state.axis[axis].max_feedrate = feedrate_mm_sec;
m_state.axis[axis].max_feedrate = feedrate_mm_sec;
}
void GCodeTimeEstimator::set_axis_max_acceleration(EAxis axis, float acceleration)
{
_state.axis[axis].max_acceleration = acceleration;
m_state.axis[axis].max_acceleration = acceleration;
}
void GCodeTimeEstimator::set_axis_max_jerk(EAxis axis, float jerk)
{
_state.axis[axis].max_jerk = jerk;
m_state.axis[axis].max_jerk = jerk;
}
float GCodeTimeEstimator::get_axis_position(EAxis axis) const
{
return _state.axis[axis].position;
return m_state.axis[axis].position;
}
float GCodeTimeEstimator::get_axis_max_feedrate(EAxis axis) const
{
return _state.axis[axis].max_feedrate;
return m_state.axis[axis].max_feedrate;
}
float GCodeTimeEstimator::get_axis_max_acceleration(EAxis axis) const
{
return _state.axis[axis].max_acceleration;
return m_state.axis[axis].max_acceleration;
}
float GCodeTimeEstimator::get_axis_max_jerk(EAxis axis) const
{
return _state.axis[axis].max_jerk;
return m_state.axis[axis].max_jerk;
}
void GCodeTimeEstimator::set_feedrate(float feedrate_mm_sec)
{
_state.feedrate = feedrate_mm_sec;
m_state.feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_feedrate() const
{
return _state.feedrate;
return m_state.feedrate;
}
void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2)
{
_state.acceleration = (_state.max_acceleration == 0) ?
m_state.acceleration = (m_state.max_acceleration == 0) ?
acceleration_mm_sec2 :
// Clamp the acceleration with the maximum.
std::min(_state.max_acceleration, acceleration_mm_sec2);
std::min(m_state.max_acceleration, acceleration_mm_sec2);
}
float GCodeTimeEstimator::get_acceleration() const
{
return _state.acceleration;
return m_state.acceleration;
}
void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2)
{
_state.max_acceleration = acceleration_mm_sec2;
m_state.max_acceleration = acceleration_mm_sec2;
if (acceleration_mm_sec2 > 0)
_state.acceleration = acceleration_mm_sec2;
m_state.acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_max_acceleration() const
{
return _state.max_acceleration;
return m_state.max_acceleration;
}
void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2)
{
_state.retract_acceleration = acceleration_mm_sec2;
m_state.retract_acceleration = acceleration_mm_sec2;
}
float GCodeTimeEstimator::get_retract_acceleration() const
{
return _state.retract_acceleration;
return m_state.retract_acceleration;
}
void GCodeTimeEstimator::set_minimum_feedrate(float feedrate_mm_sec)
{
_state.minimum_feedrate = feedrate_mm_sec;
m_state.minimum_feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_minimum_feedrate() const
{
return _state.minimum_feedrate;
return m_state.minimum_feedrate;
}
void GCodeTimeEstimator::set_minimum_travel_feedrate(float feedrate_mm_sec)
{
_state.minimum_travel_feedrate = feedrate_mm_sec;
m_state.minimum_travel_feedrate = feedrate_mm_sec;
}
float GCodeTimeEstimator::get_minimum_travel_feedrate() const
{
return _state.minimum_travel_feedrate;
return m_state.minimum_travel_feedrate;
}
void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
{
_state.filament_load_times.clear();
m_state.filament_load_times.clear();
for (double t : filament_load_times)
_state.filament_load_times.push_back((float)t);
m_state.filament_load_times.push_back((float)t);
}
void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
{
_state.filament_unload_times.clear();
m_state.filament_unload_times.clear();
for (double t : filament_unload_times)
_state.filament_unload_times.push_back((float)t);
m_state.filament_unload_times.push_back((float)t);
}
float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
{
return
(_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
(m_state.filament_load_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?
0 :
(_state.filament_load_times.size() <= id_extruder) ?
_state.filament_load_times.front() :
_state.filament_load_times[id_extruder];
(m_state.filament_load_times.size() <= id_extruder) ?
m_state.filament_load_times.front() :
m_state.filament_load_times[id_extruder];
}
float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
{
return
(_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
(m_state.filament_unload_times.empty() || id_extruder == m_state.extruder_id_unloaded) ?
0 :
(_state.filament_unload_times.size() <= id_extruder) ?
_state.filament_unload_times.front() :
_state.filament_unload_times[id_extruder];
(m_state.filament_unload_times.size() <= id_extruder) ?
m_state.filament_unload_times.front() :
m_state.filament_unload_times[id_extruder];
}
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
{
_state.extrude_factor_override_percentage = percentage;
m_state.extrude_factor_override_percentage = percentage;
}
float GCodeTimeEstimator::get_extrude_factor_override_percentage() const
{
return _state.extrude_factor_override_percentage;
return m_state.extrude_factor_override_percentage;
}
void GCodeTimeEstimator::set_dialect(GCodeFlavor dialect)
{
_state.dialect = dialect;
m_state.dialect = dialect;
}
GCodeFlavor GCodeTimeEstimator::get_dialect() const
{
PROFILE_FUNC();
return _state.dialect;
return m_state.dialect;
}
void GCodeTimeEstimator::set_units(GCodeTimeEstimator::EUnits units)
{
_state.units = units;
m_state.units = units;
}
GCodeTimeEstimator::EUnits GCodeTimeEstimator::get_units() const
{
return _state.units;
return m_state.units;
}
void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type)
{
_state.global_positioning_type = type;
m_state.global_positioning_type = type;
}
GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const
{
return _state.global_positioning_type;
return m_state.global_positioning_type;
}
void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type)
{
_state.e_local_positioning_type = type;
m_state.e_local_positioning_type = type;
}
GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const
{
return _state.e_local_positioning_type;
return m_state.e_local_positioning_type;
}
int GCodeTimeEstimator::get_g1_line_id() const
{
return _state.g1_line_id;
return m_state.g1_line_id;
}
void GCodeTimeEstimator::increment_g1_line_id()
{
++_state.g1_line_id;
++m_state.g1_line_id;
}
void GCodeTimeEstimator::reset_g1_line_id()
{
_state.g1_line_id = 0;
m_state.g1_line_id = 0;
}
void GCodeTimeEstimator::set_extruder_id(unsigned int id)
{
_state.extruder_id = id;
m_state.extruder_id = id;
}
unsigned int GCodeTimeEstimator::get_extruder_id() const
{
return _state.extruder_id;
return m_state.extruder_id;
}
void GCodeTimeEstimator::reset_extruder_id()
{
// Set the initial extruder ID to unknown. For the multi-material setup it means
// that all the filaments are parked in the MMU and no filament is loaded yet.
_state.extruder_id = _state.extruder_id_unloaded;
m_state.extruder_id = m_state.extruder_id_unloaded;
}
void GCodeTimeEstimator::add_additional_time(float timeSec)
{
PROFILE_FUNC();
_state.additional_time += timeSec;
m_state.additional_time += timeSec;
}
void GCodeTimeEstimator::set_additional_time(float timeSec)
{
_state.additional_time = timeSec;
m_state.additional_time = timeSec;
}
float GCodeTimeEstimator::get_additional_time() const
{
return _state.additional_time;
return m_state.additional_time;
}
void GCodeTimeEstimator::set_default()
@ -648,8 +660,8 @@ namespace Slic3r {
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
}
_state.filament_load_times.clear();
_state.filament_unload_times.clear();
m_state.filament_load_times.clear();
m_state.filament_unload_times.clear();
}
void GCodeTimeEstimator::reset()
@ -664,7 +676,7 @@ namespace Slic3r {
float GCodeTimeEstimator::get_time() const
{
return _time;
return m_time;
}
std::string GCodeTimeEstimator::get_time_dhms() const
@ -677,19 +689,44 @@ namespace Slic3r {
return _get_time_minutes(get_time());
}
std::vector<float> GCodeTimeEstimator::get_color_times() const
{
return m_color_times;
}
std::vector<std::string> GCodeTimeEstimator::get_color_times_dhms() const
{
std::vector<std::string> ret;
for (float t : m_color_times)
{
ret.push_back(_get_time_dhms(t));
}
return ret;
}
std::vector<std::string> GCodeTimeEstimator::get_color_times_minutes() const
{
std::vector<std::string> ret;
for (float t : m_color_times)
{
ret.push_back(_get_time_minutes(t));
}
return ret;
}
// Return an estimate of the memory consumed by the time estimator.
size_t GCodeTimeEstimator::memory_used() const
{
size_t out = sizeof(*this);
out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block);
out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId);
out += SLIC3R_STDVEC_MEMSIZE(this->m_blocks, Block);
out += SLIC3R_STDVEC_MEMSIZE(this->m_g1_line_ids, G1LineIdToBlockId);
return out;
}
void GCodeTimeEstimator::_reset()
{
_curr.reset();
_prev.reset();
m_curr.reset();
m_prev.reset();
set_axis_position(X, 0.0f);
set_axis_position(Y, 0.0f);
@ -701,19 +738,23 @@ namespace Slic3r {
reset_extruder_id();
reset_g1_line_id();
_g1_line_ids.clear();
m_g1_line_ids.clear();
_last_st_synchronized_block_id = -1;
m_last_st_synchronized_block_id = -1;
m_needs_color_times = false;
m_color_times.clear();
m_color_time_cache = 0.0f;
}
void GCodeTimeEstimator::_reset_time()
{
_time = 0.0f;
m_time = 0.0f;
}
void GCodeTimeEstimator::_reset_blocks()
{
_blocks.clear();
m_blocks.clear();
}
void GCodeTimeEstimator::_calculate_time()
@ -723,35 +764,32 @@ namespace Slic3r {
_reverse_pass();
_recalculate_trapezoids();
_time += get_additional_time();
m_time += get_additional_time();
m_color_time_cache += get_additional_time();
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i)
{
Block& block = _blocks[i];
#if ENABLE_MOVE_STATS
Block& block = m_blocks[i];
float block_time = 0.0f;
block_time += block.acceleration_time();
block_time += block.cruise_time();
block_time += block.deceleration_time();
_time += block_time;
block.elapsed_time = _time;
m_time += block_time;
block.elapsed_time = m_time;
#if ENABLE_MOVE_STATS
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
if (it == _moves_stats.end())
it = _moves_stats.insert(MovesStatsMap::value_type(block.move_type, MoveStats())).first;
it->second.count += 1;
it->second.time += block_time;
#else
_time += block.acceleration_time();
_time += block.cruise_time();
_time += block.deceleration_time();
block.elapsed_time = _time;
#endif // ENABLE_MOVE_STATS
m_color_time_cache += block_time;
}
_last_st_synchronized_block_id = (int)_blocks.size() - 1;
m_last_st_synchronized_block_id = (int)m_blocks.size() - 1;
// The additional time has been consumed (added to the total time), reset it to zero.
set_additional_time(0.);
}
@ -866,6 +904,11 @@ namespace Slic3r {
_processM566(line);
break;
}
case 600: // Set color change
{
_processM600(line);
break;
}
case 702: // MK3 MMU2: Process the final filament unload.
{
_processM702(line);
@ -934,7 +977,7 @@ namespace Slic3r {
return;
// calculates block feedrate
_curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate());
m_curr.feedrate = std::max(get_feedrate(), block.is_travel_move() ? get_minimum_travel_feedrate() : get_minimum_feedrate());
float distance = block.move_length();
float invDistance = 1.0f / distance;
@ -942,23 +985,23 @@ namespace Slic3r {
float min_feedrate_factor = 1.0f;
for (unsigned char a = X; a < Num_Axis; ++a)
{
_curr.axis_feedrate[a] = _curr.feedrate * block.delta_pos[a] * invDistance;
m_curr.axis_feedrate[a] = m_curr.feedrate * block.delta_pos[a] * invDistance;
if (a == E)
_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage();
m_curr.axis_feedrate[a] *= get_extrude_factor_override_percentage();
_curr.abs_axis_feedrate[a] = std::abs(_curr.axis_feedrate[a]);
if (_curr.abs_axis_feedrate[a] > 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]);
m_curr.abs_axis_feedrate[a] = std::abs(m_curr.axis_feedrate[a]);
if (m_curr.abs_axis_feedrate[a] > 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / m_curr.abs_axis_feedrate[a]);
}
block.feedrate.cruise = min_feedrate_factor * _curr.feedrate;
block.feedrate.cruise = min_feedrate_factor * m_curr.feedrate;
if (min_feedrate_factor < 1.0f)
{
for (unsigned char a = X; a < Num_Axis; ++a)
{
_curr.axis_feedrate[a] *= min_feedrate_factor;
_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
m_curr.axis_feedrate[a] *= min_feedrate_factor;
m_curr.abs_axis_feedrate[a] *= min_feedrate_factor;
}
}
@ -975,25 +1018,25 @@ namespace Slic3r {
block.acceleration = acceleration;
// calculates block exit feedrate
_curr.safe_feedrate = block.feedrate.cruise;
m_curr.safe_feedrate = block.feedrate.cruise;
for (unsigned char a = X; a < Num_Axis; ++a)
{
float axis_max_jerk = get_axis_max_jerk((EAxis)a);
if (_curr.abs_axis_feedrate[a] > axis_max_jerk)
_curr.safe_feedrate = std::min(_curr.safe_feedrate, axis_max_jerk);
if (m_curr.abs_axis_feedrate[a] > axis_max_jerk)
m_curr.safe_feedrate = std::min(m_curr.safe_feedrate, axis_max_jerk);
}
block.feedrate.exit = _curr.safe_feedrate;
block.feedrate.exit = m_curr.safe_feedrate;
// calculates block entry feedrate
float vmax_junction = _curr.safe_feedrate;
if (!_blocks.empty() && (_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD))
float vmax_junction = m_curr.safe_feedrate;
if (!m_blocks.empty() && (m_prev.feedrate > PREVIOUS_FEEDRATE_THRESHOLD))
{
bool prev_speed_larger = _prev.feedrate > block.feedrate.cruise;
float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / _prev.feedrate) : (_prev.feedrate / block.feedrate.cruise);
bool prev_speed_larger = m_prev.feedrate > block.feedrate.cruise;
float smaller_speed_factor = prev_speed_larger ? (block.feedrate.cruise / m_prev.feedrate) : (m_prev.feedrate / block.feedrate.cruise);
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
vmax_junction = prev_speed_larger ? block.feedrate.cruise : _prev.feedrate;
vmax_junction = prev_speed_larger ? block.feedrate.cruise : m_prev.feedrate;
float v_factor = 1.0f;
bool limited = false;
@ -1001,8 +1044,8 @@ namespace Slic3r {
for (unsigned char a = X; a < Num_Axis; ++a)
{
// Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
float v_exit = _prev.axis_feedrate[a];
float v_entry = _curr.axis_feedrate[a];
float v_exit = m_prev.axis_feedrate[a];
float v_entry = m_curr.axis_feedrate[a];
if (prev_speed_larger)
v_exit *= smaller_speed_factor;
@ -1044,23 +1087,23 @@ namespace Slic3r {
float vmax_junction_threshold = vmax_junction * 0.99f;
// Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
if ((_prev.safe_feedrate > vmax_junction_threshold) && (_curr.safe_feedrate > vmax_junction_threshold))
vmax_junction = _curr.safe_feedrate;
if ((m_prev.safe_feedrate > vmax_junction_threshold) && (m_curr.safe_feedrate > vmax_junction_threshold))
vmax_junction = m_curr.safe_feedrate;
}
float v_allowable = Block::max_allowable_speed(-acceleration, _curr.safe_feedrate, distance);
float v_allowable = Block::max_allowable_speed(-acceleration, m_curr.safe_feedrate, distance);
block.feedrate.entry = std::min(vmax_junction, v_allowable);
block.max_entry_speed = vmax_junction;
block.flags.nominal_length = (block.feedrate.cruise <= v_allowable);
block.flags.recalculate = true;
block.safe_feedrate = _curr.safe_feedrate;
block.safe_feedrate = m_curr.safe_feedrate;
// calculates block trapezoid
block.calculate_trapezoid();
// updates previous
_prev = _curr;
m_prev = m_curr;
// updates axis positions
for (unsigned char a = X; a < Num_Axis; ++a)
@ -1091,8 +1134,8 @@ namespace Slic3r {
#endif // ENABLE_MOVE_STATS
// adds block to blocks list
_blocks.emplace_back(block);
_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
m_blocks.emplace_back(block);
m_g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)m_blocks.size() - 1));
}
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
@ -1336,6 +1379,18 @@ namespace Slic3r {
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
}
void GCodeTimeEstimator::_processM600(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
m_needs_color_times = true;
_calculate_time();
if (m_color_time_cache != 0.0f)
{
m_color_times.push_back(m_color_time_cache);
m_color_time_cache = 0.0f;
}
}
void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
{
PROFILE_FUNC();
@ -1376,11 +1431,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_forward_pass()
{
PROFILE_FUNC();
if (_blocks.size() > 1)
if (m_blocks.size() > 1)
{
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size() - 1; ++i)
{
_planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
_planner_forward_pass_kernel(m_blocks[i], m_blocks[i + 1]);
}
}
}
@ -1388,11 +1443,11 @@ namespace Slic3r {
void GCodeTimeEstimator::_reverse_pass()
{
PROFILE_FUNC();
if (_blocks.size() > 1)
if (m_blocks.size() > 1)
{
for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
for (int i = (int)m_blocks.size() - 1; i >= m_last_st_synchronized_block_id + 2; --i)
{
_planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
_planner_reverse_pass_kernel(m_blocks[i - 1], m_blocks[i]);
}
}
}
@ -1444,9 +1499,9 @@ namespace Slic3r {
Block* curr = nullptr;
Block* next = nullptr;
for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
for (int i = m_last_st_synchronized_block_id + 1; i < (int)m_blocks.size(); ++i)
{
Block& b = _blocks[i];
Block& b = m_blocks[i];
curr = next;
next = &b;
@ -1517,7 +1572,7 @@ namespace Slic3r {
{
std::cout << MOVE_TYPE_STR[move.first];
std::cout << ": count " << move.second.count << " (" << 100.0f * (float)move.second.count / moves_count << "%)";
std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / _time << "%)";
std::cout << " - time: " << move.second.time << "s (" << 100.0f * move.second.time / m_time << "%)";
std::cout << std::endl;
}
std::cout << std::endl;

View File

@ -215,17 +215,22 @@ namespace Slic3r {
typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap;
private:
EMode _mode;
GCodeReader _parser;
State _state;
Feedrates _curr;
Feedrates _prev;
BlocksList _blocks;
EMode m_mode;
GCodeReader m_parser;
State m_state;
Feedrates m_curr;
Feedrates m_prev;
BlocksList m_blocks;
// Map between g1 line id and blocks id, used to speed up export of remaining times
G1LineIdToBlockIdMap _g1_line_ids;
G1LineIdToBlockIdMap m_g1_line_ids;
// Index of the last block already st_synchronized
int _last_st_synchronized_block_id;
float _time; // s
int m_last_st_synchronized_block_id;
float m_time; // s
// data to calculate color print times
bool m_needs_color_times;
std::vector<float> m_color_times;
float m_color_time_cache;
#if ENABLE_MOVE_STATS
MovesStatsMap _moves_stats;
@ -341,6 +346,15 @@ namespace Slic3r {
// Returns the estimated time, in minutes (integer)
std::string get_time_minutes() const;
// Returns the estimated time, in seconds, for each color
std::vector<float> get_color_times() const;
// Returns the estimated time, in format DDd HHh MMm SSs, for each color
std::vector<std::string> get_color_times_dhms() const;
// Returns the estimated time, in minutes (integer), for each color
std::vector<std::string> get_color_times_minutes() const;
// Return an estimate of the memory consumed by the time estimator.
size_t memory_used() const;
@ -409,6 +423,9 @@ namespace Slic3r {
// Set allowable instantaneous speed change
void _processM566(const GCodeReader::GCodeLine& line);
// Set color change
void _processM600(const GCodeReader::GCodeLine& line);
// Unload the current filament into the MK3 MMU2 unit at the end of print.
void _processM702(const GCodeReader::GCodeLine& line);

View File

@ -912,7 +912,7 @@ MedialAxis::build(ThickPolylines* polylines)
}
*/
typedef const VD::vertex_type vert_t;
//typedef const VD::vertex_type vert_t;
typedef const VD::edge_type edge_t;
// collect valid edges (i.e. prune those not belonging to MAT)

View File

@ -7,6 +7,9 @@
#include "Polygon.hpp"
#include "Polyline.hpp"
// Serialization through the Cereal library
#include <cereal/access.hpp>
#include "boost/polygon/voronoi.hpp"
using boost::polygon::voronoi_builder;
using boost::polygon::voronoi_diagram;
@ -263,6 +266,17 @@ public:
// as possible in least squares norm in regard to the 8 corners of bbox.
// Bounding box is expected to be centered around zero in all axes.
static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox);
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(m_offset, m_rotation, m_scaling_factor, m_mirror); }
explicit Transformation(int) : m_dirty(true) {}
template <class Archive> static void load_and_construct(Archive &ar, cereal::construct<Transformation> &construct)
{
// Calling a private constructor with special "int" parameter to indicate that no construction is necessary.
construct(1);
ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, construct.ptr()->m_mirror);
}
};
// Rotation when going from the first coordinate system with rotation rot_xyz_from applied

View File

@ -128,7 +128,7 @@ void Layer::make_perimeters()
&& config.external_perimeter_speed == other_config.external_perimeter_speed
&& config.gap_fill_speed == other_config.gap_fill_speed
&& config.overhangs == other_config.overhangs
&& config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0
&& config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width")
&& config.thin_walls == other_config.thin_walls
&& config.external_perimeters_first == other_config.external_perimeters_first) {
layerms.push_back(other_layerm);

View File

@ -201,7 +201,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
size_t n_groups = 0;
for (size_t i = 0; i < bridges.size(); ++ i) {
// A grup id for this bridge.
size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i];
size_t group_id = (bridge_group[i] == size_t(-1)) ? (n_groups ++) : bridge_group[i];
bridge_group[i] = group_id;
// For all possibly overlaping bridges:
for (size_t j = i + 1; j < bridges.size(); ++ j) {
@ -210,7 +210,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (intersection(bridges_grown[i], bridges_grown[j], false).empty())
continue;
// The two bridge regions intersect. Give them the same group id.
if (bridge_group[j] != -1) {
if (bridge_group[j] != size_t(-1)) {
// The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group[j];
for (size_t k = 0; k < j; ++ k)

View File

@ -22,7 +22,6 @@ Linef3 transform(const Linef3& line, const Transform3d& t)
bool Line::intersection_infinite(const Line &other, Point* point) const
{
Vec2d a1 = this->a.cast<double>();
Vec2d a2 = other.a.cast<double>();
Vec2d v12 = (other.a - this->a).cast<double>();
Vec2d v1 = (this->b - this->a).cast<double>();
Vec2d v2 = (other.b - other.a).cast<double>();

View File

@ -1,11 +1,13 @@
#ifndef MTUTILS_HPP
#define MTUTILS_HPP
#include <atomic> // for std::atomic_flag and memory orders
#include <mutex> // for std::lock_guard
#include <functional> // for std::function
#include <utility> // for std::forward
#include <atomic> // for std::atomic_flag and memory orders
#include <mutex> // for std::lock_guard
#include <functional> // for std::function
#include <utility> // for std::forward
#include <vector>
#include <algorithm>
#include <cmath>
#include "libslic3r.h"
#include "Point.hpp"
@ -242,6 +244,58 @@ template<class C> bool all_of(const C &container)
});
}
template<class T> struct remove_cvref
{
using type =
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};
template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
template<template<class> class C, class T>
class Container : public C<remove_cvref_t<T>>
{
public:
explicit Container(size_t count, T &&initval)
: C<remove_cvref_t<T>>(count, initval)
{}
};
template<class T> using DefaultContainer = std::vector<T>;
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> linspace(const T &start, const T &stop, const I &n)
{
Container<C, T> vals(n, T());
T stride = (stop - start) / n;
size_t i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
/// A set of equidistant values starting from 'start' (inclusive), ending
/// in the closest multiple of 'stride' less than or equal to 'end' and
/// leaving 'stride' space between each value.
/// Very similar to Matlab [start:stride:end] notation.
template<class T, template<class> class C = DefaultContainer>
inline C<remove_cvref_t<T>> grid(const T &start, const T &stop, const T &stride)
{
Container<C, T> vals(size_t(std::ceil((stop - start) / stride)), T());
int i = 0;
std::generate(vals.begin(), vals.end(), [&i, start, stride] {
return start + i++ * stride;
});
return vals;
}
// A shorter C++14 style form of the enable_if metafunction
template<bool B, class T>
using enable_if_t = typename std::enable_if<B, T>::type;

View File

@ -23,21 +23,6 @@ namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
size_t ModelBase::s_last_id = 0;
// Unique object / instance ID for the wipe tower.
ModelID wipe_tower_object_id()
{
static ModelBase mine;
return mine.id();
}
ModelID wipe_tower_instance_id()
{
static ModelBase mine;
return mine.id();
}
Model& Model::assign_copy(const Model &rhs)
{
this->copy_id(rhs);
@ -88,6 +73,19 @@ void Model::assign_new_unique_ids_recursive()
model_object->assign_new_unique_ids_recursive();
}
void Model::update_links_bottom_up_recursive()
{
for (std::pair<const t_model_material_id, ModelMaterial*> &kvp : this->materials)
kvp.second->set_model(this);
for (ModelObject *model_object : this->objects) {
model_object->set_model(this);
for (ModelInstance *model_instance : model_object->instances)
model_instance->set_model_object(model_object);
for (ModelVolume *model_volume : model_object->volumes)
model_volume->set_model_object(model_object);
}
}
Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances)
{
Model model;
@ -222,7 +220,7 @@ bool Model::delete_object(ModelObject* object)
return false;
}
bool Model::delete_object(ModelID id)
bool Model::delete_object(ObjectID id)
{
if (id.id != 0) {
size_t idx = 0;
@ -633,14 +631,18 @@ ModelObject::~ModelObject()
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
{
this->copy_id(rhs);
assert(this->id().invalid() || this->id() == rhs.id());
assert(this->config.id().invalid() || this->config.id() == rhs.config.id());
this->copy_id(rhs);
this->name = rhs.name;
this->input_file = rhs.input_file;
// Copies the config's ID
this->config = rhs.config;
assert(this->config.id() == rhs.config.id());
this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status;
this->layer_height_ranges = rhs.layer_height_ranges;
this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
this->layer_height_profile = rhs.layer_height_profile;
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
@ -669,14 +671,17 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
{
assert(this->id().invalid());
this->copy_id(rhs);
this->name = std::move(rhs.name);
this->input_file = std::move(rhs.input_file);
// Moves the config's ID
this->config = std::move(rhs.config);
assert(this->config.id() == rhs.config.id());
this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status);
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
@ -1081,11 +1086,11 @@ void ModelObject::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelObject::scale_mesh(const Vec3d &versor)
void ModelObject::scale_mesh_after_creation(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
v->scale_geometry(versor);
v->scale_geometry_after_creation(versor);
v->set_offset(versor.cwiseProduct(v->get_offset()));
}
this->invalidate_bounding_box();
@ -1202,13 +1207,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
if (keep_upper && upper_mesh.facets_count() > 0) {
ModelVolume* vol = upper->add_volume(upper_mesh);
vol->name = volume->name;
vol->config = volume->config;
// Don't copy the config's ID.
static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
}
if (keep_lower && lower_mesh.facets_count() > 0) {
ModelVolume* vol = lower->add_volume(lower_mesh);
vol->name = volume->name;
vol->config = volume->config;
// Don't copy the config's ID.
static_cast<DynamicPrintConfig&>(vol->config) = static_cast<const DynamicPrintConfig&>(volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != volume->config.id());
vol->set_material(volume->material_id(), *volume->material());
// Compute the lower part instances' bounding boxes to figure out where to place
@ -1283,7 +1294,10 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
new_object->name = this->name;
new_object->config = this->config;
// Don't copy the config's ID.
static_cast<DynamicPrintConfig&>(new_object->config) = static_cast<const DynamicPrintConfig&>(this->config);
assert(new_object->config.id().valid());
assert(new_object->config.id() != this->config.id());
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
@ -1576,9 +1590,9 @@ void ModelVolume::center_geometry_after_creation()
if (!shift.isApprox(Vec3d::Zero()))
{
if (m_mesh)
m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
if (m_convex_hull)
m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
translate(shift);
}
}
@ -1731,10 +1745,10 @@ void ModelVolume::mirror(Axis axis)
}
// This method could only be called before the meshes of this ModelVolumes are not shared!
void ModelVolume::scale_geometry(const Vec3d& versor)
void ModelVolume::scale_geometry_after_creation(const Vec3d& versor)
{
m_mesh->scale(versor);
m_convex_hull->scale(versor);
const_cast<TriangleMesh*>(m_mesh.get())->scale(versor);
const_cast<TriangleMesh*>(m_convex_hull.get())->scale(versor);
}
void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed)
@ -1891,7 +1905,7 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
return true;
++i_old;
++ i_old;
++ i_new;
}
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
@ -1913,21 +1927,26 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
void check_model_ids_validity(const Model &model)
{
std::set<ModelID> ids;
auto check = [&ids](ModelID id) {
assert(id.id > 0);
std::set<ObjectID> ids;
auto check = [&ids](ObjectID id) {
assert(id.valid());
assert(ids.find(id) == ids.end());
ids.insert(id);
};
for (const ModelObject *model_object : model.objects) {
check(model_object->id());
for (const ModelVolume *model_volume : model_object->volumes)
check(model_object->config.id());
for (const ModelVolume *model_volume : model_object->volumes) {
check(model_volume->id());
check(model_volume->config.id());
}
for (const ModelInstance *model_instance : model_object->instances)
check(model_instance->id());
}
for (const auto mm : model.materials)
for (const auto mm : model.materials) {
check(mm.second->id());
check(mm.second->config.id());
}
}
void check_model_ids_equal(const Model &model1, const Model &model2)
@ -1938,10 +1957,13 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
const ModelObject &model_object1 = *model1.objects[idx_model];
const ModelObject &model_object2 = * model2.objects[idx_model];
assert(model_object1.id() == model_object2.id());
assert(model_object1.config.id() == model_object2.config.id());
assert(model_object1.volumes.size() == model_object2.volumes.size());
assert(model_object1.instances.size() == model_object2.instances.size());
for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
for (size_t i = 0; i < model_object1.volumes.size(); ++ i) {
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
assert(model_object1.volumes[i]->config.id() == model_object2.volumes[i]->config.id());
}
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
}
@ -1952,9 +1974,22 @@ void check_model_ids_equal(const Model &model1, const Model &model2)
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
assert(it1->first == it2->first); // compare keys
assert(it1->second->id() == it2->second->id());
assert(it1->second->config.id() == it2->second->config.id());
}
}
}
#endif /* NDEBUG */
}
#if 0
CEREAL_REGISTER_TYPE(Slic3r::ModelObject)
CEREAL_REGISTER_TYPE(Slic3r::ModelVolume)
CEREAL_REGISTER_TYPE(Slic3r::ModelInstance)
CEREAL_REGISTER_TYPE(Slic3r::Model)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model)
#endif

View File

@ -2,11 +2,14 @@
#define slic3r_Model_hpp_
#include "libslic3r.h"
#include "PrintConfig.hpp"
#include "Geometry.hpp"
#include "Layer.hpp"
#include "ObjectID.hpp"
#include "Point.hpp"
#include "TriangleMesh.hpp"
#include "PrintConfig.hpp"
#include "Slicing.hpp"
#include "SLA/SLACommon.hpp"
#include "TriangleMesh.hpp"
#include "Arrange.hpp"
#include <map>
@ -14,8 +17,6 @@
#include <string>
#include <utility>
#include <vector>
#include "Geometry.hpp"
#include <libslic3r/SLA/SLACommon.hpp>
namespace Slic3r {
@ -27,6 +28,38 @@ class ModelVolume;
class Print;
class SLAPrint;
namespace UndoRedo {
class StackImpl;
}
class ModelConfig : public ObjectBase, public DynamicPrintConfig
{
private:
friend class cereal::access;
friend class UndoRedo::StackImpl;
friend class ModelObject;
friend class ModelVolume;
friend class ModelMaterial;
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
explicit ModelConfig() {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
explicit ModelConfig(int) : ObjectBase(-1) {}
// Copy constructor copies the ID.
explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); }
// Move constructor copies the ID.
explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); }
ModelConfig& operator=(const ModelConfig &rhs) = default;
ModelConfig& operator=(ModelConfig &&rhs) = default;
template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<DynamicPrintConfig>(this));
}
};
typedef std::string t_model_material_id;
typedef std::string t_model_material_attribute;
typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes;
@ -36,74 +69,13 @@ typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs;
// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
// Valid IDs are strictly positive (non zero).
// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
// for parameter overload.
struct ModelID
{
ModelID(size_t id) : id(id) {}
bool operator==(const ModelID &rhs) const { return this->id == rhs.id; }
bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; }
bool operator< (const ModelID &rhs) const { return this->id < rhs.id; }
bool operator> (const ModelID &rhs) const { return this->id > rhs.id; }
bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; }
bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; }
bool valid() const { return id != 0; }
size_t id;
};
// Unique object / instance ID for the wipe tower.
extern ModelID wipe_tower_object_id();
extern ModelID wipe_tower_instance_id();
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ModelBase derived instances
// are only instantiated from the main thread.
class ModelBase
{
public:
ModelID id() const { return m_id; }
protected:
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
ModelBase() : m_id(generate_new_id()) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
ModelBase(int) : m_id(ModelID(0)) {}
// Use with caution!
void set_new_unique_id() { m_id = generate_new_id(); }
void set_invalid_id() { m_id = 0; }
// Use with caution!
void copy_id(const ModelBase &rhs) { m_id = rhs.id(); }
// Override this method if a ModelBase derived class owns other ModelBase derived instances.
void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
private:
ModelID m_id;
static inline ModelID generate_new_id() { return ModelID(++ s_last_id); }
static size_t s_last_id;
friend ModelID wipe_tower_object_id();
friend ModelID wipe_tower_instance_id();
};
#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
#define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
/* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \
/* to make a private copy for background processing. */ \
static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \
static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \
static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \
static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \
static TYPE* new_copy(const TYPE &rhs) { auto *ret = new TYPE(rhs); assert(ret->id() == rhs.id()); return ret; } \
static TYPE* new_copy(TYPE &&rhs) { auto *ret = new TYPE(std::move(rhs)); assert(ret->id() == rhs.id()); return ret; } \
static TYPE make_copy(const TYPE &rhs) { TYPE ret(rhs); assert(ret.id() == rhs.id()); return ret; } \
static TYPE make_copy(TYPE &&rhs) { TYPE ret(std::move(rhs)); assert(ret.id() == rhs.id()); return ret; } \
TYPE& assign_copy(const TYPE &rhs); \
TYPE& assign_copy(TYPE &&rhs); \
/* Copy a TYPE, generate new IDs. The front end will use this call. */ \
@ -111,52 +83,62 @@ private:
/* Default constructor assigning an invalid ID. */ \
auto obj = new TYPE(-1); \
obj->assign_clone(rhs); \
assert(obj->id().valid() && obj->id() != rhs.id()); \
return obj; \
} \
TYPE make_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
TYPE obj(-1); \
obj.assign_clone(rhs); \
assert(obj.id().valid() && obj.id() != rhs.id()); \
return obj; \
} \
TYPE& assign_clone(const TYPE &rhs) { \
this->assign_copy(rhs); \
assert(this->id().valid() && this->id() == rhs.id()); \
this->assign_new_unique_ids_recursive(); \
assert(this->id().valid() && this->id() != rhs.id()); \
return *this; \
}
#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \
private: \
/* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \
explicit TYPE(int) : ModelBase(-1) {}; \
void assign_new_unique_ids_recursive();
// Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial : public ModelBase
class ModelMaterial final : public ObjectBase
{
public:
// Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
t_model_material_attributes attributes;
// Dynamic configuration storage for the object specific configuration values, overriding the global configuration.
DynamicPrintConfig config;
ModelConfig config;
Model* get_model() const { return m_model; }
void apply(const t_model_material_attributes &attributes)
{ this->attributes.insert(attributes.begin(), attributes.end()); }
protected:
friend class Model;
// Constructor, which assigns a new unique ID.
ModelMaterial(Model *model) : m_model(model) {}
// Copy constructor copies the ID and m_model!
ModelMaterial(const ModelMaterial &rhs) = default;
void set_model(Model *model) { m_model = model; }
private:
// Parent, owning this material.
Model *m_model;
ModelMaterial() = delete;
// To be accessed by the Model.
friend class Model;
// Constructor, which assigns a new unique ID to the material and to its config.
ModelMaterial(Model *model) : m_model(model) { assert(this->id().valid()); }
// Copy constructor copies the IDs of the ModelMaterial and its config, and m_model!
ModelMaterial(const ModelMaterial &rhs) = default;
void set_model(Model *model) { m_model = model; }
void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
// To be accessed by the serialization and Undo/Redo code.
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Create an object for deserialization, don't allocate IDs for ModelMaterial and its config.
ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); }
template<class Archive> void serialize(Archive &ar) {
assert(this->id().invalid()); assert(this->config.id().invalid());
ar(attributes, config);
// assert(this->id().valid()); assert(this->config.id().valid());
}
// Disabled methods.
ModelMaterial(ModelMaterial &&rhs) = delete;
ModelMaterial& operator=(const ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &&rhs) = delete;
@ -166,9 +148,8 @@ private:
// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials.
// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed,
// different rotation and different uniform scaling.
class ModelObject : public ModelBase
class ModelObject final : public ObjectBase
{
friend class Model;
public:
std::string name;
std::string input_file; // XXX: consider fs::path
@ -179,9 +160,9 @@ public:
// ModelVolumes are owned by this ModelObject.
ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
DynamicPrintConfig config;
// Variation of a layer thickness for spans of Z coordinates.
t_layer_height_ranges layer_height_ranges;
ModelConfig config;
// Variation of a layer thickness for spans of Z coordinates + optional parameter overrides.
t_layer_config_ranges layer_config_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
// The pairs of <z, layer_height> are packed into a 1D array.
std::vector<coordf_t> layer_height_profile;
@ -265,7 +246,7 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_mesh(const Vec3d& versor);
void scale_mesh_after_creation(const Vec3d& versor);
size_t materials_count() const;
size_t facets_count() const;
@ -294,26 +275,48 @@ public:
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
int get_mesh_errors_count(const int vol_idx = -1) const;
protected:
friend class Print;
friend class SLAPrint;
// Called by Print::apply() to set the model pointer after making a copy.
void set_model(Model *model) { m_model = model; }
private:
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
~ModelObject();
friend class Model;
// This constructor assigns new ID to this ModelObject and its config.
explicit ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
{ assert(this->id().valid()); }
explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false)
{ assert(this->id().invalid()); assert(this->config.id().invalid()); }
~ModelObject();
void assign_new_unique_ids_recursive() override;
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); }
explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; }
ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; }
// To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision"
// (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics).
ModelObject(const ModelObject &rhs) : ObjectBase(-1), config(-1), m_model(rhs.m_model) {
assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id());
this->assign_copy(rhs);
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
}
explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1), config(-1) {
assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id());
this->assign_copy(std::move(rhs));
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
}
ModelObject& operator=(const ModelObject &rhs) {
this->assign_copy(rhs);
m_model = rhs.m_model;
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
return *this;
}
ModelObject& operator=(ModelObject &&rhs) {
this->assign_copy(std::move(rhs));
m_model = rhs.m_model;
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id());
return *this;
}
void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject)
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
Model *m_model = nullptr;
@ -325,6 +328,24 @@ private:
mutable bool m_raw_bounding_box_valid;
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
mutable bool m_raw_mesh_bounding_box_valid;
// Called by Print::apply() to set the model pointer after making a copy.
friend class Print;
friend class SLAPrint;
void set_model(Model *model) { m_model = model; }
// Undo / Redo through the cereal serialization library
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization -> Don't allocate any IDs for the ModelObject or its config.
ModelObject() : ObjectBase(-1), config(-1), m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {
assert(this->id().invalid()); assert(this->config.id().invalid());
}
template<class Archive> void serialize(Archive &ar) {
ar(cereal::base_class<ObjectBase>(this));
ar(name, input_file, instances, volumes, config, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation,
m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid);
}
};
// Declared outside of ModelVolume, so it could be forward declared.
@ -338,20 +359,20 @@ enum class ModelVolumeType : int {
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
class ModelVolume : public ModelBase
class ModelVolume final : public ObjectBase
{
public:
std::string name;
// The triangular model.
const TriangleMesh& mesh() const { return *m_mesh.get(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<TriangleMesh>(); }
void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared<const TriangleMesh>(mesh); }
void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared<const TriangleMesh>(std::move(mesh)); }
void set_mesh(std::shared_ptr<const TriangleMesh> &mesh) { m_mesh = mesh; }
void set_mesh(std::unique_ptr<const TriangleMesh> &&mesh) { m_mesh = std::move(mesh); }
void reset_mesh() { m_mesh = std::make_shared<const TriangleMesh>(); }
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
ModelConfig config;
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
@ -386,7 +407,7 @@ public:
void mirror(Axis axis);
// This method could only be called before the meshes of this ModelVolumes are not shared!
void scale_geometry(const Vec3d& versor);
void scale_geometry_after_creation(const Vec3d& versor);
// Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box.
// Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared!
@ -432,66 +453,88 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
using ModelBase::set_new_unique_id;
void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); }
protected:
friend class Print;
friend class SLAPrint;
friend class Model;
friend class ModelObject;
// Copies IDs of both the ModelVolume and its config.
explicit ModelVolume(const ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
void assign_new_unique_ids_recursive() override { ObjectBase::set_new_unique_id(); config.set_new_unique_id(); }
void transform_this_mesh(const Transform3d& t, bool fix_left_handed);
void transform_this_mesh(const Matrix3d& m, bool fix_left_handed);
private:
// Parent object owning this ModelVolume.
ModelObject* object;
ModelObject* object;
// The triangular model.
std::shared_ptr<TriangleMesh> m_mesh;
std::shared_ptr<const TriangleMesh> m_mesh;
// Is it an object to be printed, or a modifier volume?
ModelVolumeType m_type;
t_model_material_id m_material_id;
ModelVolumeType m_type;
t_model_material_id m_material_id;
// The convex hull of this model's mesh.
std::shared_ptr<TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation;
std::shared_ptr<const TriangleMesh> m_convex_hull;
Geometry::Transformation m_transformation;
// flag to optimize the checking if the volume is splittable
// -1 -> is unknown value (before first cheking)
// 0 -> is not splittable
// 1 -> is splittable
mutable int m_is_splittable{ -1 };
mutable int m_is_splittable{ -1 };
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {}
m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
}
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
ObjectBase(other),
name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() == other.id() && this->config.id() == other.config.id());
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id());
assert(this->id() != other.id() && this->config.id() == other.config.id());
this->set_material_id(other.material_id());
this->config.set_new_unique_id();
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id());
}
ModelVolume& operator=(ModelVolume &rhs) = delete;
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization, therefore no IDs are allocated.
ModelVolume() : ObjectBase(-1), config(-1), object(nullptr) {
assert(this->id().invalid()); assert(this->config.id().invalid());
}
template<class Archive> void serialize(Archive &ar) {
ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable);
}
};
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
class ModelInstance : public ModelBase
class ModelInstance final : public ObjectBase
{
public:
enum EPrintVolumeState : unsigned char
@ -569,6 +612,7 @@ public:
protected:
friend class Print;
friend class SLAPrint;
friend class Model;
friend class ModelObject;
explicit ModelInstance(const ModelInstance &rhs) = default;
@ -579,27 +623,28 @@ private:
ModelObject* object;
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside)
{
get_arrange_polygon(); // initialize the arrange cache
}
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) { assert(this->id().valid()); }
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside)
{
get_arrange_polygon(); // initialize the arrange cache
}
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) { assert(this->id().valid() && this->id() != other.id()); }
ModelInstance() = delete;
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &&rhs) = delete;
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization, therefore no IDs are allocated.
ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); }
template<class Archive> void serialize(Archive &ar) {
ar(m_transformation, print_volume_state);
}
// Warning! This object is not guarded against concurrency.
mutable struct ArrangeCache {
bool valid = false;
ExPolygon poly;
} m_arrange_cache;
// mutable struct ArrangeCache {
// bool valid = false;
// ExPolygon poly;
// } m_arrange_cache;
};
// The print bed content.
@ -607,7 +652,7 @@ private:
// and with multiple modifier meshes.
// A model groups multiple objects, each object having possibly multiple instances,
// all objects may share mutliple materials.
class Model : public ModelBase
class Model final : public ObjectBase
{
static unsigned int s_auto_extruder_id;
@ -619,17 +664,17 @@ public:
ModelObjectPtrs objects;
// Default constructor assigns a new ID to the model.
Model() {}
Model() { assert(this->id().valid()); }
~Model() { this->clear_objects(); this->clear_materials(); }
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); }
explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; }
Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; }
Model(const Model &rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); }
explicit Model(Model &&rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); }
Model& operator=(const Model &rhs) { this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; }
Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; }
MODELBASE_DERIVED_COPY_MOVE_CLONE(Model)
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model)
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
@ -640,7 +685,7 @@ public:
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other);
void delete_object(size_t idx);
bool delete_object(ModelID id);
bool delete_object(ObjectID id);
bool delete_object(ModelObject* object);
void clear_objects();
@ -658,24 +703,24 @@ public:
BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances,
// return total number of printable objects.
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume);
// Returns true if any ModelObject was modified.
bool center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
bool center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
// Croaks if the duplicated objects do not fit the print bed.
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL);
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
bool looks_like_multipart_object() const;
void convert_multipart_object(unsigned int max_extruders);
// Ensures that the min z of the model is not negative
void adjust_min_z();
void adjust_min_z();
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
static unsigned int get_auto_extruder_id(unsigned int max_extruders);
static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
@ -687,11 +732,19 @@ public:
std::string propose_export_file_name_and_path(const std::string &new_extension) const;
private:
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
void assign_new_unique_ids_recursive();
void update_links_bottom_up_recursive();
friend class cereal::access;
friend class UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) {
ar(materials, objects);
}
};
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing.
@ -711,6 +764,6 @@ void check_model_ids_validity(const Model &model);
void check_model_ids_equal(const Model &model1, const Model &model2);
#endif /* NDEBUG */
}
} // namespace Slic3r
#endif
#endif /* slic3r_Model_hpp_ */

View File

@ -332,7 +332,7 @@ Polyline MotionPlannerGraph::shortest_path(size_t node_start, size_t node_end) c
queue.pop();
map_node_to_queue_id[u] = size_t(-1);
// Stop searching if we reached our destination.
if (u == node_end)
if (size_t(u) == node_end)
break;
// Visit each edge starting at node u.
for (const Neighbor& neighbor : m_adjacency_list[u])

View File

@ -0,0 +1,22 @@
#include "ObjectID.hpp"
namespace Slic3r {
size_t ObjectBase::s_last_id = 0;
// Unique object / instance ID for the wipe tower.
ObjectID wipe_tower_object_id()
{
static ObjectBase mine;
return mine.id();
}
ObjectID wipe_tower_instance_id()
{
static ObjectBase mine;
return mine.id();
}
} // namespace Slic3r
// CEREAL_REGISTER_TYPE(Slic3r::ObjectBase)

View File

@ -0,0 +1,93 @@
#ifndef slic3r_ObjectID_hpp_
#define slic3r_ObjectID_hpp_
#include <cereal/access.hpp>
namespace Slic3r {
namespace UndoRedo {
class StackImpl;
};
// Unique identifier of a mutable object accross the application.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
// (for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial classes)
// and to serialize / deserialize an object onto the Undo / Redo stack.
// Valid IDs are strictly positive (non zero).
// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
// for parameter overload.
class ObjectID
{
public:
ObjectID(size_t id) : id(id) {}
// Default constructor constructs an invalid ObjectID.
ObjectID() : id(0) {}
bool operator==(const ObjectID &rhs) const { return this->id == rhs.id; }
bool operator!=(const ObjectID &rhs) const { return this->id != rhs.id; }
bool operator< (const ObjectID &rhs) const { return this->id < rhs.id; }
bool operator> (const ObjectID &rhs) const { return this->id > rhs.id; }
bool operator<=(const ObjectID &rhs) const { return this->id <= rhs.id; }
bool operator>=(const ObjectID &rhs) const { return this->id >= rhs.id; }
bool valid() const { return id != 0; }
bool invalid() const { return id == 0; }
size_t id;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive &ar) { ar(id); }
};
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ObjectBase derived instances
// are only instantiated from the main thread.
class ObjectBase
{
public:
ObjectID id() const { return m_id; }
protected:
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
ObjectBase() : m_id(generate_new_id()) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
ObjectBase(int) : m_id(ObjectID(0)) {}
// The class tree will have virtual tables and type information.
virtual ~ObjectBase() {}
// Use with caution!
void set_new_unique_id() { m_id = generate_new_id(); }
void set_invalid_id() { m_id = 0; }
// Use with caution!
void copy_id(const ObjectBase &rhs) { m_id = rhs.id(); }
// Override this method if a ObjectBase derived class owns other ObjectBase derived instances.
virtual void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
private:
ObjectID m_id;
static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); }
static size_t s_last_id;
friend ObjectID wipe_tower_object_id();
friend ObjectID wipe_tower_instance_id();
friend class cereal::access;
friend class Slic3r::UndoRedo::StackImpl;
template<class Archive> void serialize(Archive &ar) { ar(m_id); }
ObjectBase(const ObjectID id) : m_id(id) {}
template<class Archive> static void load_and_construct(Archive & ar, cereal::construct<ObjectBase> &construct) { ObjectID id; ar(id); construct(id); }
};
// Unique object / instance ID for the wipe tower.
extern ObjectID wipe_tower_object_id();
extern ObjectID wipe_tower_instance_id();
} // namespace Slic3r
#endif /* slic3r_ObjectID_hpp_ */

View File

@ -175,7 +175,7 @@ void PerimeterGenerator::process()
const PerimeterGeneratorLoop &loop = contours_d[i];
// find the contour loop that contains it
for (int t = d - 1; t >= 0; -- t) {
for (int j = 0; j < contours[t].size(); ++ j) {
for (size_t j = 0; j < contours[t].size(); ++ j) {
PerimeterGeneratorLoop &candidate_parent = contours[t][j];
if (candidate_parent.polygon.contains(loop.polygon.first_point())) {
candidate_parent.children.push_back(loop);
@ -397,7 +397,7 @@ static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyli
pp.push_back(line.b);
width.push_back(line.b_width);
assert(pp.size() == segments + 1);
assert(pp.size() == segments + 1u);
assert(width.size() == segments*2);
}

View File

@ -521,7 +521,6 @@ namespace client
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
{
const std::string *subject = nullptr;
const std::string *mask = nullptr;
if (lhs.type == TYPE_STRING) {
// One type is string, the other could be converted to string.
subject = &lhs.s();
@ -563,7 +562,6 @@ namespace client
static void ternary_op(expr &lhs, expr &rhs1, expr &rhs2)
{
bool value = false;
if (lhs.type != TYPE_BOOL)
lhs.throw_exception("Not a boolean expression");
if (lhs.b())
@ -975,7 +973,7 @@ namespace client
// depending on the context->just_boolean_expression flag. This way a single static expression parser
// could serve both purposes.
start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] >
( eps(_a==true) > text_block(_r1) [_val=_1]
( (eps(_a==true) > text_block(_r1) [_val=_1])
| conditional_expression(_r1) [ px::bind(&expr<Iterator>::evaluate_boolean_to_string, _1, _val) ]
) > eoi;
start.name("start");
@ -1245,7 +1243,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co
std::string::const_iterator end = templ.end();
// Accumulator for the processed template.
std::string output;
bool res = phrase_parse(iter, end, macro_processor_instance(&context), space, output);
phrase_parse(iter, end, macro_processor_instance(&context), space, output);
if (!context.error_message.empty()) {
if (context.error_message.back() != '\n' && context.error_message.back() != '\r')
context.error_message += '\n';

View File

@ -62,8 +62,8 @@ inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
@ -291,4 +291,21 @@ namespace boost { namespace polygon {
} }
// end Boost
// Serialization through the Cereal library
namespace cereal {
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); }
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3crd &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i &v) { archive(v.x(), v.y()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i &v) { archive(v.x(), v.y(), v.z()); }
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec2i64 &v) { archive(v.x(), v.y()); }
// template<class Archive> void serialize(Archive& archive, Slic3r::Vec3i64 &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2f &v) { archive(v.x(), v.y()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3f &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); }
template<class Archive> void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); }
template<class Archive> void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); }
template<class Archive> void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); }
}
#endif

View File

@ -61,7 +61,7 @@ Polylines PolylineCollection::_chained_path_from(
while (! endpoints.empty()) {
// find nearest point
int endpoint_index = nearest_point_index<double>(endpoints, start_near, no_reverse);
assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2);
assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2);
if (move_from_src) {
retval.push_back(std::move(src[endpoints[endpoint_index/2].idx]));
} else {

View File

@ -7,11 +7,13 @@
#include "I18N.hpp"
#include "SupportMaterial.hpp"
#include "GCode.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
#include "GCode/WipeTower.hpp"
#include "Utils.hpp"
//#include "PrintExport.hpp"
#include <float.h>
#include <algorithm>
#include <limits>
#include <unordered_set>
@ -41,36 +43,6 @@ void Print::clear()
m_model.clear_objects();
}
// Only used by the Perl test cases.
void Print::reload_object(size_t /* idx */)
{
ModelObjectPtrs model_objects;
{
tbb::mutex::scoped_lock lock(this->state_mutex());
// The following call should stop background processing if it is running.
this->invalidate_all_steps();
/* TODO: this method should check whether the per-object config and per-material configs
have changed in such a way that regions need to be rearranged or we can just apply
the diff and invalidate something. Same logic as apply()
For now we just re-add all objects since we haven't implemented this incremental logic yet.
This should also check whether object volumes (parts) have changed. */
// collect all current model objects
model_objects.reserve(m_objects.size());
for (PrintObject *object : m_objects)
model_objects.push_back(object->model_object());
// remove our print objects
for (PrintObject *object : m_objects)
delete object;
m_objects.clear();
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
}
// re-add model objects
for (ModelObject *mo : model_objects)
this->add_model_object(mo);
}
PrintRegion* Print::add_region()
{
m_regions.emplace_back(new PrintRegion(this));
@ -121,7 +93,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"filament_density",
"filament_notes",
"filament_cost",
"filament_max_volumetric_speed",
"first_layer_acceleration",
"first_layer_bed_temperature",
"first_layer_speed",
@ -216,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "filament_cooling_initial_speed"
|| opt_key == "filament_cooling_final_speed"
|| opt_key == "filament_ramming_parameters"
|| opt_key == "filament_max_volumetric_speed"
|| opt_key == "gcode_flavor"
|| opt_key == "high_current_on_filament_swap"
|| opt_key == "infill_first"
@ -335,7 +307,7 @@ unsigned int Print::num_object_instances() const
{
unsigned int instances = 0;
for (const PrintObject *print_object : m_objects)
instances += print_object->copies().size();
instances += (unsigned int)print_object->copies().size();
return instances;
}
@ -358,198 +330,6 @@ double Print::max_allowed_layer_height() const
return nozzle_diameter_max;
}
// Caller is responsible for supplying models whose objects don't collide
// and have explicit instance positions.
void Print::add_model_object(ModelObject* model_object, int idx)
{
tbb::mutex::scoped_lock lock(this->state_mutex());
// Add a copy of this ModelObject to this Print.
m_model.objects.emplace_back(ModelObject::new_copy(*model_object));
m_model.objects.back()->set_model(&m_model);
// Initialize a new print object and store it at the given position.
PrintObject *object = new PrintObject(this, model_object, true);
if (idx != -1) {
delete m_objects[idx];
m_objects[idx] = object;
} else
m_objects.emplace_back(object);
// Invalidate all print steps.
this->invalidate_all_steps();
// Set the transformation matrix without translation from the first instance.
if (! model_object->instances.empty()) {
// Trafo and bounding box, both in world coordinate system.
Transform3d trafo = model_object->instances.front()->get_matrix();
BoundingBoxf3 bbox = model_object->instance_bounding_box(0);
// Now shift the object up to align it with the print bed.
trafo.data()[14] -= bbox.min(2);
// and reset the XY translation.
trafo.data()[12] = 0;
trafo.data()[13] = 0;
object->set_trafo(trafo);
}
size_t volume_id = 0;
for (const ModelVolume *volume : model_object->volumes) {
if (! volume->is_model_part() && ! volume->is_modifier())
continue;
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999);
// Find an existing print region with the same config.
size_t region_id = size_t(-1);
for (size_t i = 0; i < m_regions.size(); ++ i)
if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
}
// If no region exists with the same config, create a new one.
if (region_id == size_t(-1)) {
region_id = m_regions.size();
this->add_region(config);
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
++ volume_id;
}
// Apply config to print object.
object->config_apply(this->default_object_config());
{
//normalize_and_apply_config(object->config(), model_object->config);
DynamicPrintConfig src_normalized(model_object->config);
src_normalized.normalize();
object->config_apply(src_normalized, true);
}
}
// This function is only called through the Perl-C++ binding from the unit tests, should be
// removed when unit tests are rewritten to C++.
bool Print::apply_config_perl_tests_only(DynamicPrintConfig config)
{
tbb::mutex::scoped_lock lock(this->state_mutex());
// Perl unit tests were failing in case the preset was not normalized (e.g. https://github.com/prusa3d/PrusaSlicer/issues/2288 was caused
// by too short max_layer_height vector. Calling the necessary function Preset::normalize(...) is not currently possible because there is no
// access to preset. This should be solved when the unit tests are rewritten to C++. For now we just copy-pasted code from Preset.cpp
// to make sure the unit tests pass (functions set_num_extruders and nozzle_options()).
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter", true));
assert(nozzle_diameter != nullptr);
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : { "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
"retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed",
"retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe",
"retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour" })
{
auto *opt = config.option(key, true);
assert(opt != nullptr);
assert(opt->is_vector());
unsigned int num_extruders = (unsigned int)nozzle_diameter->values.size();
static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key));
}
// we get a copy of the config object so we can modify it safely
config.normalize();
// apply variables to placeholder parser
this->placeholder_parser().apply_config(config);
// handle changes to print config
t_config_option_keys print_diff = m_config.diff(config);
m_config.apply_only(config, print_diff, true);
bool invalidated = this->invalidate_state_by_config_options(print_diff);
// handle changes to object config defaults
m_default_object_config.apply(config, true);
for (PrintObject *object : m_objects) {
// we don't assume that config contains a full ObjectConfig,
// so we base it on the current print-wise default
PrintObjectConfig new_config = this->default_object_config();
// we override the new config with object-specific options
normalize_and_apply_config(new_config, object->model_object()->config);
// check whether the new config is different from the current one
t_config_option_keys diff = object->config().diff(new_config);
object->config_apply_only(new_config, diff, true);
invalidated |= object->invalidate_state_by_config_options(diff);
}
// handle changes to regions config defaults
m_default_region_config.apply(config, true);
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
bool rearrange_regions = false;
{
// Collect the already visited region configs into other_region_configs,
// so one may check for duplicates.
std::vector<PrintRegionConfig> other_region_configs;
for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
PrintRegion &region = *m_regions[region_id];
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *object : m_objects) {
if (region_id < object->region_volumes.size()) {
for (int volume_id : object->region_volumes[region_id]) {
const ModelVolume &volume = *object->model_object()->volumes[volume_id];
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
rearrange_regions = true;
goto exit_for_rearrange_regions;
}
} else {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
this_region_config_set = true;
}
for (const PrintRegionConfig &cfg : other_region_configs) {
// If the new config for this volume equals any of the other
// volume configs that are not currently associated to this
// region, it means the region subdivision does not make
// sense anymore.
if (cfg.equals(this_region_config)) {
rearrange_regions = true;
goto exit_for_rearrange_regions;
}
}
}
}
}
if (this_region_config_set) {
t_config_option_keys diff = region.config().diff(this_region_config);
if (! diff.empty()) {
region.config_apply_only(this_region_config, diff, false);
for (PrintObject *object : m_objects)
if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty())
invalidated |= object->invalidate_state_by_config_options(diff);
}
other_region_configs.emplace_back(std::move(this_region_config));
}
}
}
exit_for_rearrange_regions:
if (rearrange_regions) {
// The current subdivision of regions does not make sense anymore.
// We need to remove all objects and re-add them.
ModelObjectPtrs model_objects;
model_objects.reserve(m_objects.size());
for (PrintObject *object : m_objects)
model_objects.push_back(object->model_object());
this->clear();
for (ModelObject *mo : model_objects)
this->add_model_object(mo);
invalidated = true;
}
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
return invalidated;
}
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
// in the exact order and with the same IDs.
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
@ -612,7 +392,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
assert(mv_src.id() == mv_dst.id());
// Copy the ModelVolume data.
mv_dst.name = mv_src.name;
mv_dst.config = mv_src.config;
static_cast<DynamicPrintConfig&>(mv_dst.config) = static_cast<const DynamicPrintConfig&>(mv_src.config);
//FIXME what to do with the materials?
// mv_dst.m_material_id = mv_src.m_material_id;
++ i_src;
@ -620,6 +400,20 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
}
}
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
{
assert(lr_dst.size() == lr_src.size());
auto it_src = lr_src.cbegin();
for (auto &kvp_dst : lr_dst) {
const auto &kvp_src = *it_src ++;
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
kvp_dst.second = kvp_src.second;
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
@ -674,6 +468,23 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
}
// Compare just the layer ranges and their layer heights, not the associated configs.
// Ignore the layer heights if check_layer_heights is false.
bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
{
if (lr1.size() != lr2.size())
return false;
auto it2 = lr2.begin();
for (const auto &kvp1 : lr1) {
const auto &kvp2 = *it2 ++;
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
return false;
}
return true;
}
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
{
#ifdef _DEBUG
@ -724,6 +535,50 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Handle changes to regions config defaults
m_default_region_config.apply_only(config, region_diff, true);
class LayerRanges
{
public:
LayerRanges() {}
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
void assign(const t_layer_config_ranges &in) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in) {
// for (auto &range : in) {
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig* cfg = &range.second;
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
assert(it != m_ranges.end());
assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
return (it == m_ranges.end()) ? nullptr : it->second;
}
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
private:
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
};
struct ModelObjectStatus {
enum Status {
Unknown,
@ -732,9 +587,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
Moved,
Deleted,
};
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id;
Status status;
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
LayerRanges layer_ranges;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
};
@ -839,9 +695,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ModelID id;
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
@ -861,21 +717,23 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object];
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop.
continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
const ModelObject &model_object_new = *model.objects[idx_model_object];
// Check whether a model part volume was added or removed, their transformations or order changed.
// Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked.
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation ||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
model_object.layer_height_profile != model_object_new.layer_height_profile) {
model_object.layer_height_profile != model_object_new.layer_height_profile ||
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) {
@ -899,7 +757,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
model_object.config = model_object_new.config;
static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
if (! object_diff.empty() || object_config_changed) {
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
@ -915,7 +773,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
//FIXME What to do with m_material_id?
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
@ -1027,19 +886,27 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) {
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
if (region_id < print_object->region_volumes.size()) {
for (int volume_id : print_object->region_volumes[region_id]) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_id];
for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
// Regions were split. Reset this print_object.
goto print_object_end;
} else {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders);
for (size_t i = 0; i < region_id; ++i) {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *m_regions[i];
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
// Regions were merged. Reset this print_object.
@ -1054,7 +921,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
update_apply_status(print_object->invalidate_all_steps());
// Decrease the references to regions from this volume.
int ireg = 0;
for (const std::vector<int> &volumes : print_object->region_volumes) {
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
if (! volumes.empty())
-- m_regions[ireg]->m_refcnt;
++ ireg;
@ -1076,52 +943,65 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_object();
std::vector<int> map_volume_to_region(model_object.volumes.size(), -1);
const LayerRanges *layer_ranges;
{
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted);
layer_ranges = &it_status->layer_ranges;
}
std::vector<int> regions_in_object;
regions_in_object.reserve(64);
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
PrintObject &print_object = *m_objects[i];
bool fresh = print_object.region_volumes.empty();
unsigned int volume_id = 0;
unsigned int idx_region_in_object = 0;
for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id;
continue;
}
int region_id = -1;
if (&print_object == &print_object0) {
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
// Find an existing print region with the same config.
int idx_empty_slot = -1;
for (int i = 0; i < (int)m_regions.size(); ++ i) {
if (m_regions[i]->m_refcnt == 0) {
if (idx_empty_slot == -1)
idx_empty_slot = i;
} else if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
// Now insert a volume with a layer range to its own region.
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
int region_id = -1;
if (&print_object == &print_object0) {
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
// Find an existing print region with the same config.
int idx_empty_slot = -1;
for (int i = 0; i < (int)m_regions.size(); ++ i) {
if (m_regions[i]->m_refcnt == 0) {
if (idx_empty_slot == -1)
idx_empty_slot = i;
} else if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
}
}
// If no region exists with the same config, create a new one.
if (region_id == -1) {
if (idx_empty_slot == -1) {
region_id = (int)m_regions.size();
this->add_region(config);
} else {
region_id = idx_empty_slot;
m_regions[region_id]->set_config(std::move(config));
}
}
}
// If no region exists with the same config, create a new one.
if (region_id == -1) {
if (idx_empty_slot == -1) {
region_id = (int)m_regions.size();
this->add_region(config);
} else {
region_id = idx_empty_slot;
m_regions[region_id]->set_config(std::move(config));
}
}
map_volume_to_region[volume_id] = region_id;
} else
region_id = map_volume_to_region[volume_id];
// Assign volume to a region.
if (fresh) {
if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
++ m_regions[region_id]->m_refcnt;
print_object.add_region_volume(region_id, volume_id);
}
++ volume_id;
}
regions_in_object.emplace_back(region_id);
} else
region_id = regions_in_object[idx_region_in_object ++];
// Assign volume to a region.
if (fresh) {
if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
++ m_regions[region_id]->m_refcnt;
print_object.add_region_volume(region_id, volume_id, it_range->first);
}
}
++ volume_id;
}
}
}
@ -1175,9 +1055,9 @@ std::string Print::validate() const
Polygon convex_hull0 = offset(
print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
scale_(m_config.extruder_clearance_radius.value) / 2., jtRound, scale_(0.1)).front();
float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const Point &copy : print_object->m_copies) {
for (const Point &copy : print_object->copies()) {
Polygon convex_hull = convex_hull0;
convex_hull.translate(copy);
if (! intersection(convex_hulls_other, convex_hull).empty())
@ -1211,23 +1091,21 @@ std::string Print::validate() const
return L("The Spiral Vase option can only be used when printing single material objects.");
}
if (m_config.single_extruder_multi_material) {
for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
return L("All extruders must have the same diameter for single extruder multimaterial printer.");
}
if (this->has_wipe_tower() && ! m_objects.empty()) {
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
if (! m_config.use_relative_e_distances)
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
return L("All extruders must have the same diameter for the Wipe Tower.");
if (m_objects.size() > 1) {
bool has_custom_layering = false;
std::vector<std::vector<coordf_t>> layer_height_profiles;
for (const PrintObject *object : m_objects) {
has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty(); // #ys_FIXME_experiment
if (has_custom_layering) {
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
break;
@ -1259,11 +1137,10 @@ std::string Print::validate() const
if (has_custom_layering) {
const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
const PrintObject *object = m_objects[idx_object];
const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
bool failed = false;
if (layer_height_profile_tallest.size() >= layer_height_profile.size()) {
int i = 0;
size_t i = 0;
while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) {
if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) {
failed = true;
@ -1435,9 +1312,9 @@ Flow Print::brim_flow() const
generation as well. */
return Flow::new_from_config_width(
frPerimeter,
width,
m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
this->skirt_first_layer_height(),
width,
(float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
(float)this->skirt_first_layer_height(),
0
);
}
@ -1457,9 +1334,9 @@ Flow Print::skirt_flow() const
generation as well. */
return Flow::new_from_config_width(
frPerimeter,
width,
m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
this->skirt_first_layer_height(),
width,
(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
(float)this->skirt_first_layer_height(),
0
);
}
@ -1628,26 +1505,26 @@ void Print::_make_skirt()
}
// Number of skirt loops per skirt layer.
int n_skirts = m_config.skirts.value;
size_t n_skirts = m_config.skirts.value;
if (this->has_infinite_skirt() && n_skirts == 0)
n_skirts = 1;
// Initial offset of the brim inner edge from the object (possible with a support & raft).
// The skirt will touch the brim if the brim is extruded.
Flow brim_flow = this->brim_flow();
Flow brim_flow = this->brim_flow();
double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
coord_t distance = scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.);
auto distance = float(scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.));
// Draw outlines from outside to inside.
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
for (size_t i = n_skirts, extruder_idx = 0; i > 0; -- i) {
this->throw_if_canceled();
// Offset the skirt outside.
distance += coord_t(scale_(spacing));
distance += float(scale_(spacing));
// Generate the skirt centerline.
Polygon loop;
{
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1));
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty())
break;
@ -1658,9 +1535,9 @@ void Print::_make_skirt()
eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath(
erSkirt,
mm3_per_mm, // this will be overridden at G-code export time
(float)mm3_per_mm, // this will be overridden at G-code export time
flow.width,
first_layer_height // this will be overridden at G-code export time
(float)first_layer_height // this will be overridden at G-code export time
)));
eloop.paths.back().polyline = loop.split_at_first_point();
m_skirt.append(eloop);
@ -1730,7 +1607,6 @@ void Print::_make_brim()
bool Print::has_wipe_tower() const
{
return
m_config.single_extruder_multi_material.value &&
! m_config.spiral_vase.value &&
m_config.wipe_tower.value &&
m_config.nozzle_diameter.values.size() > 1;
@ -1786,7 +1662,7 @@ void Print::_make_wipe_tower()
// Insert the new support layer.
double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z;
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
it_layer = m_objects.front()->insert_support_layer(it_layer, size_t(-1), height, lt.print_z, lt.print_z - 0.5 * height);
it_layer = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height);
++ it_layer;
}
}
@ -1794,7 +1670,8 @@ void Print::_make_wipe_tower()
this->throw_if_canceled();
// Initialize the wipe tower.
WipeTowerPrusaMM wipe_tower(
WipeTower wipe_tower(
m_config.single_extruder_multi_material.value,
float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value),
float(m_config.wipe_tower_width.value),
float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
@ -1808,24 +1685,26 @@ void Print::_make_wipe_tower()
// Set the extruder & material properties at the wipe tower object.
for (size_t i = 0; i < number_of_extruders; ++ i)
wipe_tower.set_extruder(
i,
WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
m_config.filament_type.get_at(i),
m_config.temperature.get_at(i),
m_config.first_layer_temperature.get_at(i),
m_config.filament_loading_speed.get_at(i),
m_config.filament_loading_speed_start.get_at(i),
m_config.filament_unloading_speed.get_at(i),
m_config.filament_unloading_speed_start.get_at(i),
m_config.filament_toolchange_delay.get_at(i),
(float)m_config.filament_loading_speed.get_at(i),
(float)m_config.filament_loading_speed_start.get_at(i),
(float)m_config.filament_unloading_speed.get_at(i),
(float)m_config.filament_unloading_speed_start.get_at(i),
(float)m_config.filament_toolchange_delay.get_at(i),
m_config.filament_cooling_moves.get_at(i),
m_config.filament_cooling_initial_speed.get_at(i),
m_config.filament_cooling_final_speed.get_at(i),
(float)m_config.filament_cooling_initial_speed.get_at(i),
(float)m_config.filament_cooling_final_speed.get_at(i),
m_config.filament_ramming_parameters.get_at(i),
m_config.filament_max_volumetric_speed.get_at(i),
m_config.nozzle_diameter.get_at(i));
m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
@ -1834,21 +1713,21 @@ void Print::_make_wipe_tower()
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
if (!layer_tools.has_wipe_tower) continue;
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id, false);
for (const auto extruder_id : layer_tools.extruders) {
if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
// Not all of that can be used for infill purging:
volume_to_wipe -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
volume_to_wipe -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// try to assign some infills/objects for the wiping:
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
// add back the minimal amount toforce on the wipe tower:
volume_to_wipe += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
volume_to_wipe += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id;
}

View File

@ -80,8 +80,8 @@ private: // Prevents erroneous use by other classes.
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
public:
// vector of (vectors of volume ids), indexed by region_id
std::vector<std::vector<int>> region_volumes;
// vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<std::vector<std::pair<t_layer_height_range, int>>> region_volumes;
// this is set to true when LayerRegion->slices is split in top/internal/bottom
// so that next call to make_perimeters() performs a union() before computing loops
@ -99,10 +99,10 @@ public:
BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
// adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id) {
void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) {
if (region_id >= region_volumes.size())
region_volumes.resize(region_id + 1);
region_volumes[region_id].emplace_back(volume_id);
region_volumes[region_id].emplace_back(layer_range, volume_id);
}
// This is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id
@ -120,7 +120,7 @@ public:
void clear_support_layers();
SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; }
SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z);
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z);
void delete_support_layer(int idx);
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
@ -141,8 +141,9 @@ public:
void slice();
// Helpers to slice support enforcer / blocker meshes by the support generator.
std::vector<ExPolygons> slice_support_enforcers() const;
std::vector<ExPolygons> slice_support_blockers() const;
std::vector<ExPolygons> slice_support_volumes(const ModelVolumeType &model_volume_type) const;
std::vector<ExPolygons> slice_support_blockers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_BLOCKER); }
std::vector<ExPolygons> slice_support_enforcers() const { return this->slice_support_volumes(ModelVolumeType::SUPPORT_ENFORCER); }
protected:
// to be called from Print only.
@ -165,7 +166,7 @@ protected:
void update_slicing_parameters();
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
private:
void make_perimeters();
@ -201,9 +202,11 @@ private:
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> _slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z) const;
std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const;
};
struct WipeTowerData
@ -213,7 +216,7 @@ struct WipeTowerData
// Cache it here, so it does not need to be recalculated during the G-code generation.
ToolOrdering tool_ordering;
// Cache of tool changes per print layer.
std::unique_ptr<WipeTower::ToolChangeResult> priming;
std::unique_ptr<std::vector<WipeTower::ToolChangeResult>> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
std::vector<float> used_filament;
@ -238,6 +241,8 @@ struct PrintStatistics
PrintStatistics() { clear(); }
std::string estimated_normal_print_time;
std::string estimated_silent_print_time;
std::vector<std::string> estimated_normal_color_print_times;
std::vector<std::string> estimated_silent_color_print_times;
double total_used_filament;
double total_extruded_volume;
double total_cost;
@ -256,6 +261,8 @@ struct PrintStatistics
void clear() {
estimated_normal_print_time.clear();
estimated_silent_print_time.clear();
estimated_normal_color_print_times.clear();
estimated_silent_color_print_times.clear();
total_used_filament = 0.;
total_extruded_volume = 0.;
total_cost = 0.;
@ -291,11 +298,6 @@ public:
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
// The following three methods are used by the Perl tests only. Get rid of them!
void reload_object(size_t idx);
void add_model_object(ModelObject* model_object, int idx = -1);
bool apply_config_perl_tests_only(DynamicPrintConfig config);
void process() override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).

View File

@ -246,7 +246,7 @@ public:
struct TaskParams {
TaskParams() : single_model_object(0), single_model_instance_only(false), to_object_step(-1), to_print_step(-1) {}
// If non-empty, limit the processing to this ModelObject.
ModelID single_model_object;
ObjectID single_model_object;
// If set, only process single_model_object. Otherwise process everything, but single_model_object first.
bool single_model_instance_only;
// If non-negative, stop processing at the successive object step.

View File

@ -36,7 +36,6 @@ PrintConfigDef::PrintConfigDef()
void PrintConfigDef::init_common_params()
{
t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
def = this->add("printer_technology", coEnum);
@ -102,7 +101,6 @@ void PrintConfigDef::init_common_params()
void PrintConfigDef::init_fff_params()
{
t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
// Maximum extruder temperature, bumped to 1500 to support printing of glass.
@ -368,7 +366,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("end_filament_gcode", coStrings);
def->label = L("End G-code");
def->tooltip = L("This end procedure is inserted at the end of the output file, before the printer end gcode. "
def->tooltip = L("This end procedure is inserted at the end of the output file, before the printer end gcode (and "
"before any toolchange from this filament in case of multimaterial printers). "
"Note that you can use placeholder variables for all Slic3r settings. "
"If you have multiple extruders, the gcode is processed in extruder order.");
def->multiline = true;
@ -406,10 +405,13 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("bottom_fill_pattern", coEnum);
*def = *def_top_fill_pattern;
def->label = L("Bottom fill pattern");
def->category = L("Infill");
def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells.");
def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern";
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
def->enum_values = def_top_fill_pattern->enum_values;
def->aliases = def_top_fill_pattern->aliases;
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@ -1085,16 +1087,16 @@ void PrintConfigDef::init_fff_params()
// Add the machine feedrate limits for XYZE axes. (M203)
def = this->add("machine_max_feedrate_" + axis.name, coFloats);
def->full_label = (boost::format("Maximum feedrate %1%") % axis_upper).str();
L("Maximum feedrate X");
L("Maximum feedrate Y");
L("Maximum feedrate Z");
L("Maximum feedrate E");
(void)L("Maximum feedrate X");
(void)L("Maximum feedrate Y");
(void)L("Maximum feedrate Z");
(void)L("Maximum feedrate E");
def->category = L("Machine limits");
def->tooltip = (boost::format("Maximum feedrate of the %1% axis") % axis_upper).str();
L("Maximum feedrate of the X axis");
L("Maximum feedrate of the Y axis");
L("Maximum feedrate of the Z axis");
L("Maximum feedrate of the E axis");
(void)L("Maximum feedrate of the X axis");
(void)L("Maximum feedrate of the Y axis");
(void)L("Maximum feedrate of the Z axis");
(void)L("Maximum feedrate of the E axis");
def->sidetext = L("mm/s");
def->min = 0;
def->width = machine_limits_opt_width;
@ -1103,16 +1105,16 @@ void PrintConfigDef::init_fff_params()
// Add the machine acceleration limits for XYZE axes (M201)
def = this->add("machine_max_acceleration_" + axis.name, coFloats);
def->full_label = (boost::format("Maximum acceleration %1%") % axis_upper).str();
L("Maximum acceleration X");
L("Maximum acceleration Y");
L("Maximum acceleration Z");
L("Maximum acceleration E");
(void)L("Maximum acceleration X");
(void)L("Maximum acceleration Y");
(void)L("Maximum acceleration Z");
(void)L("Maximum acceleration E");
def->category = L("Machine limits");
def->tooltip = (boost::format("Maximum acceleration of the %1% axis") % axis_upper).str();
L("Maximum acceleration of the X axis");
L("Maximum acceleration of the Y axis");
L("Maximum acceleration of the Z axis");
L("Maximum acceleration of the E axis");
(void)L("Maximum acceleration of the X axis");
(void)L("Maximum acceleration of the Y axis");
(void)L("Maximum acceleration of the Z axis");
(void)L("Maximum acceleration of the E axis");
def->sidetext = L("mm/s²");
def->min = 0;
def->width = machine_limits_opt_width;
@ -1121,16 +1123,16 @@ void PrintConfigDef::init_fff_params()
// Add the machine jerk limits for XYZE axes (M205)
def = this->add("machine_max_jerk_" + axis.name, coFloats);
def->full_label = (boost::format("Maximum jerk %1%") % axis_upper).str();
L("Maximum jerk X");
L("Maximum jerk Y");
L("Maximum jerk Z");
L("Maximum jerk E");
(void)L("Maximum jerk X");
(void)L("Maximum jerk Y");
(void)L("Maximum jerk Z");
(void)L("Maximum jerk E");
def->category = L("Machine limits");
def->tooltip = (boost::format("Maximum jerk of the %1% axis") % axis_upper).str();
L("Maximum jerk of the X axis");
L("Maximum jerk of the Y axis");
L("Maximum jerk of the Z axis");
L("Maximum jerk of the E axis");
(void)L("Maximum jerk of the X axis");
(void)L("Maximum jerk of the Y axis");
(void)L("Maximum jerk of the Z axis");
(void)L("Maximum jerk of the E axis");
def->sidetext = L("mm/s");
def->min = 0;
def->width = machine_limits_opt_width;
@ -1784,7 +1786,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("start_filament_gcode", coStrings);
def->label = L("Start G-code");
def->tooltip = L("This start procedure is inserted at the beginning, after any printer start gcode. "
def->tooltip = L("This start procedure is inserted at the beginning, after any printer start gcode (and "
"after any toolchange to this filament in case of multi-material printers). "
"This is used to override settings for a specific filament. If Slic3r detects "
"M104, M109, M140 or M190 in your custom codes, such commands will "
"not be prepended automatically so you're free to customize the order "
@ -2039,9 +2042,10 @@ void PrintConfigDef::init_fff_params()
def = this->add("toolchange_gcode", coString);
def->label = L("Tool change G-code");
def->tooltip = L("This custom code is inserted right before every extruder change. "
"Note that you can use placeholder variables for all Slic3r settings as well "
"as [previous_extruder] and [next_extruder].");
def->tooltip = L("This custom code is inserted at every extruder change. If you don't leave this empty, you are "
"expected to take care of the toolchange yourself - PrusaSlicer will not output any other G-code to "
"change the filament. You can use placeholder variables for all Slic3r settings as well as [previous_extruder] "
"and [next_extruder], so e.g. the standard toolchange command can be scripted as T[next_extruder].");
def->multiline = true;
def->full_width = true;
def->height = 5;
@ -2228,7 +2232,6 @@ void PrintConfigDef::init_fff_params()
void PrintConfigDef::init_sla_params()
{
t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
// SLA Printer settings
@ -2505,6 +2508,19 @@ void PrintConfigDef::init_sla_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(1.0));
def = this->add("support_base_safety_distance", coFloat);
def->label = L("Support base safety distance");
def->category = L("Supports");
def->tooltip = L(
"The minimum distance of the pillar base from the model in mm. "
"Makes sense in zero elevation mode where a gap according "
"to this parameter is inserted between the model and the pad.");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("support_critical_angle", coFloat);
def->label = L("Critical angle");
def->category = L("Supports");
@ -2537,7 +2553,9 @@ void PrintConfigDef::init_sla_params()
def = this->add("support_object_elevation", coFloat);
def->label = L("Object elevation");
def->category = L("Supports");
def->tooltip = L("How much the supports should lift up the supported object.");
def->tooltip = L("How much the supports should lift up the supported object. "
"If this value is zero, the bottom of the model geometry "
"will be considered as part of the pad.");
def->sidetext = L("mm");
def->min = 0;
def->max = 150; // This is the max height of print on SL1
@ -2623,6 +2641,47 @@ void PrintConfigDef::init_sla_params()
def->max = 90;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(45.0));
def = this->add("pad_object_gap", coFloat);
def->label = L("Pad object gap");
def->category = L("Pad");
def->tooltip = L("The gap between the object bottom and the generated "
"pad in zero elevation mode.");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(1));
def = this->add("pad_object_connector_stride", coFloat);
def->label = L("Pad object connector stride");
def->category = L("Pad");
def->tooltip = L("Distance between two connector sticks between "
"the object pad and the generated pad.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(10));
def = this->add("pad_object_connector_width", coFloat);
def->label = L("Pad object connector width");
def->category = L("Pad");
def->tooltip = L("The width of the connectors sticks which connect the "
"object pad and the generated pad.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.5));
def = this->add("pad_object_connector_penetration", coFloat);
def->label = L("Pad object connector penetration");
def->category = L("Pad");
def->tooltip = L(
"How much should the tiny connectors penetrate into the model body.");
def->sidetext = L("mm");
def->min = 0;
def->mode = comExpert;
def->set_default_value(new ConfigOptionFloat(0.3));
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
@ -3194,3 +3253,7 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std::
}
}
#include <cereal/types/polymorphic.hpp>
CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig)

View File

@ -984,6 +984,9 @@ public:
// The height of the pillar base cone in mm.
ConfigOptionFloat support_base_height /*= 1.0*/;
// The minimum distance of the pillar base from the model in mm.
ConfigOptionFloat support_base_safety_distance; /*= 1.0*/;
// The default angle for connecting support sticks and junctions.
ConfigOptionFloat support_critical_angle /*= 45*/;
@ -1022,6 +1025,26 @@ public:
// The slope of the pad wall...
ConfigOptionFloat pad_wall_slope;
// /////////////////////////////////////////////////////////////////////////
// Zero elevation mode parameters:
// - The object pad will be derived from the the model geometry.
// - There will be a gap between the object pad and the generated pad
// according to the support_base_safety_distance parameter.
// - The two pads will be connected with tiny connector sticks
// /////////////////////////////////////////////////////////////////////////
// This is the gap between the object bottom and the generated pad
ConfigOptionFloat pad_object_gap;
// How far to place the connector sticks on the object pad perimeter
ConfigOptionFloat pad_object_connector_stride;
// The width of the connectors sticks
ConfigOptionFloat pad_object_connector_width;
// How much should the tiny connectors penetrate into the model body
ConfigOptionFloat pad_object_connector_penetration;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@ -1038,6 +1061,7 @@ protected:
OPT_PTR(support_pillar_widening_factor);
OPT_PTR(support_base_diameter);
OPT_PTR(support_base_height);
OPT_PTR(support_base_safety_distance);
OPT_PTR(support_critical_angle);
OPT_PTR(support_max_bridge_length);
OPT_PTR(support_max_pillar_link_distance);
@ -1050,6 +1074,10 @@ protected:
OPT_PTR(pad_max_merge_distance);
OPT_PTR(pad_edge_radius);
OPT_PTR(pad_wall_slope);
OPT_PTR(pad_object_gap);
OPT_PTR(pad_object_connector_stride);
OPT_PTR(pad_object_connector_width);
OPT_PTR(pad_object_connector_penetration);
}
};
@ -1190,6 +1218,8 @@ private:
this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end());
this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end());
this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end());
for (const auto &kvp : this->options)
this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second;
}
// Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def.
~PrintAndCLIConfigDef() { this->options.clear(); }
@ -1199,4 +1229,38 @@ private:
} // namespace Slic3r
// Serialization through the Cereal library
namespace cereal {
// Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig.
template <class Archive> struct specialize<Archive, Slic3r::DynamicPrintConfig, cereal::specialization::non_member_load_save> {};
template<class Archive> void load(Archive& archive, Slic3r::DynamicPrintConfig &config)
{
size_t cnt;
archive(cnt);
config.clear();
for (size_t i = 0; i < cnt; ++ i) {
size_t serialization_key_ordinal;
archive(serialization_key_ordinal);
assert(serialization_key_ordinal > 0);
auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal);
assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end());
config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive));
}
}
template<class Archive> void save(Archive& archive, const Slic3r::DynamicPrintConfig &config)
{
size_t cnt = config.size();
archive(cnt);
for (auto it = config.cbegin(); it != config.cend(); ++it) {
const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first);
assert(optdef != nullptr);
assert(optdef->serialization_key_ordinal > 0);
archive(optdef->serialization_key_ordinal);
optdef->save_option_to_archive(archive, it->second.get());
}
}
}
#endif

View File

@ -49,7 +49,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
{
// Translate meshes so that our toolpath generation algorithms work with smaller
// XY coordinates; this translation is an optimization and not strictly required.
// A cloned mesh will be aligned to 0 before slicing in _slice_region() since we
// A cloned mesh will be aligned to 0 before slicing in slice_region() since we
// don't assume it's already aligned and we don't alter the original position in model.
// We store the XY translation so that we can place copies correctly in the output G-code
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
@ -430,7 +430,7 @@ SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t p
return m_support_layers.back();
}
SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z)
{
return m_support_layers.insert(pos, new SupportLayer(id, this, height, print_z, slice_z));
}
@ -590,7 +590,12 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
bool PrintObject::invalidate_all_steps()
{
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
// First call the "invalidate" functions, which may cancel background processing.
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
// Then reset some of the depending values.
this->m_slicing_params.valid = false;
this->region_volumes.clear();
return result;
}
bool PrintObject::has_support_material() const
@ -620,7 +625,7 @@ void PrintObject::detect_surfaces_type()
// should be visible.
bool interface_shells = m_config.interface_shells.value;
for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (Layer *layer : m_layers)
@ -806,8 +811,6 @@ void PrintObject::process_external_surfaces()
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info();
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
@ -1028,7 +1031,6 @@ void PrintObject::discover_vertical_shells()
bool hole_first = true;
for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n)
if (n >= 0 && n < (int)m_layers.size()) {
Layer &neighbor_layer = *m_layers[n];
const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n];
if (hole_first) {
hole_first = false;
@ -1354,10 +1356,12 @@ PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObject
return config;
}
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
{
PrintRegionConfig config = default_region_config;
normalize_and_apply_config(config, volume.get_object()->config);
if (layer_range_config != nullptr)
normalize_and_apply_config(config, *layer_range_config);
normalize_and_apply_config(config, volume.config);
if (! volume.material_id().empty())
normalize_and_apply_config(config, volume.material()->config);
@ -1375,28 +1379,37 @@ void PrintObject::update_slicing_parameters()
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
}
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z)
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
{
PrintConfig print_config;
PrintObjectConfig object_config;
PrintRegionConfig default_region_config;
print_config .apply(full_config, true);
object_config.apply(full_config, true);
default_region_config.apply(full_config, true);
size_t num_extruders = print_config.nozzle_diameter.size();
object_config = object_config_from_model_object(object_config, model_object, num_extruders);
PrintConfig print_config;
PrintObjectConfig object_config;
PrintRegionConfig default_region_config;
print_config.apply(full_config, true);
object_config.apply(full_config, true);
default_region_config.apply(full_config, true);
size_t num_extruders = print_config.nozzle_diameter.size();
object_config = object_config_from_model_object(object_config, model_object, num_extruders);
std::vector<unsigned int> object_extruders;
for (const ModelVolume *model_volume : model_object.volumes)
if (model_volume->is_model_part())
PrintRegion::collect_object_printing_extruders(
print_config,
region_config_from_model_volume(default_region_config, *model_volume, num_extruders),
object_extruders);
std::vector<unsigned int> object_extruders;
for (const ModelVolume* model_volume : model_object.volumes)
if (model_volume->is_model_part()) {
PrintRegion::collect_object_printing_extruders(
print_config,
region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders),
object_extruders);
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range_and_config : model_object.layer_config_ranges)
if (range_and_config.second.has("perimeter_extruder") ||
range_and_config.second.has("infill_extruder") ||
range_and_config.second.has("solid_infill_extruder"))
PrintRegion::collect_object_printing_extruders(
print_config,
region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders),
object_extruders);
}
sort_remove_duplicates(object_extruders);
if (object_max_z <= 0.f)
object_max_z = model_object.raw_bounding_box().size().z();
object_max_z = (float)model_object.raw_bounding_box().size().z();
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
}
@ -1430,12 +1443,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
layer_height_profile.clear();
if (layer_height_profile.empty()) {
if (0)
if (0)
// if (this->layer_height_profile.empty())
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes);
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
else
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges);
updated = true;
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); // #ys_FIXME_experiment
updated = true;
}
return updated;
}
@ -1489,22 +1502,28 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}
// Count model parts and modifier meshes, check whether the model parts are of the same region.
int single_volume_region = -2; // not set yet
int all_volumes_single_region = -2; // not set yet
bool has_z_ranges = false;
size_t num_volumes = 0;
size_t num_modifiers = 0;
std::vector<int> map_volume_to_region(this->model_object()->volumes.size());
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
for (int volume_id : this->region_volumes[region_id]) {
int last_volume_id = -1;
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
const int volume_id = volume_and_range.second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
map_volume_to_region[volume_id] = region_id;
if (single_volume_region == -2)
// first model volume met
single_volume_region = region_id;
else if (single_volume_region != region_id)
// multiple volumes met and they are not equal
single_volume_region = -1;
++ num_volumes;
if (last_volume_id == volume_id) {
has_z_ranges = true;
} else {
last_volume_id = volume_id;
if (all_volumes_single_region == -2)
// first model volume met
all_volumes_single_region = region_id;
else if (all_volumes_single_region != region_id)
// multiple volumes met and they are not equal
all_volumes_single_region = -1;
++ num_volumes;
}
} else if (model_volume->is_modifier())
++ num_modifiers;
}
@ -1514,13 +1533,13 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
// Slice all non-modifier volumes.
bool clipped = false;
bool upscaled = false;
if (! m_config.clip_multipart_objects.value || single_volume_region >= 0) {
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
// Cheap path: Slice regions without mutual clipping.
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@ -1541,15 +1560,29 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
};
std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
for (int volume_id : this->region_volumes[region_id]) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
int volume_id = volumes_and_ranges[i].second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first);
size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
ranges.back().second = volumes_and_ranges[j].first.second;
else
ranges.emplace_back(volumes_and_ranges[j].first);
// slicing in parallel
sliced_volumes.emplace_back(volume_id, map_volume_to_region[volume_id], this->_slice_volume(slice_zs, *model_volume));
}
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume));
i = j;
} else
++ i;
}
}
// Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
tbb::parallel_for(
@ -1603,7 +1636,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
m_print->throw_if_canceled();
if (expolygons_by_layer.empty())
continue;
@ -1619,7 +1652,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
continue;
Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
@ -1752,46 +1785,127 @@ end:
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
}
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z) const
{
std::vector<const ModelVolume*> volumes;
std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
for (int volume_id : this->region_volumes[region_id]) {
const ModelVolume *volume = this->model_object()->volumes[volume_id];
if (modifier ? volume->is_modifier() : volume->is_model_part())
volumes.emplace_back(volume);
}
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
if (volume->is_model_part())
volumes.emplace_back(volume);
}
}
return this->_slice_volumes(z, volumes);
return this->slice_volumes(z, volumes);
}
std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
// Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once.
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
{
std::vector<ExPolygons> out;
if (region_id < this->region_volumes.size())
{
std::vector<std::vector<t_layer_height_range>> volume_ranges;
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
volume_ranges.reserve(volumes_and_ranges.size());
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
int volume_id = volumes_and_ranges[i].second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) {
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first);
size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) {
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
ranges.back().second = volumes_and_ranges[j].first.second;
else
ranges.emplace_back(volumes_and_ranges[j].first);
}
volume_ranges.emplace_back(std::move(ranges));
i = j;
} else
++ i;
}
if (! volume_ranges.empty())
{
bool equal_ranges = true;
for (size_t i = 1; i < volume_ranges.size(); ++ i) {
assert(! volume_ranges[i].empty());
if (volume_ranges.front() != volume_ranges[i]) {
equal_ranges = false;
break;
}
}
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
// No modifier in this region was split to layer spans.
std::vector<const ModelVolume*> volumes;
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
if (volume->is_modifier())
volumes.emplace_back(volume);
}
out = this->slice_volumes(slice_zs, volumes);
} else {
// Some modifier in this region was split to layer spans.
std::vector<char> merge;
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
int volume_id = volumes_and_ranges[i].second;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges[i].first);
size_t j = i + 1;
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
ranges.emplace_back(volumes_and_ranges[j].first);
// slicing in parallel
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, *model_volume);
if (out.empty()) {
out = std::move(this_slices);
merge.assign(out.size(), false);
} else {
for (size_t i = 0; i < out.size(); ++ i)
if (! this_slices[i].empty())
if (! out[i].empty()) {
append(out[i], this_slices[i]);
merge[i] = true;
} else
out[i] = std::move(this_slices[i]);
}
i = j;
} else
++ i;
}
}
for (size_t i = 0; i < merge.size(); ++ i)
if (merge[i])
out[i] = union_ex(out[i]);
}
}
}
return out;
}
std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType &model_volume_type) const
{
std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes)
if (volume->is_support_enforcer())
if (volume->type() == model_volume_type)
volumes.emplace_back(volume);
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z);
return this->_slice_volumes(zs, volumes);
return this->slice_volumes(zs, volumes);
}
std::vector<ExPolygons> PrintObject::slice_support_blockers() const
{
std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes)
if (volume->is_support_blocker())
volumes.emplace_back(volume);
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z);
return this->_slice_volumes(zs, volumes);
}
std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
{
std::vector<ExPolygons> layers;
if (! volumes.empty()) {
@ -1828,34 +1942,71 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
return layers;
}
std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh());
mesh.transform(volume.get_matrix(), true);
if (mesh.repaired) {
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
stl_check_facets_exact(&mesh.stl);
if (! z.empty()) {
// Compose mesh.
//FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh());
mesh.transform(volume.get_matrix(), true);
if (mesh.repaired) {
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
stl_check_facets_exact(&mesh.stl);
}
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true);
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
// TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
}
}
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true);
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
TriangleMeshSlicer mslicer;
const Print *print = this->print();
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
// TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
mslicer.init(&mesh, callback);
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled();
}
return layers;
}
// Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping.
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const
{
std::vector<ExPolygons> out;
if (! z.empty() && ! ranges.empty()) {
if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) {
// All layers fit into a single range.
out = this->slice_volume(z, volume);
} else {
std::vector<float> z_filtered;
std::vector<std::pair<size_t, size_t>> n_filtered;
z_filtered.reserve(z.size());
n_filtered.reserve(2 * ranges.size());
size_t i = 0;
for (const t_layer_height_range &range : ranges) {
for (; i < z.size() && z[i] < range.first; ++ i) ;
size_t first = i;
for (; i < z.size() && z[i] < range.second; ++ i)
z_filtered.emplace_back(z[i]);
if (i > first)
n_filtered.emplace_back(std::make_pair(first, i));
}
if (! n_filtered.empty()) {
std::vector<ExPolygons> layers = this->slice_volume(z_filtered, volume);
out.assign(z.size(), ExPolygons());
i = 0;
for (const std::pair<size_t, size_t> &span : n_filtered)
for (size_t j = span.first; j < span.second; ++ j)
out[j] = std::move(layers[i ++]);
}
}
}
return out;
}
std::string PrintObject::_fix_slicing_errors()
{
// Collect layers with slicing errors.
@ -2119,7 +2270,7 @@ void PrintObject::clip_fill_surfaces()
//Should the pw not be half of the current value?
float pw = FLT_MAX;
for (const LayerRegion *layerm : layer->m_regions)
pw = std::min<float>(pw, layerm->flow(frPerimeter).scaled_width());
pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
// Append such thick perimeters to the areas that need support
polygons_append(overhangs, offset2(perimeters, -pw, +pw));
}
@ -2154,7 +2305,7 @@ void PrintObject::discover_horizontal_shells()
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (int i = 0; i < int(m_layers.size()); ++ i) {
for (size_t i = 0; i < m_layers.size(); ++ i) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[i]->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region()->config();
@ -2171,7 +2322,7 @@ void PrintObject::discover_horizontal_shells()
if (region_config.ensure_vertical_shell_thickness.value)
continue;
for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
m_print->throw_if_canceled();
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
// Find slices of current type for current layer.
@ -2200,7 +2351,7 @@ void PrintObject::discover_horizontal_shells()
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom';
size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value;
for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - i) < solid_layers; (type == stTop) ? -- n : ++ n) {
for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) {
if (n < 0 || n >= int(m_layers.size()))
continue;
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
@ -2345,7 +2496,7 @@ void PrintObject::combine_infill()
// Work on each region separately.
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions()[region_id];
const int every = region->config().infill_every_layers.value;
const size_t every = region->config().infill_every_layers.value;
if (every < 2 || region->config().fill_density == 0.)
continue;
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.

View File

@ -59,8 +59,6 @@ SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3
void SLAAutoSupports::project_onto_mesh(std::vector<sla::SupportPoint>& points) const
{
// The function makes sure that all the points are really exactly placed on the mesh.
igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f};
igl::Hit hit_down{0, 0, 0.f, 0.f, 0.f};
// Use a reasonable granularity to account for the worker thread synchronization cost.
tbb::parallel_for(tbb::blocked_range<size_t>(0, points.size(), 64),
@ -140,7 +138,6 @@ static std::vector<SLAAutoSupports::MyLayer> make_layers(
SLAAutoSupports::MyLayer &layer_above = layers[layer_id];
SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1];
//FIXME WTF?
const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0]));
const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]);
const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports
const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle)));
@ -212,7 +209,7 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
for (Structure &top : layer_top->islands)
for (Structure::Link &bottom_link : top.islands_below) {
Structure &bottom = *bottom_link.island;
float centroids_dist = (bottom.centroid - top.centroid).norm();
//float centroids_dist = (bottom.centroid - top.centroid).norm();
// Penalization resulting from centroid offset:
// bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area));
float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()];
@ -239,7 +236,7 @@ void SLAAutoSupports::process(const std::vector<ExPolygons>& slices, const std::
// s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area);
s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area);
float force_deficit = s.support_force_deficit(m_config.tear_pressure());
//float force_deficit = s.support_force_deficit(m_config.tear_pressure());
if (s.islands_below.empty()) { // completely new island - needs support no doubt
uniformly_cover({ *s.polygon }, s, point_grid, true);
} else if (! s.dangling_areas.empty()) {
@ -380,7 +377,7 @@ static inline std::vector<Vec2f> poisson_disk_from_samples(const std::vector<Vec
{
typename Cells::iterator last_cell_id_it;
Vec2i last_cell_id(-1, -1);
for (int i = 0; i < raw_samples_sorted.size(); ++ i) {
for (size_t i = 0; i < raw_samples_sorted.size(); ++ i) {
const RawSample &sample = raw_samples_sorted[i];
if (sample.cell_id == last_cell_id) {
// This sample is in the same cell as the previous, so just increase the count. Cells are

View File

@ -8,9 +8,9 @@
#include "MTUtils.hpp"
// For debugging:
//#include <fstream>
//#include <libnest2d/tools/benchmark.h>
//#include "SVG.hpp"
// #include <fstream>
// #include <libnest2d/tools/benchmark.h>
// #include "SVG.hpp"
namespace Slic3r { namespace sla {
@ -184,9 +184,10 @@ Contour3D walls(const Polygon& lower, const Polygon& upper,
}
/// Offsetting with clipper and smoothing the edges into a curvature.
void offset(ExPolygon& sh, coord_t distance) {
void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) {
using ClipperLib::ClipperOffset;
using ClipperLib::jtRound;
using ClipperLib::jtMiter;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
using ClipperLib::Path;
@ -203,11 +204,13 @@ void offset(ExPolygon& sh, coord_t distance) {
return;
}
auto jointype = edgerounding? jtRound : jtMiter;
ClipperOffset offs;
offs.ArcTolerance = scaled<double>(0.01);
Paths result;
offs.AddPath(ctour, jtRound, etClosedPolygon);
offs.AddPaths(holes, jtRound, etClosedPolygon);
offs.AddPath(ctour, jointype, etClosedPolygon);
offs.AddPaths(holes, jointype, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
// Offsetting reverts the orientation and also removes the last vertex
@ -237,6 +240,50 @@ void offset(ExPolygon& sh, coord_t distance) {
}
}
void offset(Polygon &sh, coord_t distance, bool edgerounding = true)
{
using ClipperLib::ClipperOffset;
using ClipperLib::jtRound;
using ClipperLib::jtMiter;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
using ClipperLib::Path;
auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh);
// If the input is not at least a triangle, we can not do this algorithm
if (ctour.size() < 3) {
BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
return;
}
ClipperOffset offs;
offs.ArcTolerance = 0.01 * scaled(1.);
Paths result;
offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
// Offsetting reverts the orientation and also removes the last vertex
// so boost will not have a closed polygon.
bool found_the_contour = false;
for (auto &r : result) {
if (ClipperLib::Orientation(r)) {
// We don't like if the offsetting generates more than one contour
// but throwing would be an overkill. Instead, we should warn the
// caller about the inability to create correct geometries
if (!found_the_contour) {
auto rr = ClipperPath_to_Slic3rPolygon(r);
sh.points.swap(rr.points);
found_the_contour = true;
} else {
BOOST_LOG_TRIVIAL(warning)
<< "Warning: offsetting result is invalid!";
}
}
}
}
/// Unification of polygons (with clipper) preserving holes as well.
ExPolygons unify(const ExPolygons& shapes) {
using ClipperLib::ptSubject;
@ -307,6 +354,116 @@ ExPolygons unify(const ExPolygons& shapes) {
return retv;
}
Polygons unify(const Polygons& shapes) {
using ClipperLib::ptSubject;
bool closed = true;
bool valid = true;
ClipperLib::Clipper clipper;
for(auto& path : shapes) {
auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path);
if(!clipperpath.empty())
valid &= clipper.AddPath(clipperpath, ptSubject, closed);
}
if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";
ClipperLib::Paths result;
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
Polygons ret;
for (ClipperLib::Path &p : result) {
Polygon pp = ClipperPath_to_Slic3rPolygon(p);
if (!pp.is_clockwise()) ret.emplace_back(std::move(pp));
}
return ret;
}
// Function to cut tiny connector cavities for a given polygon. The input poly
// will be offsetted by "padding" and small rectangle shaped cavities will be
// inserted along the perimeter in every "stride" distance. The stick rectangles
// will have a with about "stick_width". The input dimensions are in world
// measure, not the scaled clipper units.
void breakstick_holes(ExPolygon& poly,
double padding,
double stride,
double stick_width,
double penetration)
{
// SVG svg("bridgestick_plate.svg");
// svg.draw(poly);
auto transf = [stick_width, penetration, padding, stride](Points &pts) {
// The connector stick will be a small rectangle with dimensions
// stick_width x (penetration + padding) to have some penetration
// into the input polygon.
Points out;
out.reserve(2 * pts.size()); // output polygon points
// stick bottom and right edge dimensions
double sbottom = scaled(stick_width);
double sright = scaled(penetration + padding);
// scaled stride distance
double sstride = scaled(stride);
double t = 0;
// process pairs of vertices as an edge, start with the last and
// first point
for (size_t i = pts.size() - 1, j = 0; j < pts.size(); i = j, ++j) {
// Get vertices and the direction vectors
const Point &a = pts[i], &b = pts[j];
Vec2d dir = b.cast<double>() - a.cast<double>();
double nrm = dir.norm();
dir /= nrm;
Vec2d dirp(-dir(Y), dir(X));
// Insert start point
out.emplace_back(a);
// dodge the start point, do not make sticks on the joins
while (t < sbottom) t += sbottom;
double tend = nrm - sbottom;
while (t < tend) { // insert the stick on the polygon perimeter
// calculate the stick rectangle vertices and insert them
// into the output.
Point p1 = a + (t * dir).cast<coord_t>();
Point p2 = p1 + (sright * dirp).cast<coord_t>();
Point p3 = p2 + (sbottom * dir).cast<coord_t>();
Point p4 = p3 + (sright * -dirp).cast<coord_t>();
out.insert(out.end(), {p1, p2, p3, p4});
// continue along the perimeter
t += sstride;
}
t = t - nrm;
// Insert edge endpoint
out.emplace_back(b);
}
// move the new points
out.shrink_to_fit();
pts.swap(out);
};
if(stride > 0.0 && stick_width > 0.0 && padding > 0.0) {
transf(poly.contour.points);
for (auto &h : poly.holes) transf(h.points);
}
// svg.draw(poly);
// svg.Close();
}
/// This method will create a rounded edge around a flat polygon in 3d space.
/// 'base_plate' parameter is the target plate.
/// 'radius' is the radius of the edges.
@ -426,40 +583,37 @@ inline Point centroid(Points& pp) {
return c;
}
inline Point centroid(const ExPolygon& poly) {
return poly.contour.centroid();
inline Point centroid(const Polygon& poly) {
return poly.centroid();
}
/// A fake concave hull that is constructed by connecting separate shapes
/// with explicit bridges. Bridges are generated from each shape's centroid
/// to the center of the "scene" which is the centroid calculated from the shape
/// centroids (a star is created...)
ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
ThrowOnCancel throw_on_cancel = [](){})
Polygons concave_hull(const Polygons& polys, double max_dist_mm = 50,
ThrowOnCancel throw_on_cancel = [](){})
{
namespace bgi = boost::geometry::index;
using SpatElement = std::pair<BoundingBox, unsigned>;
using SpatElement = std::pair<Point, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
if(polys.empty()) return ExPolygons();
if(polys.empty()) return Polygons();
ExPolygons punion = unify(polys); // could be redundant
const double max_dist = scaled(max_dist_mm);
Polygons punion = unify(polys); // could be redundant
if(punion.size() == 1) return punion;
// We get the centroids of all the islands in the 2D slice
Points centroids; centroids.reserve(punion.size());
std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
[](const ExPolygon& poly) { return centroid(poly); });
SpatIndex boxindex; unsigned idx = 0;
std::for_each(punion.begin(), punion.end(),
[&boxindex, &idx](const ExPolygon& expo) {
BoundingBox bb(expo);
boxindex.insert(std::make_pair(bb, idx++));
});
[](const Polygon& poly) { return centroid(poly); });
SpatIndex ctrindex;
unsigned idx = 0;
for(const Point &ct : centroids) ctrindex.insert(std::make_pair(ct, idx++));
// Centroid of the centroids of islands. This is where the additional
// connector sticks are routed.
@ -470,25 +624,32 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
idx = 0;
std::transform(centroids.begin(), centroids.end(),
std::back_inserter(punion),
[&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel]
[&centroids, &ctrindex, cc, max_dist, &idx, throw_on_cancel]
(const Point& c)
{
throw_on_cancel();
double dx = x(c) - x(cc), dy = y(c) - y(cc);
double l = std::sqrt(dx * dx + dy * dy);
double nx = dx / l, ny = dy / l;
double max_dist = scaled<double>(max_dist_mm);
ExPolygon& expo = punion[idx++];
BoundingBox querybb(expo);
Point& ct = centroids[idx];
querybb.offset(max_dist);
std::vector<SpatElement> result;
boxindex.query(bgi::intersects(querybb), std::back_inserter(result));
if(result.size() <= 1) return ExPolygon();
ctrindex.query(bgi::nearest(ct, 2), std::back_inserter(result));
ExPolygon r;
auto& ctour = r.contour.points;
double dist = max_dist;
for (const SpatElement &el : result)
if (el.second != idx) {
dist = Line(el.first, ct).length();
break;
}
idx++;
if (dist >= max_dist) return Polygon();
Polygon r;
auto& ctour = r.points;
ctour.reserve(3);
ctour.emplace_back(cc);
@ -507,20 +668,16 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
return punion;
}
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
float layerh, ThrowOnCancel thrfn)
void base_plate(const TriangleMesh & mesh,
ExPolygons & output,
const std::vector<float> &heights,
ThrowOnCancel thrfn)
{
TriangleMesh m = mesh;
m.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&m);
if (mesh.empty()) return;
// m.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&mesh);
auto bb = mesh.bounding_box();
float gnd = float(bb.min(Z));
std::vector<float> heights = {float(bb.min(Z))};
for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
heights.emplace_back(hi);
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
std::vector<ExPolygons> out; out.reserve(heights.size());
slicer.slice(heights, 0.f, &out, thrfn);
size_t count = 0; for(auto& o : out) count += o.size();
@ -542,7 +699,24 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
}
}
Contour3D create_base_pool(const ExPolygons &ground_layer,
void base_plate(const TriangleMesh &mesh,
ExPolygons & output,
float h,
float layerh,
ThrowOnCancel thrfn)
{
auto bb = mesh.bounding_box();
float gnd = float(bb.min(Z));
std::vector<float> heights = {float(bb.min(Z))};
for(float hi = gnd + layerh; hi <= gnd + h; hi += layerh)
heights.emplace_back(hi);
base_plate(mesh, output, heights, thrfn);
}
Contour3D create_base_pool(const Polygons &ground_layer,
const ExPolygons &obj_self_pad = {},
const PoolConfig& cfg = PoolConfig())
{
// for debugging:
@ -557,7 +731,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
// serve as the bottom plate of the pad. We will offset this concave hull
// and then offset back the result with clipper with rounding edges ON. This
// trick will create a nice rounded pad shape.
ExPolygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
Polygons concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel);
const double thickness = cfg.min_wall_thickness_mm;
const double wingheight = cfg.min_wall_height_mm;
@ -577,42 +751,37 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
Contour3D pool;
for(ExPolygon& concaveh : concavehs) {
if(concaveh.contour.points.empty()) return pool;
// Get rid of any holes in the concave hull output.
concaveh.holes.clear();
for(Polygon& concaveh : concavehs) {
if(concaveh.points.empty()) return pool;
// Here lies the trick that does the smoothing only with clipper offset
// calls. The offset is configured to round edges. Inner edges will
// be rounded because we offset twice: ones to get the outer (top) plate
// and again to get the inner (bottom) plate
auto outer_base = concaveh;
outer_base.holes.clear();
offset(outer_base, s_safety_dist + s_wingdist + s_thickness);
ExPolygon bottom_poly = outer_base;
bottom_poly.holes.clear();
ExPolygon bottom_poly; bottom_poly.contour = outer_base;
offset(bottom_poly, -s_bottom_offs);
// Punching a hole in the top plate for the cavity
ExPolygon top_poly;
ExPolygon middle_base;
ExPolygon inner_base;
top_poly.contour = outer_base.contour;
top_poly.contour = outer_base;
if(wingheight > 0) {
inner_base = outer_base;
inner_base.contour = outer_base;
offset(inner_base, -(s_thickness + s_wingdist + s_eradius));
middle_base = outer_base;
middle_base.contour = outer_base;
offset(middle_base, -s_thickness);
top_poly.holes.emplace_back(middle_base.contour);
auto& tph = top_poly.holes.back().points;
std::reverse(tph.begin(), tph.end());
}
ExPolygon ob = outer_base; double wh = 0;
ExPolygon ob; ob.contour = outer_base; double wh = 0;
// now we will calculate the angle or portion of the circle from
// pi/2 that will connect perfectly with the bottom plate.
@ -659,6 +828,7 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
if(wingheight > 0) {
// Generate the smoothed edge geometry
wh = 0;
ob = middle_base;
if(s_eradius) pool.merge(round_edges(middle_base,
r,
phi - 90, // from tangent lines
@ -673,11 +843,59 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
wh, -wingdist, thrcl));
}
// Now we need to triangulate the top and bottom plates as well as the
// cavity bottom plate which is the same as the bottom plate but it is
// elevated by the thickness.
if (cfg.embed_object) {
ExPolygons bttms = diff_ex(to_polygons(bottom_poly),
to_polygons(obj_self_pad));
assert(!bttms.empty());
std::sort(bttms.begin(), bttms.end(),
[](const ExPolygon& e1, const ExPolygon& e2) {
return e1.contour.area() > e2.contour.area();
});
if(wingheight > 0) inner_base.holes = bttms.front().holes;
else top_poly.holes = bttms.front().holes;
auto straight_walls =
[&pool](const Polygon &cntr, coord_t z_low, coord_t z_high) {
auto lines = cntr.lines();
for (auto &l : lines) {
auto s = coord_t(pool.points.size());
auto& pts = pool.points;
pts.emplace_back(unscale(l.a.x(), l.a.y(), z_low));
pts.emplace_back(unscale(l.b.x(), l.b.y(), z_low));
pts.emplace_back(unscale(l.a.x(), l.a.y(), z_high));
pts.emplace_back(unscale(l.b.x(), l.b.y(), z_high));
pool.indices.emplace_back(s, s + 1, s + 3);
pool.indices.emplace_back(s, s + 3, s + 2);
}
};
coord_t z_lo = -scaled(fullheight), z_hi = -scaled(wingheight);
for (ExPolygon &ep : bttms) {
pool.merge(triangulate_expolygon_3d(ep, -fullheight, true));
for (auto &h : ep.holes) straight_walls(h, z_lo, z_hi);
}
// Skip the outer contour, triangulate the holes
for (auto it = std::next(bttms.begin()); it != bttms.end(); ++it) {
pool.merge(triangulate_expolygon_3d(*it, -wingheight));
straight_walls(it->contour, z_lo, z_hi);
}
} else {
// Now we need to triangulate the top and bottom plates as well as
// the cavity bottom plate which is the same as the bottom plate
// but it is elevated by the thickness.
pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
}
pool.merge(triangulate_expolygon_3d(top_poly));
pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true));
if(wingheight > 0)
pool.merge(triangulate_expolygon_3d(inner_base, -wingheight));
@ -687,8 +905,8 @@ Contour3D create_base_pool(const ExPolygons &ground_layer,
return pool;
}
void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
const PoolConfig& cfg)
void create_base_pool(const Polygons &ground_layer, TriangleMesh& out,
const ExPolygons &holes, const PoolConfig& cfg)
{
@ -698,7 +916,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
// std::fstream fout("pad_debug.obj", std::fstream::out);
// if(fout.good()) pool.to_obj(fout);
out.merge(mesh(create_base_pool(ground_layer, cfg)));
out.merge(mesh(create_base_pool(ground_layer, holes, cfg)));
}
}

View File

@ -8,7 +8,9 @@
namespace Slic3r {
class ExPolygon;
class Polygon;
using ExPolygons = std::vector<ExPolygon>;
using Polygons = std::vector<Polygon>;
class TriangleMesh;
@ -19,16 +21,40 @@ using ThrowOnCancel = std::function<void(void)>;
/// Calculate the polygon representing the silhouette from the specified height
void base_plate(const TriangleMesh& mesh, // input mesh
ExPolygons& output, // Output will be merged with
float zlevel = 0.1f, // Plate creation level
float samplingheight = 0.1f, // The height range to sample
float layerheight = 0.05f, // The sampling height
ThrowOnCancel thrfn = [](){}); // Will be called frequently
void base_plate(const TriangleMesh& mesh, // input mesh
ExPolygons& output, // Output will be merged with
const std::vector<float>&, // Exact Z levels to sample
ThrowOnCancel thrfn = [](){}); // Will be called frequently
// Function to cut tiny connector cavities for a given polygon. The input poly
// will be offsetted by "padding" and small rectangle shaped cavities will be
// inserted along the perimeter in every "stride" distance. The stick rectangles
// will have a with about "stick_width". The input dimensions are in world
// measure, not the scaled clipper units.
void breakstick_holes(ExPolygon &poly,
double padding,
double stride,
double stick_width,
double penetration = 0.0);
struct PoolConfig {
double min_wall_thickness_mm = 2;
double min_wall_height_mm = 5;
double max_merge_distance_mm = 50;
double edge_radius_mm = 1;
double wall_slope = std::atan(1.0); // Universal constant for Pi/4
struct EmbedObject {
double object_gap_mm = 0.5;
double stick_stride_mm = 10;
double stick_width_mm = 0.3;
double stick_penetration_mm = 0.1;
bool enabled = false;
operator bool() const { return enabled; }
} embed_object;
ThrowOnCancel throw_on_cancel = [](){};
@ -42,15 +68,12 @@ struct PoolConfig {
};
/// Calculate the pool for the mesh for SLA printing
void create_base_pool(const ExPolygons& base_plate,
void create_base_pool(const Polygons& base_plate,
TriangleMesh& output_mesh,
const ExPolygons& holes,
const PoolConfig& = PoolConfig());
/// TODO: Currently the base plate of the pool will have half the height of the
/// whole pool. So the carved out space has also half the height. This is not
/// a particularly elegant solution, the thickness should be exactly
/// min_wall_thickness and it should be corrected in the future. This method
/// will return the correct value for further processing.
/// Returns the elevation needed for compensating the pad.
inline double get_pad_elevation(const PoolConfig& cfg) {
return cfg.min_wall_thickness_mm;
}

View File

@ -43,6 +43,8 @@ struct SupportPoint {
bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; }
bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); }
template<class Archive> void serialize(Archive &ar) { ar(pos, head_front_radius, is_new_island); }
};
/// An index-triangle structure for libIGL functions. Also serves as an
@ -60,7 +62,7 @@ class EigenMesh3D {
Eigen::MatrixXd m_V;
Eigen::MatrixXi m_F;
double m_ground_level = 0;
double m_ground_level = 0, m_gnd_offset = 0;
std::unique_ptr<AABBImpl> m_aabb;
public:
@ -71,7 +73,9 @@ public:
~EigenMesh3D();
inline double ground_level() const { return m_ground_level; }
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
inline void ground_level_offset(double o) { m_gnd_offset = o; }
inline double ground_level_offset() const { return m_gnd_offset; }
inline const Eigen::MatrixXd& V() const { return m_V; }
inline const Eigen::MatrixXi& F() const { return m_F; }
@ -149,6 +153,12 @@ public:
#endif /* SLIC3R_SLA_NEEDS_WINDTREE */
double squared_distance(const Vec3d& p, int& i, Vec3d& c) const;
inline double squared_distance(const Vec3d &p) const
{
int i;
Vec3d c;
return squared_distance(p, i, c);
}
};

View File

@ -7,13 +7,15 @@
#include <Eigen/Geometry>
#include <libslic3r/BoundingBox.hpp>
namespace Slic3r {
namespace sla {
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
using SpatElement = std::pair<Vec3d, unsigned>;
using PointIndexEl = std::pair<Vec3d, unsigned>;
class SpatIndex {
class PointIndex {
class Impl;
// We use Pimpl because it takes a long time to compile boost headers which
@ -21,30 +23,67 @@ class SpatIndex {
std::unique_ptr<Impl> m_impl;
public:
SpatIndex();
~SpatIndex();
PointIndex();
~PointIndex();
SpatIndex(const SpatIndex&);
SpatIndex(SpatIndex&&);
SpatIndex& operator=(const SpatIndex&);
SpatIndex& operator=(SpatIndex&&);
PointIndex(const PointIndex&);
PointIndex(PointIndex&&);
PointIndex& operator=(const PointIndex&);
PointIndex& operator=(PointIndex&&);
void insert(const SpatElement&);
bool remove(const SpatElement&);
void insert(const PointIndexEl&);
bool remove(const PointIndexEl&);
inline void insert(const Vec3d& v, unsigned idx)
{
insert(std::make_pair(v, unsigned(idx)));
}
std::vector<SpatElement> query(std::function<bool(const SpatElement&)>);
std::vector<SpatElement> nearest(const Vec3d&, unsigned k);
std::vector<PointIndexEl> query(std::function<bool(const PointIndexEl&)>);
std::vector<PointIndexEl> nearest(const Vec3d&, unsigned k);
// For testing
size_t size() const;
bool empty() const { return size() == 0; }
void foreach(std::function<void(const SpatElement& el)> fn);
void foreach(std::function<void(const PointIndexEl& el)> fn);
};
using BoxIndexEl = std::pair<Slic3r::BoundingBox, unsigned>;
class BoxIndex {
class Impl;
// We use Pimpl because it takes a long time to compile boost headers which
// is the engine of this class. We include it only in the cpp file.
std::unique_ptr<Impl> m_impl;
public:
BoxIndex();
~BoxIndex();
BoxIndex(const BoxIndex&);
BoxIndex(BoxIndex&&);
BoxIndex& operator=(const BoxIndex&);
BoxIndex& operator=(BoxIndex&&);
void insert(const BoxIndexEl&);
inline void insert(const BoundingBox& bb, unsigned idx)
{
insert(std::make_pair(bb, unsigned(idx)));
}
bool remove(const BoxIndexEl&);
enum QueryType { qtIntersects, qtWithin };
std::vector<BoxIndexEl> query(const BoundingBox&, QueryType qt);
// For testing
size_t size() const;
bool empty() const { return size() == 0; }
void foreach(std::function<void(const BoxIndexEl& el)> fn);
};
}

View File

@ -9,10 +9,12 @@
#include "SLASpatIndex.hpp"
#include "SLABasePool.hpp"
#include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/Model.hpp>
#include <libnest2d/optimizers/nlopt/genetic.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <libslic3r/I18N.hpp>
@ -413,7 +415,7 @@ struct Pillar {
assert(steps > 0);
height = jp(Z) - endp(Z);
if(height > 0) { // Endpoint is below the starting point
if(height > EPSILON) { // Endpoint is below the starting point
// We just create a bridge geometry with the pillar parameters and
// move the data.
@ -528,6 +530,7 @@ struct CompactBridge {
const Vec3d& ep,
const Vec3d& n,
double r,
bool endball = true,
size_t steps = 45)
{
Vec3d startp = sp + r * n;
@ -542,11 +545,13 @@ struct CompactBridge {
auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa);
for(auto& p : upperball.points) p += startp;
auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
for(auto& p : lowerball.points) p += endp;
if(endball) {
auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
for(auto& p : lowerball.points) p += endp;
mesh.merge(lowerball);
}
mesh.merge(upperball);
mesh.merge(lowerball);
}
};
@ -556,28 +561,111 @@ struct Pad {
PoolConfig cfg;
double zlevel = 0;
Pad() {}
Pad() = default;
Pad(const TriangleMesh& object_support_mesh,
const ExPolygons& baseplate,
Pad(const TriangleMesh& support_mesh,
const ExPolygons& modelbase,
double ground_level,
const PoolConfig& pcfg) :
cfg(pcfg),
zlevel(ground_level +
(sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) )
sla::get_pad_fullheight(pcfg) -
sla::get_pad_elevation(pcfg))
{
ExPolygons basep;
cfg.throw_on_cancel();
Polygons basep;
auto &thr = cfg.throw_on_cancel;
// The 0.1f is the layer height with which the mesh is sampled and then
// the layers are unified into one vector of polygons.
base_plate(object_support_mesh, basep,
float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm),
0.1f, pcfg.throw_on_cancel);
thr();
for(auto& bp : baseplate) basep.emplace_back(bp);
// Get a sample for the pad from the support mesh
{
ExPolygons platetmp;
float zstart = float(zlevel);
float zend = zstart + float(get_pad_fullheight(pcfg) + EPSILON);
base_plate(support_mesh, platetmp, grid(zstart, zend, 0.1f), thr);
// We don't need no... holes control...
for (const ExPolygon &bp : platetmp)
basep.emplace_back(std::move(bp.contour));
}
if(pcfg.embed_object) {
// If the zero elevation mode is ON, we need to process the model
// base silhouette. Create the offsetted version and punch the
// breaksticks across its perimeter.
ExPolygons modelbase_offs = modelbase;
if (pcfg.embed_object.object_gap_mm > 0.0)
modelbase_offs
= offset_ex(modelbase_offs,
float(scaled(pcfg.embed_object.object_gap_mm)));
// Create a spatial index of the support silhouette polygons.
// This will be used to check for intersections with the model
// silhouette polygons. If there is no intersection, then a certain
// part of the pad is redundant as it does not host any supports.
BoxIndex bindex;
{
unsigned idx = 0;
for(auto &bp : basep) {
auto bb = bp.bounding_box();
bb.offset(float(scaled(pcfg.min_wall_thickness_mm)));
bindex.insert(bb, idx++);
}
}
// Punching the breaksticks across the offsetted polygon perimeters
ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
for(auto& poly : modelbase_offs) {
std::vector<BoxIndexEl> qres =
bindex.query(poly.contour.bounding_box(),
BoxIndex::qtIntersects);
if (!qres.empty()) {
// The model silhouette polygon 'poly' HAS an intersection
// with the support silhouettes. Include this polygon
// in the pad holes with the breaksticks and merge the
// original (offsetted) version with the rest of the pad
// base plate.
basep.emplace_back(poly.contour);
// The holes of 'poly' will become positive parts of the
// pad, so they has to be checked for intersections as well
// and erased if there is no intersection with the supports
auto it = poly.holes.begin();
while(it != poly.holes.end()) {
if (bindex.query(it->bounding_box(),
BoxIndex::qtIntersects).empty())
it = poly.holes.erase(it);
else
++it;
}
// Punch the breaksticks
sla::breakstick_holes(
poly,
pcfg.embed_object.object_gap_mm, // padding
pcfg.embed_object.stick_stride_mm,
pcfg.embed_object.stick_width_mm,
pcfg.embed_object.stick_penetration_mm);
pad_stickholes.emplace_back(poly);
}
}
create_base_pool(basep, tmesh, pad_stickholes, cfg);
} else {
for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
create_base_pool(basep, tmesh, {}, cfg);
}
create_base_pool(basep, tmesh, cfg);
tmesh.translate(0, 0, float(zlevel));
}
@ -603,7 +691,7 @@ inline Vec2d to_vec2(const Vec3d& v3) {
return {v3(X), v3(Y)};
}
bool operator==(const SpatElement& e1, const SpatElement& e2) {
bool operator==(const PointIndexEl& e1, const PointIndexEl& e2) {
return e1.second == e2.second;
}
@ -620,7 +708,7 @@ ClusteredPoints cluster(const PointSet& points,
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const SpatElement&, const SpatElement&)> predicate,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points);
// This class will hold the support tree meshes with some additional bookkeeping
@ -763,9 +851,9 @@ public:
}
const Pad& create_pad(const TriangleMesh& object_supports,
const ExPolygons& baseplate,
const ExPolygons& modelbase,
const PoolConfig& cfg) {
m_pad = Pad(object_supports, baseplate, ground_level, cfg);
m_pad = Pad(object_supports, modelbase, ground_level, cfg);
return m_pad;
}
@ -808,7 +896,6 @@ public:
merged.merge(bs.mesh);
}
if(m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh
meshcache = TriangleMesh();
@ -819,7 +906,7 @@ public:
// The mesh will be passed by const-pointer to TriangleMeshSlicer,
// which will need this.
meshcache.require_shared_vertices();
if (!meshcache.empty()) meshcache.require_shared_vertices();
// TODO: Is this necessary?
//meshcache.repair();
@ -947,7 +1034,7 @@ class SLASupportTree::Algorithm {
ThrowOnCancel m_thr;
// A spatial index to easily find strong pillars to connect to.
SpatIndex m_pillar_index;
PointIndex m_pillar_index;
inline double ray_mesh_intersect(const Vec3d& s,
const Vec3d& dir)
@ -1149,7 +1236,7 @@ class SLASupportTree::Algorithm {
auto hr = m.query_ray_hit(p + sd*dir, dir);
if(ins_check && hr.is_inside()) {
if(hr.distance() > r + sd) hits[i] = HitResult(0.0);
if(hr.distance() > 2 * r + sd) hits[i] = HitResult(0.0);
else {
// re-cast the ray from the outside of the object
auto hr2 =
@ -1265,8 +1352,11 @@ class SLASupportTree::Algorithm {
// For connecting a head to a nearby pillar.
bool connect_to_nearpillar(const Head& head, long nearpillar_id) {
auto nearpillar = [this, nearpillar_id]() { return m_result.pillar(nearpillar_id); };
if(nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
auto nearpillar = [this, nearpillar_id]() {
return m_result.pillar(nearpillar_id);
};
if (nearpillar().bridges > m_cfg.max_bridges_on_pillar) return false;
Vec3d headjp = head.junction_point();
Vec3d nearjp_u = nearpillar().startpoint();
@ -1337,7 +1427,7 @@ class SLASupportTree::Algorithm {
}
bool search_pillar_and_connect(const Head& head) {
SpatIndex spindex = m_pillar_index;
PointIndex spindex = m_pillar_index;
long nearest_id = -1;
@ -1370,6 +1460,120 @@ class SLASupportTree::Algorithm {
return nearest_id >= 0;
}
// This is a proxy function for pillar creation which will mind the gap
// between the pad and the model bottom in zero elevation mode.
void create_ground_pillar(const Vec3d &jp,
const Vec3d &sourcedir,
double radius,
int head_id = -1)
{
// People were killed for this number (seriously)
static const double SQR2 = std::sqrt(2.0);
static const Vec3d DOWN = {0.0, 0.0, -1.0};
double gndlvl = m_result.ground_level;
Vec3d endp = {jp(X), jp(Y), gndlvl};
double sd = m_cfg.pillar_base_safety_distance_mm;
int pillar_id = -1;
double min_dist = sd + m_cfg.base_radius_mm + EPSILON;
double dist = 0;
bool can_add_base = true;
bool normal_mode = true;
if (m_cfg.object_elevation_mm < EPSILON
&& (dist = std::sqrt(m_mesh.squared_distance(endp))) < min_dist) {
// Get the distance from the mesh. This can be later optimized
// to get the distance in 2D plane because we are dealing with
// the ground level only.
normal_mode = false;
double mv = min_dist - dist;
double azimuth = std::atan2(sourcedir(Y), sourcedir(X));
double sinpolar = std::sin(PI - m_cfg.bridge_slope);
double cospolar = std::cos(PI - m_cfg.bridge_slope);
double cosazm = std::cos(azimuth);
double sinazm = std::sin(azimuth);
auto dir = Vec3d(cosazm * sinpolar, sinazm * sinpolar, cospolar)
.normalized();
using namespace libnest2d::opt;
StopCriteria scr;
scr.stop_score = min_dist;
SubplexOptimizer solver(scr);
auto result = solver.optimize_max(
[this, dir, jp, gndlvl](double mv) {
Vec3d endp = jp + SQR2 * mv * dir;
endp(Z) = gndlvl;
return std::sqrt(m_mesh.squared_distance(endp));
},
initvals(mv), bound(0.0, 2 * min_dist));
mv = std::get<0>(result.optimum);
endp = jp + SQR2 * mv * dir;
Vec3d pgnd = {endp(X), endp(Y), gndlvl};
can_add_base = result.score > min_dist;
double gnd_offs = m_mesh.ground_level_offset();
auto abort_in_shame =
[gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
{
normal_mode = true;
can_add_base = false; // Nothing left to do, hope for the best
endp = {jp(X), jp(Y), gndlvl - gnd_offs };
};
// We have to check if the bridge is feasible.
if (bridge_mesh_intersect(jp, dir, radius) < (endp - jp).norm())
abort_in_shame();
else {
// If the new endpoint is below ground, do not make a pillar
if (endp(Z) < gndlvl)
endp = endp - SQR2 * (gndlvl - endp(Z)) * dir; // back off
else {
auto hit = bridge_mesh_intersect(endp, DOWN, radius);
if (!std::isinf(hit.distance())) abort_in_shame();
Pillar &plr = m_result.add_pillar(endp, pgnd, radius);
if (can_add_base)
plr.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
pillar_id = plr.id;
}
m_result.add_bridge(jp, endp, radius);
m_result.add_junction(endp, radius);
// Add a degenerated pillar and the bridge.
// The degenerate pillar will have zero length and it will
// prevent from queries of head_pillar() to have non-existing
// pillar when the head should have one.
if (head_id >= 0)
m_result.add_pillar(unsigned(head_id), jp, radius);
}
}
if (normal_mode) {
Pillar &plr = head_id >= 0
? m_result.add_pillar(unsigned(head_id),
endp,
radius)
: m_result.add_pillar(jp, endp, radius);
if (can_add_base)
plr.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
pillar_id = plr.id;
}
if(pillar_id >= 0) // Save the pillar endpoint in the spatial index
m_pillar_index.insert(endp, pillar_id);
}
public:
Algorithm(const SupportConfig& config,
@ -1447,9 +1651,9 @@ public:
// (Quaternion::FromTwoVectors) and apply the rotation to the
// arrow head.
double z = n(2);
double r = 1.0; // for normalized vector
double polar = std::acos(z / r);
double z = n(2);
double r = 1.0; // for normalized vector
double polar = std::acos(z / r);
double azimuth = std::atan2(n(1), n(0));
// skip if the tilt is not sane
@ -1473,14 +1677,14 @@ public:
std::cos(polar)).normalized();
// check available distance
double t = pinhead_mesh_intersect(
hp, // touching point
nn, // normal
pin_r,
m_cfg.head_back_radius_mm,
w);
EigenMesh3D::hit_result t
= pinhead_mesh_intersect(hp, // touching point
nn, // normal
pin_r,
m_cfg.head_back_radius_mm,
w);
if(t <= w) {
if(t.distance() <= w) {
// Let's try to optimize this angle, there might be a
// viable normal that doesn't collide with the model
@ -1523,12 +1727,17 @@ public:
// save the verified and corrected normal
m_support_nmls.row(fidx) = nn;
if(t > w) {
// mark the point for needing a head.
m_iheads.emplace_back(fidx);
} else if( polar >= 3*PI/4 ) {
// Headless supports do not tilt like the headed ones so
// the normal should point almost to the ground.
if (t.distance() > w) {
// Check distance from ground, we might have zero elevation.
if (hp(Z) + w * nn(Z) < m_result.ground_level) {
m_iheadless.emplace_back(fidx);
} else {
// mark the point for needing a head.
m_iheads.emplace_back(fidx);
}
} else if (polar >= 3 * PI / 4) {
// Headless supports do not tilt like the headed ones
// so the normal should point almost to the ground.
m_iheadless.emplace_back(fidx);
}
}
@ -1594,16 +1803,22 @@ public:
// from each other in the XY plane to not cross their pillar bases
// These clusters of support points will join in one pillar,
// possibly in their centroid support point.
auto pointfn = [this](unsigned i) {
return m_result.head(i).junction_point();
};
auto predicate = [this](const SpatElement& e1, const SpatElement& e2) {
auto predicate = [this](const PointIndexEl &e1,
const PointIndexEl &e2) {
double d2d = distance(to_2d(e1.first), to_2d(e2.first));
double d3d = distance(e1.first, e2.first);
return d2d < 2 * m_cfg.base_radius_mm &&
d3d < m_cfg.max_bridge_length_mm;
return d2d < 2 * m_cfg.base_radius_mm
&& d3d < m_cfg.max_bridge_length_mm;
};
m_pillar_clusters = cluster(ground_head_indices, pointfn, predicate,
m_pillar_clusters = cluster(ground_head_indices,
pointfn,
predicate,
m_cfg.max_bridges_on_pillar);
}
@ -1615,7 +1830,7 @@ public:
void routing_to_ground()
{
const double pradius = m_cfg.head_back_radius_mm;
const double gndlvl = m_result.ground_level;
// const double gndlvl = m_result.ground_level;
ClusterEl cl_centroids;
cl_centroids.reserve(m_pillar_clusters.size());
@ -1648,13 +1863,8 @@ public:
Head& h = m_result.head(hid);
h.transform();
Vec3d p = h.junction_point(); p(Z) = gndlvl;
auto& plr = m_result.add_pillar(hid, p, h.r_back_mm)
.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
// Save the pillar endpoint and the pillar id in the spatial index
m_pillar_index.insert(plr.endpoint(), unsigned(plr.id));
create_ground_pillar(h.junction_point(), h.dir, h.r_back_mm, h.id);
}
// now we will go through the clusters ones again and connect the
@ -1681,15 +1891,12 @@ public:
!search_pillar_and_connect(sidehead))
{
Vec3d pstart = sidehead.junction_point();
Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
//Vec3d pend = Vec3d{pstart(X), pstart(Y), gndlvl};
// Could not find a pillar, create one
auto& pillar = m_result.add_pillar(unsigned(sidehead.id),
pend, pradius)
.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
// connects to ground, eligible for bridging
m_pillar_index.insert(pend, unsigned(pillar.id));
create_ground_pillar(pstart,
sidehead.dir,
pradius,
sidehead.id);
}
}
}
@ -1718,12 +1925,7 @@ public:
m_result.add_bridge(hjp, endp, head.r_back_mm);
m_result.add_junction(endp, head.r_back_mm);
auto groundp = endp;
groundp(Z) = m_result.ground_level;
auto& newpillar = m_result.add_pillar(endp, groundp, head.r_back_mm)
.add_base(m_cfg.base_height_mm,
m_cfg.base_radius_mm);
m_pillar_index.insert(groundp, unsigned(newpillar.id));
this->create_ground_pillar(endp, dir, head.r_back_mm);
};
std::vector<unsigned> modelpillars;
@ -1884,6 +2086,28 @@ public:
}
}
// Helper function for interconnect_pillars where pairs of already connected
// pillars should be checked for not to be processed again. This can be done
// in O(log) or even constant time with a set or an unordered set of hash
// values uniquely representing a pair of integers. The order of numbers
// within the pair should not matter, it has the same unique hash.
template<class I> static I pairhash(I a, I b)
{
using std::ceil; using std::log2; using std::max; using std::min;
static_assert(std::is_integral<I>::value,
"This function works only for integral types.");
I g = min(a, b), l = max(a, b);
auto bits_g = g ? int(ceil(log2(g))) : 0;
// Assume the hash will fit into the output variable
assert((l ? (ceil(log2(l))) : 0) + bits_g < int(sizeof(I) * CHAR_BIT));
return (l << bits_g) + g;
}
void interconnect_pillars() {
// Now comes the algorithm that connects pillars with each other.
// Ideally every pillar should be connected with at least one of its
@ -1901,44 +2125,50 @@ public:
std::set<unsigned long> pairs;
// A function to connect one pillar with its neighbors. THe number of
// neighbors is given in the configuration. This function if called
// for every pillar in the pillar index. A pair of pillar will not
// be connected multiple times this is ensured by the 'pairs' set which
// remembers the processed pillar pairs
auto cascadefn =
[this, d, &pairs, min_height_ratio, H1] (const SpatElement& el)
[this, d, &pairs, min_height_ratio, H1] (const PointIndexEl& el)
{
Vec3d qp = el.first;
Vec3d qp = el.first; // endpoint of the pillar
const Pillar& pillar = m_result.pillar(el.second);
const Pillar& pillar = m_result.pillar(el.second); // actual pillar
// Get the max number of neighbors a pillar should connect to
unsigned neighbors = m_cfg.pillar_cascade_neighbors;
// connections are enough for one pillar
// connections are already enough for the pillar
if(pillar.links >= neighbors) return;
// Query all remaining points within reach
auto qres = m_pillar_index.query([qp, d](const SpatElement& e){
auto qres = m_pillar_index.query([qp, d](const PointIndexEl& e){
return distance(e.first, qp) < d;
});
// sort the result by distance (have to check if this is needed)
std::sort(qres.begin(), qres.end(),
[qp](const SpatElement& e1, const SpatElement& e2){
[qp](const PointIndexEl& e1, const PointIndexEl& e2){
return distance(e1.first, qp) < distance(e2.first, qp);
});
for(auto& re : qres) {
for(auto& re : qres) { // process the queried neighbors
if(re.second == el.second) continue;
if(re.second == el.second) continue; // Skip self
auto a = el.second, b = re.second;
// I hope that the area of a square is never equal to its
// circumference
auto hashval = 2 * (a + b) + a * b;
// Get unique hash for the given pair (order doesn't matter)
auto hashval = pairhash(a, b);
// Search for the pair amongst the remembered pairs
if(pairs.find(hashval) != pairs.end()) continue;
const Pillar& neighborpillar = m_result.pillars()[re.second];
// this neighbor is occupied
// this neighbor is occupied, skip
if(neighborpillar.links >= neighbors) continue;
if(interconnect(pillar, neighborpillar)) {
@ -1961,46 +2191,78 @@ public:
}
};
// Run the cascade for the pillars in the index
m_pillar_index.foreach(cascadefn);
// We would be done here if we could allow some pillars to not be
// connected with any neighbors. But this might leave the support tree
// unprintable.
//
// The current solution is to insert additional pillars next to these
// lonely pillars. One or even two additional pillar might get inserted
// depending on the length of the lonely pillar.
size_t pillarcount = m_result.pillars().size();
// Again, go through all pillars, this time in the whole support tree
// not just the index.
for(size_t pid = 0; pid < pillarcount; pid++) {
auto pillar = [this, pid]() { return m_result.pillar(pid); };
// Decide how many additional pillars will be needed:
unsigned needpillars = 0;
if(pillar().bridges > m_cfg.max_bridges_on_pillar) needpillars = 3;
else if(pillar().links < 2 && pillar().height > H2) {
if (pillar().bridges > m_cfg.max_bridges_on_pillar)
needpillars = 3;
else if (pillar().links < 2 && pillar().height > H2) {
// Not enough neighbors to support this pillar
needpillars = 2 - pillar().links;
}
else if(pillar().links < 1 && pillar().height > H1) {
} else if (pillar().links < 1 && pillar().height > H1) {
// No neighbors could be found and the pillar is too long.
needpillars = 1;
}
// Search for new pillar locations
bool found = false;
double alpha = 0; // goes to 2Pi
double r = 2 * m_cfg.base_radius_mm;
Vec3d pillarsp = pillar().startpoint();
// Search for new pillar locations:
bool found = false;
double alpha = 0; // goes to 2Pi
double r = 2 * m_cfg.base_radius_mm;
Vec3d pillarsp = pillar().startpoint();
// temp value for starting point detection
Vec3d sp(pillarsp(X), pillarsp(Y), pillarsp(Z) - r);
std::vector<bool> tv(needpillars, false);
std::vector<Vec3d> spts(needpillars);
// A vector of bool for placement feasbility
std::vector<bool> canplace(needpillars, false);
std::vector<Vec3d> spts(needpillars); // vector of starting points
double gnd = m_result.ground_level;
double min_dist = m_cfg.pillar_base_safety_distance_mm +
m_cfg.base_radius_mm + EPSILON;
while(!found && alpha < 2*PI) {
for(unsigned n = 0; n < needpillars; n++) {
double a = alpha + n * PI/3;
Vec3d s = sp;
for (unsigned n = 0;
n < needpillars && (!n || canplace[n - 1]);
n++)
{
double a = alpha + n * PI / 3;
Vec3d s = sp;
s(X) += std::cos(a) * r;
s(Y) += std::sin(a) * r;
spts[n] = s;
// Check the path vertically down
auto hr = bridge_mesh_intersect(s, {0, 0, -1}, pillar().r);
tv[n] = std::isinf(hr.distance());
Vec3d gndsp{s(X), s(Y), gnd};
// If the path is clear, check for pillar base collisions
canplace[n] = std::isinf(hr.distance()) &&
std::sqrt(m_mesh.squared_distance(gndsp)) >
min_dist;
}
found = std::all_of(tv.begin(), tv.end(), [](bool v){return v;});
found = std::all_of(canplace.begin(), canplace.end(),
[](bool v) { return v; });
// 20 angles will be tried...
alpha += 0.1 * PI;
@ -2010,7 +2272,7 @@ public:
newpills.reserve(needpillars);
if(found) for(unsigned n = 0; n < needpillars; n++) {
Vec3d s = spts[n]; double gnd = m_result.ground_level;
Vec3d s = spts[n];
Pillar p(s, Vec3d(s(X), s(Y), gnd), pillar().r);
p.add_base(m_cfg.base_height_mm, m_cfg.base_radius_mm);
@ -2075,9 +2337,13 @@ public:
// This is only for checking
double idist = bridge_mesh_intersect(sph, dir, R, true);
double dist = ray_mesh_intersect(sj, dir);
if (std::isinf(dist))
dist = sph(Z) - m_mesh.ground_level()
+ m_mesh.ground_level_offset();
if(std::isinf(idist) || std::isnan(idist) || idist < 2*R ||
std::isinf(dist) || std::isnan(dist) || dist < 2*R) {
if(std::isnan(idist) || idist < 2*R ||
std::isnan(dist) || dist < 2*R)
{
BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless"
<< " support stick at: "
<< sj.transpose();
@ -2085,7 +2351,7 @@ public:
}
Vec3d ej = sj + (dist + HWIDTH_MM)* dir;
m_result.add_compact_bridge(sp, ej, n, R);
m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
}
}
};
@ -2214,7 +2480,9 @@ bool SLASupportTree::generate(const std::vector<SupportPoint> &support_points,
return pc == ABORT;
}
SLASupportTree::SLASupportTree(): m_impl(new Impl()) {}
SLASupportTree::SLASupportTree(double gnd_lvl): m_impl(new Impl()) {
m_impl->ground_level = gnd_lvl;
}
const TriangleMesh &SLASupportTree::merged_mesh() const
{
@ -2226,7 +2494,7 @@ void SLASupportTree::merged_mesh_with_pad(TriangleMesh &outmesh) const {
outmesh.merge(get_pad());
}
SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) const
{
if(init_layerh < 0) init_layerh = layerh;
auto& stree = get();
@ -2245,36 +2513,31 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
std::vector<ExPolygons> ret;
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
return ret;
}
SlicedSupports SLASupportTree::slice(const std::vector<float> &heights,
std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
float cr) const
{
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this
if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
std::vector<ExPolygons> ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
return ret;
}
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
const TriangleMesh &SLASupportTree::add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const
{
// PoolConfig pcfg;
// pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
// pcfg.min_wall_height_mm = min_wall_height_mm;
// pcfg.max_merge_distance_mm = max_merge_distance_mm;
// pcfg.edge_radius_mm = edge_radius_mm;
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
return m_impl->create_pad(merged_mesh(), modelbase, pcfg).tmesh;
}
const TriangleMesh &SLASupportTree::get_pad() const

View File

@ -24,10 +24,11 @@ class TriangleMesh;
class Model;
class ModelInstance;
class ModelObject;
class Polygon;
class ExPolygon;
using SliceLayer = std::vector<ExPolygon>;
using SlicedSupports = std::vector<SliceLayer>;
using Polygons = std::vector<Polygon>;
using ExPolygons = std::vector<ExPolygon>;
namespace sla {
@ -81,6 +82,10 @@ struct SupportConfig {
// and the model object's bounding box bottom.
double object_elevation_mm = 10;
// The shortest distance between a pillar base perimeter from the model
// body. This is only useful when elevation is set to zero.
double pillar_base_safety_distance_mm = 0.5;
// /////////////////////////////////////////////////////////////////////////
// Compile time configuration values (candidates for runtime)
// /////////////////////////////////////////////////////////////////////////
@ -160,7 +165,7 @@ class SLASupportTree {
public:
SLASupportTree();
SLASupportTree(double ground_level = 0.0);
SLASupportTree(const std::vector<SupportPoint>& pts,
const EigenMesh3D& em,
@ -179,12 +184,17 @@ public:
void merged_mesh_with_pad(TriangleMesh&) const;
/// Get the sliced 2d layers of the support geometry.
SlicedSupports slice(float layerh, float init_layerh = -1.0) const;
std::vector<ExPolygons> slice(float layerh, float init_layerh = -1.0) const;
SlicedSupports slice(const std::vector<float>&, float closing_radius) const;
std::vector<ExPolygons> slice(const std::vector<float> &,
float closing_radius) const;
/// Adding the "pad" (base pool) under the supports
const TriangleMesh& add_pad(const SliceLayer& baseplate,
/// modelbase will be used according to the embed_object flag in PoolConfig.
/// If set, the plate will interpreted as the model's intrinsic pad.
/// Otherwise, the modelbase will be unified with the base plate calculated
/// from the supports.
const TriangleMesh& add_pad(const ExPolygons& modelbase,
const PoolConfig& pcfg) const;
/// Get the pad geometry

View File

@ -29,69 +29,137 @@ namespace sla {
using igl::PI;
/* **************************************************************************
* SpatIndex implementation
* PointIndex implementation
* ************************************************************************** */
class SpatIndex::Impl {
class PointIndex::Impl {
public:
using BoostIndex = boost::geometry::index::rtree< SpatElement,
using BoostIndex = boost::geometry::index::rtree< PointIndexEl,
boost::geometry::index::rstar<16, 4> /* ? */ >;
BoostIndex m_store;
};
SpatIndex::SpatIndex(): m_impl(new Impl()) {}
SpatIndex::~SpatIndex() {}
PointIndex::PointIndex(): m_impl(new Impl()) {}
PointIndex::~PointIndex() {}
SpatIndex::SpatIndex(const SpatIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
SpatIndex::SpatIndex(SpatIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
PointIndex::PointIndex(const PointIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
PointIndex::PointIndex(PointIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
SpatIndex& SpatIndex::operator=(const SpatIndex &cpy)
PointIndex& PointIndex::operator=(const PointIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
SpatIndex& SpatIndex::operator=(SpatIndex &&cpy)
PointIndex& PointIndex::operator=(PointIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
void SpatIndex::insert(const SpatElement &el)
void PointIndex::insert(const PointIndexEl &el)
{
m_impl->m_store.insert(el);
}
bool SpatIndex::remove(const SpatElement& el)
bool PointIndex::remove(const PointIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
std::vector<SpatElement>
SpatIndex::query(std::function<bool(const SpatElement &)> fn)
std::vector<PointIndexEl>
PointIndex::query(std::function<bool(const PointIndexEl &)> fn)
{
namespace bgi = boost::geometry::index;
std::vector<SpatElement> ret;
std::vector<PointIndexEl> ret;
m_impl->m_store.query(bgi::satisfies(fn), std::back_inserter(ret));
return ret;
}
std::vector<SpatElement> SpatIndex::nearest(const Vec3d &el, unsigned k = 1)
std::vector<PointIndexEl> PointIndex::nearest(const Vec3d &el, unsigned k = 1)
{
namespace bgi = boost::geometry::index;
std::vector<SpatElement> ret; ret.reserve(k);
std::vector<PointIndexEl> ret; ret.reserve(k);
m_impl->m_store.query(bgi::nearest(el, k), std::back_inserter(ret));
return ret;
}
size_t SpatIndex::size() const
size_t PointIndex::size() const
{
return m_impl->m_store.size();
}
void SpatIndex::foreach(std::function<void (const SpatElement &)> fn)
void PointIndex::foreach(std::function<void (const PointIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
/* **************************************************************************
* BoxIndex implementation
* ************************************************************************** */
class BoxIndex::Impl {
public:
using BoostIndex = boost::geometry::index::
rtree<BoxIndexEl, boost::geometry::index::rstar<16, 4> /* ? */>;
BoostIndex m_store;
};
BoxIndex::BoxIndex(): m_impl(new Impl()) {}
BoxIndex::~BoxIndex() {}
BoxIndex::BoxIndex(const BoxIndex &cpy): m_impl(new Impl(*cpy.m_impl)) {}
BoxIndex::BoxIndex(BoxIndex&& cpy): m_impl(std::move(cpy.m_impl)) {}
BoxIndex& BoxIndex::operator=(const BoxIndex &cpy)
{
m_impl.reset(new Impl(*cpy.m_impl));
return *this;
}
BoxIndex& BoxIndex::operator=(BoxIndex &&cpy)
{
m_impl.swap(cpy.m_impl);
return *this;
}
void BoxIndex::insert(const BoxIndexEl &el)
{
m_impl->m_store.insert(el);
}
bool BoxIndex::remove(const BoxIndexEl& el)
{
return m_impl->m_store.remove(el) == 1;
}
std::vector<BoxIndexEl> BoxIndex::query(const BoundingBox &qrbb,
BoxIndex::QueryType qt)
{
namespace bgi = boost::geometry::index;
std::vector<BoxIndexEl> ret; ret.reserve(m_impl->m_store.size());
switch (qt) {
case qtIntersects:
m_impl->m_store.query(bgi::intersects(qrbb), std::back_inserter(ret));
break;
case qtWithin:
m_impl->m_store.query(bgi::within(qrbb), std::back_inserter(ret));
}
return ret;
}
size_t BoxIndex::size() const
{
return m_impl->m_store.size();
}
void BoxIndex::foreach(std::function<void (const BoxIndexEl &)> fn)
{
for(auto& el : m_impl->m_store) fn(el);
}
@ -343,12 +411,14 @@ PointSet normals(const PointSet& points,
return ret;
}
namespace bgi = boost::geometry::index;
using Index3D = bgi::rtree< SpatElement, bgi::rstar<16, 4> /* ? */ >;
using Index3D = bgi::rtree< PointIndexEl, bgi::rstar<16, 4> /* ? */ >;
ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
std::function<std::vector<SpatElement>(const Index3D&, const SpatElement&)> qfn)
ClusteredPoints cluster(Index3D &sindex,
unsigned max_points,
std::function<std::vector<PointIndexEl>(
const Index3D &, const PointIndexEl &)> qfn)
{
using Elems = std::vector<SpatElement>;
using Elems = std::vector<PointIndexEl>;
// Recursive function for visiting all the points in a given distance to
// each other
@ -356,8 +426,8 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
[&sindex, &group, max_points, qfn](Elems& pts, Elems& cluster)
{
for(auto& p : pts) {
std::vector<SpatElement> tmp = qfn(sindex, p);
auto cmp = [](const SpatElement& e1, const SpatElement& e2){
std::vector<PointIndexEl> tmp = qfn(sindex, p);
auto cmp = [](const PointIndexEl& e1, const PointIndexEl& e2){
return e1.second < e2.second;
};
@ -401,12 +471,12 @@ ClusteredPoints cluster(Index3D& sindex, unsigned max_points,
}
namespace {
std::vector<SpatElement> distance_queryfn(const Index3D& sindex,
const SpatElement& p,
std::vector<PointIndexEl> distance_queryfn(const Index3D& sindex,
const PointIndexEl& p,
double dist,
unsigned max_points)
{
std::vector<SpatElement> tmp; tmp.reserve(max_points);
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sindex.query(
bgi::nearest(p.first, max_points),
std::back_inserter(tmp)
@ -433,7 +503,7 @@ ClusteredPoints cluster(
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
[dist, max_points](const Index3D& sidx, const SpatElement& p)
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});
@ -443,7 +513,7 @@ ClusteredPoints cluster(
ClusteredPoints cluster(
const std::vector<unsigned>& indices,
std::function<Vec3d(unsigned)> pointfn,
std::function<bool(const SpatElement&, const SpatElement&)> predicate,
std::function<bool(const PointIndexEl&, const PointIndexEl&)> predicate,
unsigned max_points)
{
// A spatial index for querying the nearest points
@ -453,10 +523,10 @@ ClusteredPoints cluster(
for(auto idx : indices) sindex.insert( std::make_pair(pointfn(idx), idx));
return cluster(sindex, max_points,
[max_points, predicate](const Index3D& sidx, const SpatElement& p)
[max_points, predicate](const Index3D& sidx, const PointIndexEl& p)
{
std::vector<SpatElement> tmp; tmp.reserve(max_points);
sidx.query(bgi::satisfies([p, predicate](const SpatElement& e){
std::vector<PointIndexEl> tmp; tmp.reserve(max_points);
sidx.query(bgi::satisfies([p, predicate](const PointIndexEl& e){
return predicate(p, e);
}), std::back_inserter(tmp));
return tmp;
@ -473,7 +543,7 @@ ClusteredPoints cluster(const PointSet& pts, double dist, unsigned max_points)
sindex.insert(std::make_pair(Vec3d(pts.row(i)), unsigned(i)));
return cluster(sindex, max_points,
[dist, max_points](const Index3D& sidx, const SpatElement& p)
[dist, max_points](const Index3D& sidx, const PointIndexEl& p)
{
return distance_queryfn(sidx, p, dist, max_points);
});

View File

@ -31,11 +31,10 @@ using SupportTreePtr = std::unique_ptr<sla::SLASupportTree>;
class SLAPrintObject::SupportData
{
public:
sla::EigenMesh3D emesh; // index-triangle representation
std::vector<sla::SupportPoint>
support_points; // all the support points (manual/auto)
SupportTreePtr support_tree_ptr; // the supports
SlicedSupports support_slices; // sliced supports
sla::EigenMesh3D emesh; // index-triangle representation
std::vector<sla::SupportPoint> support_points; // all the support points (manual/auto)
SupportTreePtr support_tree_ptr; // the supports
std::vector<ExPolygons> support_slices; // sliced supports
inline SupportData(const TriangleMesh &trmesh) : emesh(trmesh) {}
};
@ -212,8 +211,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
Moved,
Deleted,
};
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id;
ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {}
ObjectID id;
Status status;
// Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
@ -316,9 +315,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
print_object(print_object),
trafo(print_object->trafo()),
status(status) {}
PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {}
// ID of the ModelObject & PrintObject
ModelID id;
ObjectID id;
// Pointer to the old PrintObject
SLAPrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
@ -368,7 +367,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
// Synchronize Object's config.
bool object_config_changed = model_object.config != model_object_new.config;
if (object_config_changed)
model_object.config = model_object_new.config;
static_cast<DynamicPrintConfig&>(model_object.config) = static_cast<const DynamicPrintConfig&>(model_object_new.config);
if (! object_diff.empty() || object_config_changed) {
SLAPrintObjectConfig new_config = m_default_object_config;
normalize_and_apply_config(new_config, model_object.config);
@ -441,12 +440,10 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
update_apply_status(this->invalidate_all_steps());
m_objects = print_objects_new;
// Delete the PrintObjects marked as Unknown or Deleted.
bool deleted_objects = false;
for (auto &pos : print_object_status)
if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) {
update_apply_status(pos.print_object->invalidate_all_steps());
delete pos.print_object;
deleted_objects = true;
}
if (new_objects)
update_apply_status(false);
@ -473,7 +470,7 @@ void SLAPrint::set_task(const TaskParams &params)
int n_object_steps = int(params.to_object_step) + 1;
if (n_object_steps == 0)
n_object_steps = (int)slaposCount;
n_object_steps = int(slaposCount);
if (params.single_model_object.valid()) {
// Find the print object to be processed with priority.
@ -488,7 +485,7 @@ void SLAPrint::set_task(const TaskParams &params)
// Find out whether the priority print object is being currently processed.
bool running = false;
for (int istep = 0; istep < n_object_steps; ++ istep) {
if (! print_object->m_stepmask[istep])
if (! print_object->m_stepmask[size_t(istep)])
// Step was skipped, cancel.
break;
if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) {
@ -504,7 +501,7 @@ void SLAPrint::set_task(const TaskParams &params)
if (params.single_model_instance_only) {
// Suppress all the steps of other instances.
for (SLAPrintObject *po : m_objects)
for (int istep = 0; istep < (int)slaposCount; ++ istep)
for (size_t istep = 0; istep < slaposCount; ++ istep)
po->m_stepmask[istep] = false;
} else if (! running) {
// Swap the print objects, so that the selected print_object is first in the row.
@ -514,15 +511,15 @@ void SLAPrint::set_task(const TaskParams &params)
}
// and set the steps for the current object.
for (int istep = 0; istep < n_object_steps; ++ istep)
print_object->m_stepmask[istep] = true;
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
print_object->m_stepmask[istep] = false;
print_object->m_stepmask[size_t(istep)] = true;
for (int istep = n_object_steps; istep < int(slaposCount); ++ istep)
print_object->m_stepmask[size_t(istep)] = false;
} else {
// Slicing all objects.
bool running = false;
for (SLAPrintObject *print_object : m_objects)
for (int istep = 0; istep < n_object_steps; ++ istep) {
if (! print_object->m_stepmask[istep]) {
if (! print_object->m_stepmask[size_t(istep)]) {
// Step may have been skipped. Restart.
goto loop_end;
}
@ -538,8 +535,8 @@ void SLAPrint::set_task(const TaskParams &params)
this->call_cancel_callback();
for (SLAPrintObject *po : m_objects) {
for (int istep = 0; istep < n_object_steps; ++ istep)
po->m_stepmask[istep] = true;
for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep)
po->m_stepmask[size_t(istep)] = true;
for (auto istep = size_t(n_object_steps); istep < slaposCount; ++ istep)
po->m_stepmask[istep] = false;
}
}
@ -557,9 +554,9 @@ void SLAPrint::set_task(const TaskParams &params)
void SLAPrint::finalize()
{
for (SLAPrintObject *po : m_objects)
for (int istep = 0; istep < (int)slaposCount; ++ istep)
for (size_t istep = 0; istep < slaposCount; ++ istep)
po->m_stepmask[istep] = true;
for (int istep = 0; istep < (int)slapsCount; ++ istep)
for (size_t istep = 0; istep < slapsCount; ++ istep)
m_stepmask[istep] = true;
}
@ -597,21 +594,48 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat();
scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat();
scfg.base_height_mm = c.support_base_height.getFloat();
scfg.pillar_base_safety_distance_mm =
c.support_base_safety_distance.getFloat() < EPSILON ?
scfg.safety_distance_mm : c.support_base_safety_distance.getFloat();
return scfg;
}
sla::PoolConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) {
sla::PoolConfig::EmbedObject ret;
ret.enabled = c.support_object_elevation.getFloat() <= EPSILON &&
c.pad_enable.getBool() && c.supports_enable.getBool();
if(ret.enabled) {
ret.object_gap_mm = c.pad_object_gap.getFloat();
ret.stick_width_mm = c.pad_object_connector_width.getFloat();
ret.stick_stride_mm = c.pad_object_connector_stride.getFloat();
ret.stick_penetration_mm = c.pad_object_connector_penetration
.getFloat();
}
return ret;
}
sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
sla::PoolConfig pcfg;
pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat();
pcfg.wall_slope = c.pad_wall_slope.getFloat();
pcfg.edge_radius_mm = c.pad_edge_radius.getFloat();
pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0;
// We do not support radius for now
pcfg.edge_radius_mm = 0.0; //c.pad_edge_radius.getFloat();
pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat();
pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
// set builtin pad implicitly ON
pcfg.embed_object = builtin_pad_cfg(c);
return pcfg;
}
}
std::string SLAPrint::validate() const
@ -635,8 +659,20 @@ std::string SLAPrint::validate() const
2 * cfg.head_back_radius_mm -
cfg.head_penetration_mm;
if(supports_en && pinhead_width > cfg.object_elevation_mm)
double elv = cfg.object_elevation_mm;
if(supports_en && elv > EPSILON && elv < pinhead_width )
return L("Elevation is too low for object.");
sla::PoolConfig::EmbedObject builtinpad = builtin_pad_cfg(po->config());
if(supports_en && builtinpad.enabled &&
cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
return L(
"The endings of the support pillars will be deployed on the "
"gap between the object and the pad. 'Support base safety "
"distance' has to be greater than the 'Pad object gap' "
"parameter to avoid this.");
}
}
return "";
@ -751,18 +787,27 @@ void SLAPrint::process()
mit->set_model_slice_idx(po, id); ++mit;
}
if(po.m_config.supports_enable.getBool() ||
po.m_config.pad_enable.getBool())
{
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
}
};
// In this step we check the slices, identify island and cover them with
// support points. Then we sprinkle the rest of the mesh.
auto support_points = [this, ostepd](SLAPrintObject& po) {
const ModelObject& mo = *po.m_model_object;
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
// If supports are disabled, we can skip the model scan.
if(!po.m_config.supports_enable.getBool()) return;
if (!po.m_supportdata)
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()));
const ModelObject& mo = *po.m_model_object;
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
@ -771,7 +816,7 @@ void SLAPrint::process()
// into the backend cache.
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
// Hypotetical use of the slice index:
// Hypothetical use of the slice index:
// auto bb = po.transformed_mesh().bounding_box();
// auto range = po.get_slice_records(bb.min(Z));
// std::vector<float> heights; heights.reserve(range.size());
@ -820,13 +865,40 @@ void SLAPrint::process()
BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
<< po.m_supportdata->support_points.size();
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass the update status to GLGizmoSlaSupports
m_report_status(*this, -1, L("Generating support points"), SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
// the update status to GLGizmoSlaSupports
m_report_status(*this,
-1,
L("Generating support points"),
SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
}
else {
// There are either some points on the front-end, or the user removed them on purpose. No calculation will be done.
// There are either some points on the front-end, or the user
// removed them on purpose. No calculation will be done.
po.m_supportdata->support_points = po.transformed_support_points();
}
// If the zero elevation mode is engaged, we have to filter out all the
// points that are on the bottom of the object
if (po.config().support_object_elevation.getFloat() <= EPSILON) {
double gnd = po.m_supportdata->emesh.ground_level();
auto & pts = po.m_supportdata->support_points;
double tolerance = po.config().pad_enable.getBool()
? po.m_config.pad_wall_thickness.getFloat()
: po.m_config.support_base_height.getFloat();
// get iterator to the reorganized vector end
auto endit = std::remove_if(
pts.begin(),
pts.end(),
[tolerance, gnd](const sla::SupportPoint &sp) {
double diff = std::abs(gnd - double(sp.pos(Z)));
return diff <= tolerance;
});
// erase all elements after the new end
pts.erase(endit, pts.end());
}
};
// In this step we create the supports
@ -834,9 +906,18 @@ void SLAPrint::process()
{
if(!po.m_supportdata) return;
sla::PoolConfig pcfg = make_pool_config(po.m_config);
if (pcfg.embed_object)
po.m_supportdata->emesh.ground_level_offset(
pcfg.min_wall_thickness_mm);
if(!po.m_config.supports_enable.getBool()) {
// Generate empty support tree. It can still host a pad
po.m_supportdata->support_tree_ptr.reset(new SLASupportTree());
po.m_supportdata->support_tree_ptr.reset(
new SLASupportTree(po.m_supportdata->emesh.ground_level()));
return;
}
@ -888,40 +969,33 @@ void SLAPrint::process()
// and before the supports had been sliced. (or the slicing has to be
// repeated)
if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) {
BOOST_LOG_TRIVIAL(error) << "Uninitialized support data at "
<< "pad creation.";
return;
}
if(po.m_config.pad_enable.getBool())
{
double wt = po.m_config.pad_wall_thickness.getFloat();
double h = po.m_config.pad_wall_height.getFloat();
double md = po.m_config.pad_max_merge_distance.getFloat();
// Radius is disabled for now...
double er = 0; // po.m_config.pad_edge_radius.getFloat();
double tilt = po.m_config.pad_wall_slope.getFloat() * PI / 180.0;
double lh = po.m_config.layer_height.getFloat();
double elevation = po.m_config.support_object_elevation.getFloat();
if(!po.m_config.supports_enable.getBool()) elevation = 0;
sla::PoolConfig pcfg(wt, h, md, er, tilt);
// Get the distilled pad configuration from the config
sla::PoolConfig pcfg = make_pool_config(po.m_config);
ExPolygons bp;
double pad_h = sla::get_pad_fullheight(pcfg);
auto&& trmesh = po.transformed_mesh();
ExPolygons bp; // This will store the base plate of the pad.
double pad_h = sla::get_pad_fullheight(pcfg);
const TriangleMesh &trmesh = po.transformed_mesh();
// This call can get pretty time consuming
auto thrfn = [this](){ throw_if_canceled(); };
if(elevation < pad_h) {
// we have to count with the model geometry for the base plate
sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn);
if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
// No support (thus no elevation) or zero elevation mode
// we sometimes call it "builtin pad" is enabled so we will
// get a sample from the bottom of the mesh and use it for pad
// creation.
sla::base_plate(trmesh,
bp,
float(pad_h),
float(po.m_config.layer_height.getFloat()),
thrfn);
}
pcfg.throw_on_cancel = thrfn;
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
} else {
} else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) {
po.m_supportdata->support_tree_ptr->remove_pad();
}
@ -938,6 +1012,11 @@ void SLAPrint::process()
if(sd) sd->support_slices.clear();
// Don't bother if no supports and no pad is present.
if (!po.m_config.supports_enable.getBool() &&
!po.m_config.pad_enable.getBool())
return;
if(sd && sd->support_tree_ptr) {
std::vector<float> heights; heights.reserve(po.m_slice_index.size());
@ -964,7 +1043,8 @@ void SLAPrint::process()
po.m_slice_index[i].set_support_slice_idx(po, i);
}
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
// status to the 3D preview to load the SLA slices.
m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
};
@ -1536,14 +1616,17 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
return true;
}
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
Inherited(print, model_object),
m_stepmask(slaposCount, true),
m_transformed_rmesh( [this](TriangleMesh& obj){
obj = m_model_object->raw_mesh(); obj.transform(m_trafo); obj.require_shared_vertices();
})
{
}
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
: Inherited(print, model_object)
, m_stepmask(slaposCount, true)
, m_transformed_rmesh([this](TriangleMesh &obj) {
obj = m_model_object->raw_mesh();
if (!obj.empty()) {
obj.transform(m_trafo);
obj.require_shared_vertices();
}
})
{}
SLAPrintObject::~SLAPrintObject() {}
@ -1582,13 +1665,19 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "support_critical_angle"
|| opt_key == "support_max_bridge_length"
|| opt_key == "support_max_pillar_link_distance"
|| opt_key == "support_base_safety_distance"
) {
steps.emplace_back(slaposSupportTree);
} else if (
opt_key == "pad_wall_height"
|| opt_key == "pad_max_merge_distance"
|| opt_key == "pad_wall_slope"
|| opt_key == "pad_edge_radius") {
|| opt_key == "pad_edge_radius"
|| opt_key == "pad_object_gap"
|| opt_key == "pad_object_connector_stride"
|| opt_key == "pad_object_connector_width"
|| opt_key == "pad_object_connector_penetration"
) {
steps.emplace_back(slaposBasePool);
} else {
// All keys should be covered.
@ -1629,17 +1718,16 @@ bool SLAPrintObject::invalidate_all_steps()
}
double SLAPrintObject::get_elevation() const {
bool se = m_config.supports_enable.getBool();
double ret = se? m_config.support_object_elevation.getFloat() : 0;
bool en = m_config.supports_enable.getBool();
double ret = en ? m_config.support_object_elevation.getFloat() : 0.;
// if the pad is enabled, then half of the pad height is its base plate
if(m_config.pad_enable.getBool()) {
// Normally the elevation for the pad itself would be the thickness of
// its walls but currently it is half of its thickness. Whatever it
// will be in the future, we provide the config to the get_pad_elevation
// method and we will have the correct value
sla::PoolConfig pcfg = make_pool_config(m_config);
ret += sla::get_pad_elevation(pcfg);
if(!pcfg.embed_object) ret += sla::get_pad_elevation(pcfg);
}
return ret;
@ -1647,14 +1735,14 @@ double SLAPrintObject::get_elevation() const {
double SLAPrintObject::get_current_elevation() const
{
bool se = m_config.supports_enable.getBool();
bool has_supports = is_step_done(slaposSupportTree);
bool has_pad = is_step_done(slaposBasePool);
if(!has_supports && !has_pad)
return 0;
else if(has_supports && !has_pad)
return se ? m_config.support_object_elevation.getFloat() : 0;
else if(has_supports && !has_pad) {
return m_config.support_object_elevation.getFloat();
}
return get_elevation();
}
@ -1682,13 +1770,14 @@ namespace { // dummy empty static containers for return values in some methods
const std::vector<ExPolygons> EMPTY_SLICES;
const TriangleMesh EMPTY_MESH;
const ExPolygons EMPTY_SLICE;
const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS;
}
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
{
return m_supportdata->support_points;
return m_supportdata? m_supportdata->support_points : EMPTY_SUPPORT_POINTS;
}
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const

View File

@ -54,10 +54,10 @@ public:
bool is_left_handed() const { return m_left_handed; }
struct Instance {
Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
Instance(ObjectID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {}
bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; }
// ID of the corresponding ModelInstance.
ModelID instance_id;
ObjectID instance_id;
// Slic3r::Point objects in scaled G-code coordinates
Point shift;
// Rotation along the Z axis, in radians.

View File

@ -153,24 +153,33 @@ SlicingParameters SlicingParameters::create_from_config(
return params;
}
// Convert layer_height_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges)
{
std::vector<std::pair<t_layer_height_range, coordf_t>> out;
out.reserve(config_ranges.size());
for (const auto &kvp : config_ranges)
out.emplace_back(kvp.first, kvp.second.option("layer_height")->getFloat());
return out;
}
// Convert layer_config_ranges to layer_height_profile. Both are referenced to z=0, meaning the raft layers are not accounted for
// in the height profile and the printed object may be lifted by the raft thickness at the time of the G-code generation.
std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges)
const t_layer_config_ranges &layer_config_ranges) // #ys_FIXME_experiment
{
// 1) If there are any height ranges, trim one by the other to make them non-overlapping. Insert the 1st layer if fixed.
std::vector<std::pair<t_layer_height_range,coordf_t>> ranges_non_overlapping;
ranges_non_overlapping.reserve(layer_height_ranges.size() * 4);
ranges_non_overlapping.reserve(layer_config_ranges.size() * 4); // #ys_FIXME_experiment
if (slicing_params.first_object_layer_height_fixed())
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
t_layer_height_range(0., slicing_params.first_object_layer_height),
slicing_params.first_object_layer_height));
// The height ranges are sorted lexicographically by low / high layer boundaries.
for (t_layer_height_ranges::const_iterator it_range = layer_height_ranges.begin(); it_range != layer_height_ranges.end(); ++ it_range) {
for (t_layer_config_ranges::const_iterator it_range = layer_config_ranges.begin(); it_range != layer_config_ranges.end(); ++ it_range) {
coordf_t lo = it_range->first.first;
coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height());
coordf_t height = it_range->second;
coordf_t height = it_range->second.option("layer_height")->getFloat();
if (! ranges_non_overlapping.empty())
// Trim current low with the last high.
lo = std::max(lo, ranges_non_overlapping.back().first.second);
@ -187,7 +196,6 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
coordf_t hi = it_range->first.second;
coordf_t height = it_range->second;
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (lo > last_z + EPSILON) {
// Insert a step of normal layer height.
layer_height_profile.push_back(last_z);
@ -203,7 +211,6 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
}
coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2];
coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1];
if (last_z < slicing_params.object_print_z_height()) {
// Insert a step of normal layer height up to the object top.
layer_height_profile.push_back(last_z);
@ -219,7 +226,7 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges,
const t_layer_config_ranges & /* layer_config_ranges */,
const ModelVolumePtrs &volumes)
{
// 1) Initialize the SlicingAdaptive class with the object meshes.
@ -245,7 +252,6 @@ std::vector<coordf_t> layer_height_profile_adaptive(
}
coordf_t slice_z = slicing_params.first_object_layer_height;
coordf_t height = slicing_params.first_object_layer_height;
coordf_t cusp_height = 0.;
int current_facet = 0;
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
height = 999;
@ -401,7 +407,6 @@ void adjust_layer_height_profile(
}
// Adjust height by layer_thickness_delta.
coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
coordf_t height_new = height;
switch (action) {
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:

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