mirror of
https://github.com/FULU-Foundation/OrcaSlicer-bambulab.git
synced 2026-07-11 08:04:25 +00:00
Merge branch 'master' into lm_warnings
This commit is contained in:
+1
-1
@@ -46,7 +46,7 @@ BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 75
|
||||
ColumnLimit: 78
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
|
||||
@@ -355,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
|
||||
|
||||
Vendored
+8
-5
@@ -36,10 +36,11 @@ set(DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/destdir" CACHE PATH "Destination direct
|
||||
option(DEP_DEBUG "Build debug variants (only applicable on Windows)" ON)
|
||||
option(DEP_WX_STABLE "Build against wxWidgets stable 3.0 as opposed to default 3.1 (Linux only)" OFF)
|
||||
|
||||
# IGL static library in release mode produces 50MB binary. On the build server, it should be
|
||||
# disabled and used in header-only mode. On developer machines, it can be enabled to speed
|
||||
# up conpilation and suppress warnings coming from IGL.
|
||||
option(DEP_BUILD_IGL_STATIC "Build IGL as a static library. Might cause link errors and increase binary size." OFF)
|
||||
# On developer machines, it can be enabled to speed up compilation and suppress warnings coming from IGL.
|
||||
# FIXME:
|
||||
# Enabling this option is not safe. IGL will compile itself with its own version of Eigen while
|
||||
# Slic3r compiles with a different version which will cause runtime errors.
|
||||
# option(DEP_BUILD_IGL_STATIC "Build IGL as a static library. Might cause link errors and increase binary size." OFF)
|
||||
|
||||
message(STATUS "PrusaSlicer deps DESTDIR: ${DESTDIR}")
|
||||
message(STATUS "PrusaSlicer deps debug build: ${DEP_DEBUG}")
|
||||
@@ -88,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
|
||||
@@ -102,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()
|
||||
|
||||
Vendored
+11
-1
@@ -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"
|
||||
@@ -54,7 +64,7 @@ ExternalProject_Add(dep_libigl
|
||||
-DLIBIGL_BUILD_PYTHON=OFF
|
||||
-DLIBIGL_BUILD_TESTS=OFF
|
||||
-DLIBIGL_BUILD_TUTORIALS=OFF
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=${DEP_BUILD_IGL_STATIC}
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=OFF #${DEP_BUILD_IGL_STATIC}
|
||||
-DLIBIGL_WITHOUT_COPYLEFT=OFF
|
||||
-DLIBIGL_WITH_CGAL=OFF
|
||||
-DLIBIGL_WITH_COMISO=OFF
|
||||
|
||||
Vendored
+15
-1
@@ -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"
|
||||
@@ -264,7 +278,7 @@ ExternalProject_Add(dep_libigl
|
||||
-DLIBIGL_BUILD_PYTHON=OFF
|
||||
-DLIBIGL_BUILD_TESTS=OFF
|
||||
-DLIBIGL_BUILD_TUTORIALS=OFF
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=${DEP_BUILD_IGL_STATIC}
|
||||
-DLIBIGL_USE_STATIC_LIBRARY=OFF #${DEP_BUILD_IGL_STATIC}
|
||||
-DLIBIGL_WITHOUT_COPYLEFT=OFF
|
||||
-DLIBIGL_WITH_CGAL=OFF
|
||||
-DLIBIGL_WITH_COMISO=OFF
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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;
|
||||
+44
-38
@@ -146,60 +146,66 @@ sub mesh {
|
||||
}
|
||||
|
||||
sub model {
|
||||
my ($model_name, %params) = @_;
|
||||
my ($model_names, %params) = @_;
|
||||
$model_names = [ $model_names ] if ! ref($model_names);
|
||||
|
||||
my $input_file = "${model_name}.stl";
|
||||
my $mesh = mesh($model_name, %params);
|
||||
# $mesh->write_ascii("out/$input_file");
|
||||
|
||||
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 |
@@ -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 |
@@ -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 |
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 465 B |
@@ -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 |
@@ -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])];
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
+1
-1
@@ -579,7 +579,7 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
|
||||
#endif /* SLIC3R_GUI */
|
||||
<< std::endl
|
||||
<< "https://github.com/prusa3d/PrusaSlicer" << std::endl << std::endl
|
||||
<< "Usage: slic3r [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
|
||||
<< "Usage: prusa-slicer [ ACTIONS ] [ TRANSFORM ] [ OPTIONS ] [ file.stl ... ]" << std::endl
|
||||
<< std::endl
|
||||
<< "Actions:" << std::endl;
|
||||
cli_actions_config_def.print_cli_help(boost::nowide::cout, false);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+12
-12
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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] == '>');
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 \"\"");
|
||||
|
||||
@@ -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 \"\"");
|
||||
|
||||
+22
-21
@@ -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;
|
||||
@@ -740,7 +741,7 @@ static int srec2b(char * infile, FILE * inf,
|
||||
}
|
||||
if (nextaddr+srec.reclen > maxaddr)
|
||||
maxaddr = nextaddr+srec.reclen;
|
||||
reccount++;
|
||||
reccount++;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
+6
-6
@@ -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;
|
||||
}
|
||||
|
||||
@@ -941,7 +941,7 @@ insert_ln ( LIST * l, LISTNODE * ln, void * data_ptr )
|
||||
|
|
||||
| Insert data before the nth item in the list.
|
||||
-----------------------------------------------------------------*/
|
||||
int
|
||||
int
|
||||
lins_n ( LISTID lid, void * data_ptr, unsigned int n )
|
||||
{
|
||||
int i;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,8 +1088,8 @@ 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;
|
||||
avrdude_message(MSG_INFO, "%s: stk500_set_sck_period(): p = %.1f us too small, using %.1f us\n",
|
||||
@@ -1099,7 +1099,7 @@ static int stk500_set_sck_period(PROGRAMMER * pgm, double v)
|
||||
avrdude_message(MSG_INFO, "%s: stk500_set_sck_period(): p = %.1f us too large, using %.1f us\n",
|
||||
progname, v / 1e-6, dur * min / 1e-6);
|
||||
}
|
||||
|
||||
|
||||
return stk500_setparm(pgm, Parm_STK_SCK_DURATION, dur);
|
||||
}
|
||||
|
||||
|
||||
+110
-95
@@ -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, ¤t_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) {
|
||||
|
||||
+13
-13
@@ -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) {
|
||||
|
||||
@@ -10,5 +10,5 @@ if(libigl_FOUND)
|
||||
target_link_libraries(libigl INTERFACE igl::core)
|
||||
else()
|
||||
message(STATUS "IGL NOT found, using bundled version...")
|
||||
target_include_directories(libigl INTERFACE SYSTEM ${LIBDIR}/libigl)
|
||||
target_include_directories(libigl SYSTEM BEFORE INTERFACE ${LIBDIR}/libigl)
|
||||
endif()
|
||||
|
||||
+260
-292
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+121
-41
@@ -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)
|
||||
|
||||
+162
-32
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()) {
|
||||
@@ -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)
|
||||
@@ -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";
|
||||
|
||||
@@ -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?
|
||||
@@ -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,7 +1003,7 @@ 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())
|
||||
|
||||
+176
-96
@@ -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)));
|
||||
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
|
||||
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;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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 : {
|
||||
@@ -2556,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;
|
||||
|
||||
@@ -2734,38 +2794,58 @@ 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;
|
||||
if (m_writer.extruder() != nullptr) {
|
||||
// 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()->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", 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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -468,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)
|
||||
{
|
||||
@@ -493,17 +493,20 @@ 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+361
-101
@@ -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_
|
||||
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
+145
-9
@@ -1,11 +1,16 @@
|
||||
#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"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@@ -239,13 +244,144 @@ template<class C> bool all_of(const C &container)
|
||||
});
|
||||
}
|
||||
|
||||
template<class X, class Y> inline X ceil_i(X x, Y y)
|
||||
template<class T> struct remove_cvref
|
||||
{
|
||||
static_assert(std::is_integral<X>::value &&
|
||||
std::is_integral<Y>::value && sizeof(X) >= sizeof(Y),
|
||||
"");
|
||||
using type =
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
};
|
||||
|
||||
return (x % y) ? x / y + 1 : x / y;
|
||||
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;
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
// Type safe conversions to and from scaled and unscaled coordinates
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// A meta-predicate which is true for integers wider than or equal to coord_t
|
||||
template<class I> struct is_scaled_coord
|
||||
{
|
||||
static const SLIC3R_CONSTEXPR bool value =
|
||||
std::is_integral<I>::value &&
|
||||
std::numeric_limits<I>::digits >=
|
||||
std::numeric_limits<coord_t>::digits;
|
||||
};
|
||||
|
||||
// Meta predicates for floating, 'scaled coord' and generic arithmetic types
|
||||
template<class T>
|
||||
using FloatingOnly = enable_if_t<std::is_floating_point<T>::value, T>;
|
||||
|
||||
template<class T>
|
||||
using ScaledCoordOnly = enable_if_t<is_scaled_coord<T>::value, T>;
|
||||
|
||||
template<class T>
|
||||
using ArithmeticOnly = enable_if_t<std::is_arithmetic<T>::value, T>;
|
||||
|
||||
// A shorter form for a generic Eigen vector which is widely used in PrusaSlicer
|
||||
template<class T, int N>
|
||||
using EigenVec = Eigen::Matrix<T, N, 1, Eigen::DontAlign>;
|
||||
|
||||
// Semantics are the following:
|
||||
// Upscaling (scaled()): only from floating point types (or Vec) to either
|
||||
// floating point or integer 'scaled coord' coordinates.
|
||||
// Downscaling (unscaled()): from arithmetic types (or Vec) to either
|
||||
// floating point only
|
||||
|
||||
// Conversion definition from unscaled to floating point scaled
|
||||
template<class Tout,
|
||||
class Tin,
|
||||
class = FloatingOnly<Tin>,
|
||||
class = FloatingOnly<Tout>>
|
||||
inline SLIC3R_CONSTEXPR Tout scaled(const Tin &v) SLIC3R_NOEXCEPT
|
||||
{
|
||||
return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
// Conversion definition from unscaled to integer 'scaled coord'.
|
||||
// TODO: is the rounding necessary ? Here it is to show that it can be different
|
||||
// but it does not have to be. Using std::round means loosing noexcept and
|
||||
// constexpr modifiers
|
||||
template<class Tout = coord_t, class Tin, class = FloatingOnly<Tin>>
|
||||
inline SLIC3R_CONSTEXPR ScaledCoordOnly<Tout> scaled(const Tin &v) SLIC3R_NOEXCEPT
|
||||
{
|
||||
//return static_cast<Tout>(std::round(v / SCALING_FACTOR));
|
||||
return static_cast<Tout>(v / static_cast<Tin>(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
// Conversion for Eigen vectors (N dimensional points)
|
||||
template<class Tout = coord_t, class Tin, int N, class = FloatingOnly<Tin>>
|
||||
inline EigenVec<ArithmeticOnly<Tout>, N> scaled(const EigenVec<Tin, N> &v)
|
||||
{
|
||||
return (v / SCALING_FACTOR).template cast<Tout>();
|
||||
}
|
||||
|
||||
// Conversion from arithmetic scaled type to floating point unscaled
|
||||
template<class Tout = double,
|
||||
class Tin,
|
||||
class = ArithmeticOnly<Tin>,
|
||||
class = FloatingOnly<Tout>>
|
||||
inline SLIC3R_CONSTEXPR Tout unscaled(const Tin &v) SLIC3R_NOEXCEPT
|
||||
{
|
||||
return static_cast<Tout>(v * static_cast<Tout>(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
// Unscaling for Eigen vectors. Input base type can be arithmetic, output base
|
||||
// type can only be floating point.
|
||||
template<class Tout = double,
|
||||
class Tin,
|
||||
int N,
|
||||
class = ArithmeticOnly<Tin>,
|
||||
class = FloatingOnly<Tout>>
|
||||
inline SLIC3R_CONSTEXPR EigenVec<Tout, N> unscaled(
|
||||
const EigenVec<Tin, N> &v) SLIC3R_NOEXCEPT
|
||||
{
|
||||
return v.template cast<Tout>() * SCALING_FACTOR;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -39,7 +39,7 @@ template<> inline Slic3r::Points& contour(Slic3r::Polygon& sh) { return sh.point
|
||||
template<> inline const Slic3r::Points& contour(const Slic3r::Polygon& sh) { return sh.points; }
|
||||
|
||||
template<> Slic3r::Points::iterator begin(Slic3r::Points& pts, const PathTag&) { return pts.begin();}
|
||||
template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.begin(); }
|
||||
template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.cbegin(); }
|
||||
template<> Slic3r::Points::iterator end(Slic3r::Points& pts, const PathTag&) { return pts.end();}
|
||||
template<> Slic3r::Points::const_iterator cend(const Slic3r::Points& pts, const PathTag&) { return pts.cend(); }
|
||||
|
||||
@@ -71,62 +71,67 @@ using Rational = boost::rational<__int128>;
|
||||
|
||||
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
|
||||
{
|
||||
const Polygon& chull = pc == pcConvex ? p : libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<Polygon, Unit, Rational>(chull);
|
||||
|
||||
m_right = box.right_extent();
|
||||
m_bottom = box.bottom_extent();
|
||||
m_axis = box.axis();
|
||||
const Polygon &chull = pc == pcConvex ? p :
|
||||
libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<Polygon, Unit, Rational>(chull);
|
||||
|
||||
m_right = libnest2d::cast<long double>(box.right_extent());
|
||||
m_bottom = libnest2d::cast<long double>(box.bottom_extent());
|
||||
m_axis = box.axis();
|
||||
}
|
||||
|
||||
MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
|
||||
{
|
||||
const ExPolygon& chull = pc == pcConvex ? p : libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<ExPolygon, Unit, Rational>(chull);
|
||||
|
||||
m_right = box.right_extent();
|
||||
m_bottom = box.bottom_extent();
|
||||
m_axis = box.axis();
|
||||
const ExPolygon &chull = pc == pcConvex ? p :
|
||||
libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<ExPolygon, Unit, Rational>(chull);
|
||||
|
||||
m_right = libnest2d::cast<long double>(box.right_extent());
|
||||
m_bottom = libnest2d::cast<long double>(box.bottom_extent());
|
||||
m_axis = box.axis();
|
||||
}
|
||||
|
||||
MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc)
|
||||
{
|
||||
const Points& chull = pc == pcConvex ? pts : libnest2d::sl::convexHull(pts);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<Points, Unit, Rational>(chull);
|
||||
|
||||
m_right = box.right_extent();
|
||||
m_bottom = box.bottom_extent();
|
||||
m_axis = box.axis();
|
||||
const Points &chull = pc == pcConvex ? pts :
|
||||
libnest2d::sl::convexHull(pts);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<Points, Unit, Rational>(chull);
|
||||
|
||||
m_right = libnest2d::cast<long double>(box.right_extent());
|
||||
m_bottom = libnest2d::cast<long double>(box.bottom_extent());
|
||||
m_axis = box.axis();
|
||||
}
|
||||
|
||||
double MinAreaBoundigBox::angle_to_X() const
|
||||
{
|
||||
double ret = std::atan2(m_axis.y(), m_axis.x());
|
||||
auto s = std::signbit(ret);
|
||||
if(s) ret += 2 * PI;
|
||||
auto s = std::signbit(ret);
|
||||
if (s) ret += 2 * PI;
|
||||
return -ret;
|
||||
}
|
||||
|
||||
long double MinAreaBoundigBox::width() const
|
||||
{
|
||||
return std::abs(m_bottom) / std::sqrt(libnest2d::pl::magnsq<Point, long double>(m_axis));
|
||||
return std::abs(m_bottom) /
|
||||
std::sqrt(libnest2d::pl::magnsq<Point, long double>(m_axis));
|
||||
}
|
||||
|
||||
long double MinAreaBoundigBox::height() const
|
||||
{
|
||||
return std::abs(m_right) / std::sqrt(libnest2d::pl::magnsq<Point, long double>(m_axis));
|
||||
return std::abs(m_right) /
|
||||
std::sqrt(libnest2d::pl::magnsq<Point, long double>(m_axis));
|
||||
}
|
||||
|
||||
long double MinAreaBoundigBox::area() const
|
||||
{
|
||||
long double asq = libnest2d::pl::magnsq<Point, long double>(m_axis);
|
||||
return m_bottom * m_right / asq;
|
||||
return m_bottom * m_right / asq;
|
||||
}
|
||||
|
||||
void remove_collinear_points(Polygon &p)
|
||||
@@ -138,5 +143,4 @@ void remove_collinear_points(ExPolygon &p)
|
||||
{
|
||||
p = libnest2d::removeCollinearPoints<ExPolygon>(p, Unit(0));
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
+73
-36
@@ -22,21 +22,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);
|
||||
@@ -87,6 +72,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;
|
||||
@@ -221,7 +219,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;
|
||||
@@ -622,14 +620,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;
|
||||
@@ -658,14 +660,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);
|
||||
@@ -1070,11 +1075,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();
|
||||
@@ -1191,13 +1196,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
|
||||
@@ -1272,7 +1283,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);
|
||||
@@ -1564,8 +1578,10 @@ void ModelVolume::center_geometry_after_creation()
|
||||
Vec3d shift = this->mesh().bounding_box().center();
|
||||
if (!shift.isApprox(Vec3d::Zero()))
|
||||
{
|
||||
m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
if (m_mesh)
|
||||
const_cast<TriangleMesh*>(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
if (m_convex_hull)
|
||||
const_cast<TriangleMesh*>(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
translate(shift);
|
||||
}
|
||||
}
|
||||
@@ -1718,10 +1734,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)
|
||||
@@ -1843,7 +1859,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) {
|
||||
@@ -1865,21 +1881,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)
|
||||
@@ -1890,10 +1911,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());
|
||||
}
|
||||
@@ -1904,9 +1928,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
|
||||
+220
-161
@@ -2,19 +2,20 @@
|
||||
#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 <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "Geometry.hpp"
|
||||
#include <libslic3r/SLA/SLACommon.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@@ -26,6 +27,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;
|
||||
@@ -35,74 +68,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. */ \
|
||||
@@ -110,52 +82,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;
|
||||
@@ -165,9 +147,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
|
||||
@@ -178,9 +159,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;
|
||||
@@ -264,7 +245,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;
|
||||
@@ -288,31 +269,53 @@ public:
|
||||
|
||||
std::string get_export_filename() const;
|
||||
|
||||
// Get full stl statistics for all object's meshes
|
||||
// Get full stl statistics for all object's meshes
|
||||
stl_stats get_object_stl_stats() const;
|
||||
// Get count of errors in the mesh( or all object's meshes, if volume index isn't defined)
|
||||
// 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;
|
||||
@@ -323,7 +326,25 @@ private:
|
||||
mutable BoundingBoxf3 m_raw_bounding_box;
|
||||
mutable bool m_raw_bounding_box_valid;
|
||||
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
|
||||
mutable bool m_raw_mesh_bounding_box_valid;
|
||||
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.
|
||||
@@ -337,20 +358,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; };
|
||||
@@ -385,7 +406,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!
|
||||
@@ -431,66 +452,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
|
||||
@@ -556,6 +599,7 @@ public:
|
||||
protected:
|
||||
friend class Print;
|
||||
friend class SLAPrint;
|
||||
friend class Model;
|
||||
friend class ModelObject;
|
||||
|
||||
explicit ModelInstance(const ModelInstance &rhs) = default;
|
||||
@@ -566,15 +610,22 @@ private:
|
||||
ModelObject* object;
|
||||
|
||||
// Constructor, which assigns a new unique ID.
|
||||
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
|
||||
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) {}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// The print bed content.
|
||||
@@ -582,7 +633,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;
|
||||
|
||||
@@ -594,17 +645,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);
|
||||
@@ -615,7 +666,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();
|
||||
|
||||
@@ -633,24 +684,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);
|
||||
@@ -662,11 +713,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.
|
||||
@@ -686,6 +745,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_ */
|
||||
|
||||
@@ -62,10 +62,10 @@ std::string toString(const Model& model, bool holes = true) {
|
||||
objinst->transform_mesh(&tmpmesh);
|
||||
ExPolygons expolys = tmpmesh.horizontal_projection();
|
||||
for(auto& expoly_complex : expolys) {
|
||||
|
||||
auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
|
||||
|
||||
ExPolygons tmp = expoly_complex.simplify(scaled<double>(1.));
|
||||
if(tmp.empty()) continue;
|
||||
auto expoly = tmp.front();
|
||||
ExPolygon expoly = tmp.front();
|
||||
expoly.contour.make_clockwise();
|
||||
for(auto& h : expoly.holes) h.make_counter_clockwise();
|
||||
|
||||
@@ -610,7 +610,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model,
|
||||
|
||||
if(tolerance > EPSILON) {
|
||||
Polygons pp { p };
|
||||
pp = p.simplify(double(scaled(tolerance)));
|
||||
pp = p.simplify(scaled<double>(tolerance));
|
||||
if (!pp.empty()) p = pp.front();
|
||||
}
|
||||
|
||||
@@ -633,8 +633,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model,
|
||||
if(item.vertexCount() > 3) {
|
||||
item.rotation(Geometry::rotation_diff_z(rotation0, objinst->get_rotation()));
|
||||
item.translation({
|
||||
ClipperLib::cInt(objinst->get_offset(X)/SCALING_FACTOR),
|
||||
ClipperLib::cInt(objinst->get_offset(Y)/SCALING_FACTOR)
|
||||
scaled<ClipperLib::cInt>(objinst->get_offset(X)),
|
||||
scaled<ClipperLib::cInt>(objinst->get_offset(Y))
|
||||
});
|
||||
ret.emplace_back(objinst, item);
|
||||
}
|
||||
@@ -658,8 +658,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model,
|
||||
Item item(std::move(pn));
|
||||
item.rotation(wti.rotation),
|
||||
item.translation({
|
||||
ClipperLib::cInt(wti.pos(0)/SCALING_FACTOR),
|
||||
ClipperLib::cInt(wti.pos(1)/SCALING_FACTOR)
|
||||
scaled<ClipperLib::cInt>(wti.pos(0)),
|
||||
scaled<ClipperLib::cInt>(wti.pos(1))
|
||||
});
|
||||
ret.emplace_back(nullptr, item);
|
||||
}
|
||||
@@ -822,7 +822,9 @@ bool arrange(Model &model, // The model with the geometries
|
||||
|
||||
auto& cfn = stopcondition;
|
||||
|
||||
coord_t md = ceil_i(min_obj_distance, 2) - SCALED_EPSILON;
|
||||
// Integer ceiling the min distance from the bed perimeters
|
||||
coord_t md = min_obj_distance - SCALED_EPSILON;
|
||||
md = (md % 2) ? md / 2 + 1 : md / 2;
|
||||
|
||||
auto binbb = Box({libnest2d::Coord{bbb.min(0)} - md,
|
||||
libnest2d::Coord{bbb.min(1)} - md},
|
||||
@@ -916,7 +918,9 @@ void find_new_position(const Model &model,
|
||||
|
||||
BoundingBox bbb(bed);
|
||||
|
||||
coord_t md = ceil_i(min_obj_distance, 2) - SCALED_EPSILON;
|
||||
// Integer ceiling the min distance from the bed perimeters
|
||||
coord_t md = min_obj_distance - SCALED_EPSILON;
|
||||
md = (md % 2) ? md / 2 + 1 : md / 2;
|
||||
|
||||
auto binbb = Box({libnest2d::Coord{bbb.min(0)} - md,
|
||||
libnest2d::Coord{bbb.min(1)} - md},
|
||||
|
||||
@@ -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)
|
||||
@@ -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_ */
|
||||
+19
-2
@@ -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
|
||||
|
||||
+197
-317
@@ -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 ®ion = *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 ®ion_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 ((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);
|
||||
}
|
||||
++ 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 © : print_object->m_copies) {
|
||||
for (const Point © : 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;
|
||||
@@ -1434,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
|
||||
);
|
||||
}
|
||||
@@ -1456,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
|
||||
);
|
||||
}
|
||||
@@ -1633,20 +1511,20 @@ void Print::_make_skirt()
|
||||
|
||||
// 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 (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;
|
||||
@@ -1657,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);
|
||||
@@ -1729,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;
|
||||
@@ -1785,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;
|
||||
}
|
||||
}
|
||||
@@ -1793,12 +1670,13 @@ 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),
|
||||
float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
|
||||
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
|
||||
float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging),
|
||||
m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes,
|
||||
m_wipe_tower_data.tool_ordering.first_extruder());
|
||||
|
||||
@@ -1807,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)
|
||||
@@ -1833,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;
|
||||
}
|
||||
|
||||
+18
-16
@@ -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
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -366,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;
|
||||
@@ -404,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);
|
||||
@@ -1782,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 "
|
||||
@@ -2037,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;
|
||||
@@ -2501,6 +2507,19 @@ void PrintConfigDef::init_sla_params()
|
||||
def->min = 0;
|
||||
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");
|
||||
@@ -2534,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
|
||||
@@ -2620,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)
|
||||
@@ -3191,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)
|
||||
|
||||
@@ -983,6 +983,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*/;
|
||||
@@ -996,7 +999,7 @@ public:
|
||||
// The elevation in Z direction upwards. This is the space between the pad
|
||||
// and the model object's bounding box bottom. Units in mm.
|
||||
ConfigOptionFloat support_object_elevation /*= 5.0*/;
|
||||
|
||||
|
||||
/////// Following options influence automatic support points placement:
|
||||
ConfigOptionInt support_points_density_relative;
|
||||
ConfigOptionFloat support_points_minimal_distance;
|
||||
@@ -1021,6 +1024,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
|
||||
|
||||
+245
-91
@@ -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).
|
||||
@@ -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
|
||||
@@ -1351,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);
|
||||
@@ -1372,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);
|
||||
}
|
||||
|
||||
@@ -1427,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;
|
||||
}
|
||||
@@ -1486,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;
|
||||
}
|
||||
@@ -1511,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)
|
||||
@@ -1538,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(
|
||||
@@ -1600,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;
|
||||
@@ -1616,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]));
|
||||
@@ -1749,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()) {
|
||||
@@ -1825,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.
|
||||
@@ -2116,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));
|
||||
}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
#include "SLABoostAdapter.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#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 {
|
||||
|
||||
@@ -183,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;
|
||||
@@ -202,11 +204,13 @@ void offset(ExPolygon& sh, coord_t distance) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto jointype = edgerounding? jtRound : jtMiter;
|
||||
|
||||
ClipperOffset offs;
|
||||
offs.ArcTolerance = 0.01*scaled(1.0);
|
||||
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
|
||||
@@ -236,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;
|
||||
@@ -306,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.
|
||||
@@ -351,7 +509,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||
double x2 = xx*xx;
|
||||
double stepy = std::sqrt(r2 - x2);
|
||||
|
||||
offset(ob, s*scaled(xx));
|
||||
offset(ob, s * scaled(xx));
|
||||
wh = ceilheight_mm - radius_mm + stepy;
|
||||
|
||||
Contour3D pwalls;
|
||||
@@ -375,7 +533,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
||||
double xx = radius_mm - i*stepx;
|
||||
double x2 = xx*xx;
|
||||
double stepy = std::sqrt(r2 - x2);
|
||||
offset(ob, s*scaled(xx));
|
||||
offset(ob, s * scaled(xx));
|
||||
wh = ceilheight_mm - radius_mm - stepy;
|
||||
|
||||
Contour3D pwalls;
|
||||
@@ -425,41 +583,38 @@ 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();
|
||||
|
||||
const double max_dist = scaled(max_dist_mm);
|
||||
|
||||
ExPolygons punion = unify(polys); // could be redundant
|
||||
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.
|
||||
Point cc = centroid(centroids);
|
||||
@@ -469,30 +624,37 @@ 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]
|
||||
[¢roids, &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(max_dist_mm);
|
||||
|
||||
ExPolygon& expo = punion[idx++];
|
||||
BoundingBox querybb(expo);
|
||||
|
||||
querybb.offset(max_dist);
|
||||
|
||||
Point& ct = centroids[idx];
|
||||
|
||||
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);
|
||||
|
||||
Point d(coord_t(scaled(1.)*nx), coord_t(scaled(1.)*ny));
|
||||
Point d(scaled(nx), scaled(ny));
|
||||
ctour.emplace_back(c + Point( -y(d), x(d) ));
|
||||
ctour.emplace_back(c + Point( y(d), -x(d) ));
|
||||
offset(r, scaled(1.));
|
||||
@@ -506,42 +668,55 @@ 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);
|
||||
|
||||
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)));
|
||||
if (mesh.empty()) return;
|
||||
// m.require_shared_vertices(); // TriangleMeshSlicer needs this
|
||||
TriangleMeshSlicer slicer(&mesh);
|
||||
|
||||
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();
|
||||
|
||||
|
||||
// Now we have to unify all slice layers which can be an expensive operation
|
||||
// so we will try to simplify the polygons
|
||||
ExPolygons tmp; tmp.reserve(count);
|
||||
for(ExPolygons& o : out)
|
||||
for(ExPolygon& e : o) {
|
||||
auto&& exss = e.simplify(scaled(0.1));
|
||||
auto&& exss = e.simplify(scaled<double>(0.1));
|
||||
for(ExPolygon& ep : exss) tmp.emplace_back(std::move(ep));
|
||||
}
|
||||
|
||||
|
||||
ExPolygons utmp = unify(tmp);
|
||||
|
||||
|
||||
for(auto& o : utmp) {
|
||||
auto&& smp = o.simplify(scaled(0.1));
|
||||
auto&& smp = o.simplify(scaled<double>(0.1));
|
||||
output.insert(output.end(), smp.begin(), smp.end());
|
||||
}
|
||||
}
|
||||
|
||||
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:
|
||||
@@ -556,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;
|
||||
@@ -576,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.
|
||||
@@ -658,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
|
||||
@@ -672,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));
|
||||
@@ -686,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)
|
||||
{
|
||||
|
||||
|
||||
@@ -697,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)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -541,12 +544,14 @@ struct CompactBridge {
|
||||
double fa = 2*PI/steps;
|
||||
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)) )
|
||||
zlevel(ground_level +
|
||||
sla::get_pad_fullheight(pcfg) -
|
||||
sla::get_pad_elevation(pcfg))
|
||||
{
|
||||
ExPolygons basep;
|
||||
cfg.throw_on_cancel();
|
||||
Polygons basep;
|
||||
auto &thr = cfg.throw_on_cancel;
|
||||
|
||||
thr();
|
||||
|
||||
// Get a sample for the pad from the support mesh
|
||||
{
|
||||
ExPolygons platetmp;
|
||||
|
||||
// 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);
|
||||
float zstart = float(zlevel);
|
||||
float zend = zstart + float(get_pad_fullheight(pcfg) + EPSILON);
|
||||
|
||||
for(auto& bp : baseplate) basep.emplace_back(bp);
|
||||
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 =
|
||||
@@ -1264,9 +1351,12 @@ 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;
|
||||
|
||||
@@ -1369,6 +1459,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:
|
||||
|
||||
@@ -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;
|
||||
@@ -1883,6 +2085,28 @@ public:
|
||||
m_pillar_index.insert(pillar.endpoint(), pillid);
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -1900,45 +2124,51 @@ public:
|
||||
double min_height_ratio = 0.5;
|
||||
|
||||
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;
|
||||
|
||||
const Pillar& pillar = m_result.pillar(el.second);
|
||||
Vec3d qp = el.first; // endpoint of the pillar
|
||||
|
||||
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)) {
|
||||
@@ -1960,47 +2190,79 @@ public:
|
||||
if(pillar.links >= neighbors) break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -80,6 +81,10 @@ struct SupportConfig {
|
||||
// The elevation in Z direction upwards. This is the space between the pad
|
||||
// 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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
+182
-100
@@ -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);
|
||||
@@ -471,7 +470,7 @@ void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
|
||||
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.
|
||||
@@ -486,7 +485,7 @@ void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
// 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))) {
|
||||
@@ -502,7 +501,7 @@ void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
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.
|
||||
@@ -512,15 +511,15 @@ void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
@@ -536,8 +535,8 @@ void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -555,9 +554,9 @@ void SLAPrint::set_task(const TaskParams ¶ms)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -595,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
|
||||
@@ -632,9 +658,21 @@ std::string SLAPrint::validate() const
|
||||
cfg.head_width_mm +
|
||||
2 * cfg.head_back_radius_mm -
|
||||
cfg.head_penetration_mm;
|
||||
|
||||
double elv = cfg.object_elevation_mm;
|
||||
|
||||
if(supports_en && pinhead_width > 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 "";
|
||||
@@ -666,7 +704,7 @@ void SLAPrint::process()
|
||||
double ilhd = m_material_config.initial_layer_height.getFloat();
|
||||
auto ilh = float(ilhd);
|
||||
|
||||
auto ilhs = scaled(ilhd);
|
||||
coord_t ilhs = scaled(ilhd);
|
||||
const size_t objcount = m_objects.size();
|
||||
|
||||
static const unsigned min_objstatus = 0; // where the per object operations start
|
||||
@@ -692,17 +730,15 @@ void SLAPrint::process()
|
||||
|
||||
// We need to prepare the slice index...
|
||||
|
||||
double lhd = m_objects.front()->m_config.layer_height.getFloat();
|
||||
float lh = float(lhd);
|
||||
auto lhs = scaled(lhd);
|
||||
|
||||
auto &&bb3d = mesh.bounding_box();
|
||||
double minZ = bb3d.min(Z) - po.get_elevation();
|
||||
double maxZ = bb3d.max(Z);
|
||||
auto minZf = float(minZ);
|
||||
|
||||
auto minZs = scaled(minZ);
|
||||
auto maxZs = scaled(maxZ);
|
||||
double lhd = m_objects.front()->m_config.layer_height.getFloat();
|
||||
float lh = float(lhd);
|
||||
coord_t lhs = scaled(lhd);
|
||||
auto && bb3d = mesh.bounding_box();
|
||||
double minZ = bb3d.min(Z) - po.get_elevation();
|
||||
double maxZ = bb3d.max(Z);
|
||||
auto minZf = float(minZ);
|
||||
coord_t minZs = scaled(minZ);
|
||||
coord_t maxZs = scaled(maxZ);
|
||||
|
||||
po.m_slice_index.clear();
|
||||
|
||||
@@ -720,8 +756,9 @@ void SLAPrint::process()
|
||||
|
||||
if(slindex_it == po.m_slice_index.end())
|
||||
//TRN To be shown at the status bar on SLA slicing error.
|
||||
throw std::runtime_error(L("Slicing had to be stopped "
|
||||
"due to an internal error."));
|
||||
throw std::runtime_error(
|
||||
L("Slicing had to be stopped due to an internal error: "
|
||||
"Inconsistent slice index."));
|
||||
|
||||
po.m_model_height_levels.clear();
|
||||
po.m_model_height_levels.reserve(po.m_slice_index.size());
|
||||
@@ -750,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();
|
||||
|
||||
@@ -770,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());
|
||||
@@ -819,23 +865,59 @@ 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
|
||||
auto support_tree = [this, ostepd](SLAPrintObject& po)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -857,7 +939,7 @@ void SLAPrint::process()
|
||||
|
||||
ctl.stopcondition = [this](){ return canceled(); };
|
||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
|
||||
po.m_supportdata->support_tree_ptr.reset(
|
||||
new SLASupportTree(po.m_supportdata->support_points,
|
||||
po.m_supportdata->emesh, scfg, ctl));
|
||||
@@ -887,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();
|
||||
}
|
||||
|
||||
@@ -937,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());
|
||||
@@ -963,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);
|
||||
};
|
||||
|
||||
@@ -1011,9 +1092,6 @@ void SLAPrint::process()
|
||||
using ClipperPolygons = std::vector<ClipperPolygon>;
|
||||
namespace sl = libnest2d::shapelike; // For algorithms
|
||||
|
||||
// If the raster has vertical orientation, we will flip the coordinates
|
||||
// bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
|
||||
|
||||
// Set up custom union and diff functions for clipper polygons
|
||||
auto polyunion = [] (const ClipperPolygons& subjects)
|
||||
{
|
||||
@@ -1064,8 +1142,8 @@ void SLAPrint::process()
|
||||
|
||||
const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20]
|
||||
|
||||
const double width = scaled(m_printer_config.display_width.getFloat());
|
||||
const double height = scaled(m_printer_config.display_height.getFloat());
|
||||
const auto width = scaled<double>(m_printer_config.display_width.getFloat());
|
||||
const auto height = scaled<double>(m_printer_config.display_height.getFloat());
|
||||
const double display_area = width*height;
|
||||
|
||||
// get polygons for all instances in the object
|
||||
@@ -1121,11 +1199,6 @@ void SLAPrint::process()
|
||||
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
||||
instances[i].shift(Y)});
|
||||
|
||||
// if (flpXY) {
|
||||
// for(auto& p : poly.Contour) std::swap(p.X, p.Y);
|
||||
// for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
|
||||
// }
|
||||
|
||||
polygons.emplace_back(std::move(poly));
|
||||
}
|
||||
}
|
||||
@@ -1543,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() {}
|
||||
|
||||
@@ -1589,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.
|
||||
@@ -1636,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;
|
||||
@@ -1654,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();
|
||||
}
|
||||
@@ -1689,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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
@@ -217,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.
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Utils.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
@@ -128,15 +130,17 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters
|
||||
}
|
||||
|
||||
typedef std::pair<coordf_t,coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range,coordf_t> t_layer_height_ranges;
|
||||
typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
|
||||
|
||||
extern std::vector<std::pair<t_layer_height_range, coordf_t>> layer_height_ranges(const t_layer_config_ranges &config_ranges);
|
||||
|
||||
extern 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);
|
||||
|
||||
extern 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);
|
||||
|
||||
|
||||
@@ -171,4 +175,9 @@ extern int generate_layer_height_texture(
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
namespace cereal
|
||||
{
|
||||
template<class Archive> void serialize(Archive& archive, Slic3r::t_layer_height_range &lhr) { archive(lhr.first, lhr.second); }
|
||||
}
|
||||
|
||||
#endif /* slic3r_Slicing_hpp_ */
|
||||
|
||||
@@ -829,7 +829,7 @@ namespace SupportMaterialInternal {
|
||||
assert(expansion_scaled >= 0.f);
|
||||
for (const ExtrusionPath &ep : loop.paths)
|
||||
if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
|
||||
float exp = 0.5f * scale_(ep.width) + expansion_scaled;
|
||||
float exp = 0.5f * (float)scale_(ep.width) + expansion_scaled;
|
||||
if (ep.is_closed()) {
|
||||
if (ep.size() >= 3) {
|
||||
// This is a complete loop.
|
||||
@@ -2214,7 +2214,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
|
||||
// Expand the bases of the support columns in the 1st layer.
|
||||
columns_base->polygons = diff(
|
||||
offset(columns_base->polygons, inflate_factor_1st_layer),
|
||||
offset(m_object->layers().front()->slices.expolygons, scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(m_object->layers().front()->slices.expolygons, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
if (contacts != nullptr)
|
||||
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
|
||||
}
|
||||
@@ -3226,7 +3226,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
// TODO: use brim ordering algorithm
|
||||
Polygons to_infill_polygons = to_polygons(to_infill);
|
||||
// TODO: use offset2_ex()
|
||||
to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing()));
|
||||
to_infill = offset_ex(to_infill, - 0.4f * float(flow.scaled_spacing()));
|
||||
extrusion_entities_append_paths(
|
||||
base_layer.extrusions,
|
||||
to_polylines(std::move(to_infill_polygons)),
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#define ENABLE_RENDER_STATISTICS 0
|
||||
// Shows an imgui dialog with camera related data
|
||||
#define ENABLE_CAMERA_STATISTICS 0
|
||||
// Render the picking pass instead of the main scene (use [T] key to toggle between regular rendering and picking pass only rendering)
|
||||
#define ENABLE_RENDER_PICKING_PASS 0
|
||||
|
||||
|
||||
//====================
|
||||
@@ -39,13 +41,4 @@
|
||||
#define ENABLE_TEXTURES_FROM_SVG (1 && ENABLE_1_42_0_ALPHA7)
|
||||
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha8 techs
|
||||
//====================
|
||||
#define ENABLE_1_42_0_ALPHA8 1
|
||||
|
||||
// Toolbars and Gizmos use icons imported from svg files
|
||||
#define ENABLE_SVG_ICONS (1 && ENABLE_1_42_0_ALPHA8 && ENABLE_TEXTURES_FROM_SVG)
|
||||
|
||||
|
||||
#endif // _technologies_h_
|
||||
|
||||
@@ -547,9 +547,9 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
#if REALfloat
|
||||
qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
|
||||
#else
|
||||
src_vertices.reserve(this->its.vertices() * 3);
|
||||
src_vertices.reserve(this->its.vertices.size() * 3);
|
||||
// We will now fill the vector with input points for computation:
|
||||
for (const stl_vertex &v : ths->its.vertices.size())
|
||||
for (const stl_vertex &v : this->its.vertices)
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
src_vertices.emplace_back(v(i));
|
||||
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");
|
||||
|
||||
@@ -195,4 +195,24 @@ TriangleMesh make_sphere(double rho, double fa=(2*PI/360));
|
||||
|
||||
}
|
||||
|
||||
// Serialization through the Cereal library
|
||||
#include <cereal/access.hpp>
|
||||
namespace cereal {
|
||||
template <class Archive> struct specialize<Archive, Slic3r::TriangleMesh, cereal::specialization::non_member_load_save> {};
|
||||
template<class Archive> void load(Archive &archive, Slic3r::TriangleMesh &mesh) {
|
||||
stl_file &stl = mesh.stl;
|
||||
stl.stats.type = inmemory;
|
||||
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
|
||||
stl_allocate(&stl);
|
||||
archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
|
||||
stl_get_size(&stl);
|
||||
mesh.repair();
|
||||
}
|
||||
template<class Archive> void save(Archive &archive, const Slic3r::TriangleMesh &mesh) {
|
||||
const stl_file& stl = mesh.stl;
|
||||
archive(stl.stats.number_of_facets, stl.stats.original_num_facets);
|
||||
archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -167,7 +167,7 @@ template<class T> size_t next_highest_power_of_2(T v,
|
||||
extern std::string xml_escape(std::string text);
|
||||
|
||||
|
||||
#if defined __GNUC__ & __GNUC__ < 5
|
||||
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
|
||||
// Older GCCs don't have std::is_trivially_copyable
|
||||
// cf. https://gcc.gnu.org/onlinedocs/gcc-4.9.4/libstdc++/manual/manual/status.html#status.iso.2011
|
||||
#warning "GCC version < 5, faking std::is_trivially_copyable"
|
||||
@@ -182,7 +182,7 @@ class ScopeGuard
|
||||
public:
|
||||
typedef std::function<void()> Closure;
|
||||
private:
|
||||
bool committed;
|
||||
// bool committed;
|
||||
Closure closure;
|
||||
|
||||
public:
|
||||
|
||||
@@ -61,20 +61,6 @@ typedef double coordf_t;
|
||||
#define SLIC3R_NOEXCEPT noexcept
|
||||
#endif
|
||||
|
||||
template<class Tf> inline SLIC3R_CONSTEXPR coord_t scaled(Tf val)
|
||||
{
|
||||
static_assert (std::is_floating_point<Tf>::value, "Floating point only");
|
||||
return coord_t(val / Tf(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
template<class Tf = double> inline SLIC3R_CONSTEXPR Tf unscaled(coord_t val)
|
||||
{
|
||||
static_assert (std::is_floating_point<Tf>::value, "Floating point only");
|
||||
return Tf(val * Tf(SCALING_FACTOR));
|
||||
}
|
||||
|
||||
inline SLIC3R_CONSTEXPR float unscaledf(coord_t val) { return unscaled<float>(val); }
|
||||
|
||||
inline std::string debug_out_path(const char *name, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
|
||||
@@ -100,7 +100,10 @@
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
#include <Eigen/Dense>
|
||||
#include <Eigen/Geometry>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include <cereal/access.hpp>
|
||||
#include <cereal/types/base_class.hpp>
|
||||
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
||||
+11
-6
@@ -22,6 +22,10 @@
|
||||
static const size_t MAX_SIZE = sizeof(char) * 255;
|
||||
static const int MAX_SAFE_INT = (unsigned int) -1 >> 1;
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Define comparison operators, storing the
|
||||
* ASCII code per each symbol in hexadecimal notation.
|
||||
@@ -50,8 +54,8 @@ strcut (char *str, int begin, int len) {
|
||||
|
||||
if((int)l < 0 || (int)l > MAX_SAFE_INT) return -1;
|
||||
|
||||
if (len < 0) len = l - begin + 1;
|
||||
if (begin + len > (int)l) len = l - begin;
|
||||
if (len < 0) len = (int)l - begin + 1;
|
||||
if (begin + len > (int)l) len = (int)l - begin;
|
||||
memmove(str + begin, str + begin + len, l - len + 1 - begin);
|
||||
|
||||
return len;
|
||||
@@ -104,7 +108,7 @@ parse_int (const char *s) {
|
||||
static char *
|
||||
parse_slice (char *buf, char sep) {
|
||||
char *pr, *part;
|
||||
int plen;
|
||||
size_t plen;
|
||||
|
||||
/* Find separator in buf */
|
||||
pr = strchr(buf, sep);
|
||||
@@ -210,8 +214,9 @@ semver_parse_version (const char *str, semver_t *ver) {
|
||||
static int
|
||||
compare_prerelease (char *x, char *y) {
|
||||
char *lastx, *lasty, *xptr, *yptr, *endptr;
|
||||
int xlen, ylen, xisnum, yisnum, xnum, ynum;
|
||||
int xn, yn, min, res;
|
||||
size_t xlen, ylen, xn, yn, min;
|
||||
int xisnum, yisnum, xnum, ynum;
|
||||
int res;
|
||||
if (x == NULL && y == NULL) return 0;
|
||||
if (y == NULL && x) return -1;
|
||||
if (x == NULL && y) return 1;
|
||||
@@ -572,7 +577,7 @@ semver_clean (char *s) {
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (contains(s[i], VALID_CHARS, mlen) == 0) {
|
||||
res = strcut(s, i, 1);
|
||||
res = strcut(s, (int)i, 1);
|
||||
if(res == -1) return -1;
|
||||
--len; --i;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/GUI_ObjectManipulation.hpp
|
||||
GUI/GUI_ObjectSettings.cpp
|
||||
GUI/GUI_ObjectSettings.hpp
|
||||
GUI/GUI_ObjectLayers.cpp
|
||||
GUI/GUI_ObjectLayers.hpp
|
||||
GUI/LambdaObjectDialog.cpp
|
||||
GUI/LambdaObjectDialog.hpp
|
||||
GUI/Tab.cpp
|
||||
@@ -146,6 +148,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/PresetUpdater.hpp
|
||||
Utils/Time.cpp
|
||||
Utils/Time.hpp
|
||||
Utils/UndoRedo.cpp
|
||||
Utils/UndoRedo.hpp
|
||||
Utils/HexFile.cpp
|
||||
Utils/HexFile.hpp
|
||||
)
|
||||
@@ -159,7 +163,7 @@ endif ()
|
||||
|
||||
add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude imgui ${GLEW_LIBRARIES})
|
||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES})
|
||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||
endif ()
|
||||
|
||||
+14
-15
@@ -274,8 +274,8 @@ void Bed3D::Axes::render_axis(double length) const
|
||||
|
||||
Bed3D::Bed3D()
|
||||
: m_type(Custom)
|
||||
, m_requires_canvas_update(false)
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
, m_requires_canvas_update(false)
|
||||
, m_vbo_id(0)
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
, m_scale_factor(1.0f)
|
||||
@@ -330,12 +330,11 @@ Point Bed3D::point_projection(const Point& point) const
|
||||
}
|
||||
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const
|
||||
void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
EType type = useVBOs ? m_type : Custom;
|
||||
switch (type)
|
||||
switch (m_type)
|
||||
{
|
||||
case MK2:
|
||||
{
|
||||
@@ -361,7 +360,7 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_fa
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const
|
||||
void Bed3D::render(float theta, float scale_factor) const
|
||||
{
|
||||
m_scale_factor = scale_factor;
|
||||
|
||||
@@ -372,17 +371,17 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_fa
|
||||
{
|
||||
case MK2:
|
||||
{
|
||||
render_prusa(canvas, "mk2", theta, useVBOs);
|
||||
render_prusa("mk2", theta);
|
||||
break;
|
||||
}
|
||||
case MK3:
|
||||
{
|
||||
render_prusa(canvas, "mk3", theta, useVBOs);
|
||||
render_prusa("mk3", theta);
|
||||
break;
|
||||
}
|
||||
case SL1:
|
||||
{
|
||||
render_prusa(canvas, "sl1", theta, useVBOs);
|
||||
render_prusa("sl1", theta);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -546,7 +545,7 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom
|
||||
if (!bottom)
|
||||
{
|
||||
filename = model_path + "_bed.stl";
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, true)) {
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
|
||||
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
|
||||
if (key == "mk2")
|
||||
// hardcoded value to match the stl model
|
||||
@@ -628,12 +627,12 @@ void Bed3D::render_prusa_shader(bool transparent) const
|
||||
if (position_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(position_id));
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_position_offset()));
|
||||
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_position_offset()));
|
||||
}
|
||||
if (tex_coords_id != -1)
|
||||
{
|
||||
glsafe(::glEnableVertexAttribArray(tex_coords_id));
|
||||
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)m_triangles.get_tex_coords_offset()));
|
||||
glsafe(::glVertexAttribPointer(tex_coords_id, 2, GL_FLOAT, GL_FALSE, stride, (GLvoid*)(intptr_t)m_triangles.get_tex_coords_offset()));
|
||||
}
|
||||
|
||||
glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));
|
||||
@@ -651,7 +650,7 @@ void Bed3D::render_prusa_shader(bool transparent) const
|
||||
}
|
||||
}
|
||||
#else
|
||||
void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const
|
||||
void Bed3D::render_prusa(const std::string& key, float theta) const
|
||||
{
|
||||
std::string tex_path = resources_dir() + "/icons/bed/" + key;
|
||||
|
||||
@@ -677,7 +676,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
|
||||
std::string filename = tex_path + "_top.png";
|
||||
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_top_texture.load_from_file(filename, true))
|
||||
if (!m_top_texture.load_from_file(filename, true, true))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
@@ -694,7 +693,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
|
||||
filename = tex_path + "_bottom.png";
|
||||
if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
|
||||
{
|
||||
if (!m_bottom_texture.load_from_file(filename, true))
|
||||
if (!m_bottom_texture.load_from_file(filename, true, true))
|
||||
{
|
||||
render_custom();
|
||||
return;
|
||||
@@ -711,7 +710,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
|
||||
if (theta <= 90.0f)
|
||||
{
|
||||
filename = model_path + "_bed.stl";
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) {
|
||||
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
|
||||
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
|
||||
if (key == "mk2")
|
||||
// hardcoded value to match the stl model
|
||||
|
||||
@@ -44,8 +44,8 @@ public:
|
||||
const float* get_vertices_data() const;
|
||||
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
|
||||
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
|
||||
unsigned int get_position_offset() const { return 0; }
|
||||
unsigned int get_tex_coords_offset() const { return (unsigned int)(3 * sizeof(float)); }
|
||||
size_t get_position_offset() const { return 0; }
|
||||
size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
|
||||
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
|
||||
#else
|
||||
const float* get_vertices() const { return m_vertices.data(); }
|
||||
@@ -128,7 +128,11 @@ public:
|
||||
bool contains(const Point& point) const;
|
||||
Point point_projection(const Point& point) const;
|
||||
|
||||
void render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
void render(GLCanvas3D* canvas, float theta, float scale_factor) const;
|
||||
#else
|
||||
void render(float theta, float scale_factor) const;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
void render_axes() const;
|
||||
|
||||
private:
|
||||
@@ -140,7 +144,7 @@ private:
|
||||
void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const;
|
||||
void render_prusa_shader(bool transparent) const;
|
||||
#else
|
||||
void render_prusa(const std::string &key, float theta, bool useVBOs) const;
|
||||
void render_prusa(const std::string& key, float theta) const;
|
||||
#endif // ENABLE_TEXTURES_FROM_SVG
|
||||
void render_custom() const;
|
||||
#if ENABLE_TEXTURES_FROM_SVG
|
||||
|
||||
+230
-493
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user