Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_color_print_times

This commit is contained in:
Enrico Turri 2019-07-08 08:45:11 +02:00
commit ad5329253e
46 changed files with 2360 additions and 849 deletions

View file

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

View file

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

View file

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

BIN
resources/icons/row.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
resources/icons/table.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

View file

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

View file

@ -11,10 +11,16 @@
#include <boost/algorithm/string/classification.hpp> #include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp> #include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/nowide/fstream.hpp> #include <boost/nowide/fstream.hpp>
#include <boost/nowide/cstdio.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 <expat.h>
#include <Eigen/Dense> #include <Eigen/Dense>
#include "miniz_extension.hpp" #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 PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.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_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 std::string SLA_SUPPORT_POINTS_FILE = "Metadata/Slic3r_PE_sla_support_points.txt";
const char* MODEL_TAG = "model"; const char* MODEL_TAG = "model";
@ -331,6 +338,7 @@ namespace Slic3r {
typedef std::map<int, ObjectMetadata> IdToMetadataMap; typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, Geometry> IdToGeometryMap; typedef std::map<int, Geometry> IdToGeometryMap;
typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap; 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; typedef std::map<int, std::vector<sla::SupportPoint>> IdToSlaSupportPointsMap;
// Version of the 3mf file // Version of the 3mf file
@ -347,6 +355,7 @@ namespace Slic3r {
CurrentConfig m_curr_config; CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata; IdToMetadataMap m_objects_metadata;
IdToLayerHeightsProfileMap m_layer_heights_profiles; IdToLayerHeightsProfileMap m_layer_heights_profiles;
IdToLayerConfigRangesMap m_layer_config_ranges;
IdToSlaSupportPointsMap m_sla_support_points; IdToSlaSupportPointsMap m_sla_support_points;
std::string m_curr_metadata_name; std::string m_curr_metadata_name;
std::string m_curr_characters; 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 _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); 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_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_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); 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_curr_config.volume_id = -1;
m_objects_metadata.clear(); m_objects_metadata.clear();
m_layer_heights_profiles.clear(); m_layer_heights_profiles.clear();
m_layer_config_ranges.clear();
m_sla_support_points.clear(); m_sla_support_points.clear();
m_curr_metadata_name.clear(); m_curr_metadata_name.clear();
m_curr_characters.clear(); m_curr_characters.clear();
@ -546,9 +557,14 @@ namespace Slic3r {
if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE)) 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); _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)) else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE))
{ {
// extract 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()) if (obj_layer_heights_profile != m_layer_heights_profiles.end())
model_object->layer_height_profile = obj_layer_heights_profile->second; 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. // 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); 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()) { 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) 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) 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_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_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_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_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_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); 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; 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"). // 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. // 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! // 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; return false;
} }
vertices_count += its.vertices.size(); vertices_count += (int)its.vertices.size();
const Transform3d& matrix = volume->get_matrix(); const Transform3d& matrix = volume->get_matrix();
@ -1925,7 +2017,7 @@ namespace Slic3r {
// updates triangle offsets // updates triangle offsets
volume_it->second.first_triangle_id = triangles_count; 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; volume_it->second.last_triangle_id = triangles_count - 1;
for (size_t i = 0; i < its.indices.size(); ++ i) for (size_t i = 0; i < its.indices.size(); ++ i)
@ -2013,6 +2105,70 @@ namespace Slic3r {
return true; 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.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) bool _3MF_Exporter::_add_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model)
{ {
std::string out = ""; std::string out = "";

View file

@ -106,6 +106,9 @@ struct AMFParserContext
// amf/material/metadata // amf/material/metadata
NODE_TYPE_OBJECT, // amf/object NODE_TYPE_OBJECT, // amf/object
// amf/object/metadata // 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_MESH, // amf/object/mesh
NODE_TYPE_VERTICES, // amf/object/mesh/vertices NODE_TYPE_VERTICES, // amf/object/mesh/vertices
NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex
@ -189,7 +192,7 @@ struct AMFParserContext
}; };
// Version of the amf file // Version of the amf file
unsigned int m_version; unsigned int m_version;
// Current Expat XML parser instance. // Current Expat XML parser instance.
XML_Parser m_parser; XML_Parser m_parser;
// Model to receive objects extracted from an AMF file. // 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"); m_value[0] = get_attribute(atts, "type");
node_type_new = NODE_TYPE_METADATA; 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) if (m_path[1] == NODE_TYPE_OBJECT)
node_type_new = NODE_TYPE_MESH; node_type_new = NODE_TYPE_MESH;
} else if (strcmp(name, "instance") == 0) { } 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) else if (strcmp(name, "mirrorz") == 0)
node_type_new = NODE_TYPE_MIRRORZ; 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; break;
case 4: case 4:
if (m_path[3] == NODE_TYPE_VERTICES) { 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) } else if (strcmp(name, "triangle") == 0)
node_type_new = NODE_TYPE_TRIANGLE; 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; break;
case 5: case 5:
if (strcmp(name, "coordinates") == 0) { if (strcmp(name, "coordinates") == 0) {
@ -571,8 +584,13 @@ void AMFParserContext::endElement(const char * /* name */)
config = &m_material->config; config = &m_material->config;
else if (m_path[1] == NODE_TYPE_OBJECT && m_object) else if (m_path[1] == NODE_TYPE_OBJECT && m_object)
config = &m_object->config; 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; 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) if (config)
config->set_deserialize(opt_key, m_value[1]); 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) { } 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) if (end != nullptr)
*end = 0; *end = 0;
point(coord_idx) = atof(p); point(coord_idx) = float(atof(p));
if (++coord_idx == 5) { if (++coord_idx == 5) {
m_object->sla_support_points.push_back(sla::SupportPoint(point)); m_object->sla_support_points.push_back(sla::SupportPoint(point));
coord_idx = 0; coord_idx = 0;
@ -609,6 +627,16 @@ void AMFParserContext::endElement(const char * /* name */)
} }
m_object->sla_points_status = sla::PointsStatus::UserModified; 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) { else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
if (strcmp(opt_key, "modifier") == 0) { if (strcmp(opt_key, "modifier") == 0) {
// Is this volume a modifier volume? // Is this volume a modifier volume?
@ -907,6 +935,31 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
} }
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges) //FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
// #ys_FIXME_experiment : Try to export layer config range
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.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; const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
if (!sla_support_points.empty()) { if (!sla_support_points.empty()) {
// Store the SLA supports as a single semicolon separated list. // Store the SLA supports as a single semicolon separated list.
@ -941,7 +994,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << " </coordinates>\n"; stream << " </coordinates>\n";
stream << " </vertex>\n"; stream << " </vertex>\n";
} }
num_vertices += its.vertices.size(); num_vertices += (int)its.vertices.size();
} }
stream << " </vertices>\n"; stream << " </vertices>\n";
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) { for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {

View file

@ -629,7 +629,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
this->config = rhs.config; this->config = rhs.config;
this->sla_support_points = rhs.sla_support_points; this->sla_support_points = rhs.sla_support_points;
this->sla_points_status = rhs.sla_points_status; 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->layer_height_profile = rhs.layer_height_profile;
this->origin_translation = rhs.origin_translation; this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box; m_bounding_box = rhs.m_bounding_box;
@ -665,7 +665,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
this->config = std::move(rhs.config); this->config = std::move(rhs.config);
this->sla_support_points = std::move(rhs.sla_support_points); this->sla_support_points = std::move(rhs.sla_support_points);
this->sla_points_status = std::move(rhs.sla_points_status); 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->layer_height_profile = std::move(rhs.layer_height_profile);
this->origin_translation = std::move(rhs.origin_translation); this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box); m_bounding_box = std::move(rhs.m_bounding_box);
@ -1845,7 +1845,7 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
return true; return true;
++i_old; ++ i_old;
++ i_new; ++ i_new;
} }
for (; i_old < model_object_old.volumes.size(); ++ i_old) { for (; i_old < model_object_old.volumes.size(); ++ i_old) {

View file

@ -179,8 +179,8 @@ public:
ModelVolumePtrs volumes; ModelVolumePtrs volumes;
// Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings.
DynamicPrintConfig config; DynamicPrintConfig config;
// Variation of a layer thickness for spans of Z coordinates. // Variation of a layer thickness for spans of Z coordinates + optional parameter overrides.
t_layer_height_ranges layer_height_ranges; t_layer_config_ranges layer_config_ranges;
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. // 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. // The pairs of <z, layer_height> are packed into a 1D array.
std::vector<coordf_t> layer_height_profile; std::vector<coordf_t> layer_height_profile;
@ -597,8 +597,8 @@ public:
Model() {} Model() {}
~Model() { this->clear_objects(); this->clear_materials(); } ~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" */ // 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). */ // (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); } Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); }
explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(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=(const Model &rhs) { this->assign_copy(rhs); return *this; }

View file

@ -12,6 +12,8 @@
//#include "PrintExport.hpp" //#include "PrintExport.hpp"
#include <float.h>
#include <algorithm> #include <algorithm>
#include <limits> #include <limits>
#include <unordered_set> #include <unordered_set>
@ -41,36 +43,6 @@ void Print::clear()
m_model.clear_objects(); 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() PrintRegion* Print::add_region()
{ {
m_regions.emplace_back(new PrintRegion(this)); m_regions.emplace_back(new PrintRegion(this));
@ -335,7 +307,7 @@ unsigned int Print::num_object_instances() const
{ {
unsigned int instances = 0; unsigned int instances = 0;
for (const PrintObject *print_object : m_objects) for (const PrintObject *print_object : m_objects)
instances += print_object->copies().size(); instances += (unsigned int)print_object->copies().size();
return instances; return instances;
} }
@ -358,198 +330,6 @@ double Print::max_allowed_layer_height() const
return nozzle_diameter_max; return nozzle_diameter_max;
} }
// Caller is responsible for supplying models whose objects don't collide
// and have explicit instance positions.
void Print::add_model_object(ModelObject* model_object, int idx)
{
tbb::mutex::scoped_lock lock(this->state_mutex());
// Add a copy of this ModelObject to this Print.
m_model.objects.emplace_back(ModelObject::new_copy(*model_object));
m_model.objects.back()->set_model(&m_model);
// Initialize a new print object and store it at the given position.
PrintObject *object = new PrintObject(this, model_object, true);
if (idx != -1) {
delete m_objects[idx];
m_objects[idx] = object;
} else
m_objects.emplace_back(object);
// Invalidate all print steps.
this->invalidate_all_steps();
// Set the transformation matrix without translation from the first instance.
if (! model_object->instances.empty()) {
// Trafo and bounding box, both in world coordinate system.
Transform3d trafo = model_object->instances.front()->get_matrix();
BoundingBoxf3 bbox = model_object->instance_bounding_box(0);
// Now shift the object up to align it with the print bed.
trafo.data()[14] -= bbox.min(2);
// and reset the XY translation.
trafo.data()[12] = 0;
trafo.data()[13] = 0;
object->set_trafo(trafo);
}
size_t volume_id = 0;
for (const ModelVolume *volume : model_object->volumes) {
if (! volume->is_model_part() && ! volume->is_modifier())
continue;
// Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999);
// Find an existing print region with the same config.
size_t region_id = size_t(-1);
for (size_t i = 0; i < m_regions.size(); ++ i)
if (config.equals(m_regions[i]->config())) {
region_id = i;
break;
}
// If no region exists with the same config, create a new one.
if (region_id == size_t(-1)) {
region_id = m_regions.size();
this->add_region(config);
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
++ volume_id;
}
// Apply config to print object.
object->config_apply(this->default_object_config());
{
//normalize_and_apply_config(object->config(), model_object->config);
DynamicPrintConfig src_normalized(model_object->config);
src_normalized.normalize();
object->config_apply(src_normalized, true);
}
}
// This function is only called through the Perl-C++ binding from the unit tests, should be
// removed when unit tests are rewritten to C++.
bool Print::apply_config_perl_tests_only(DynamicPrintConfig config)
{
tbb::mutex::scoped_lock lock(this->state_mutex());
// Perl unit tests were failing in case the preset was not normalized (e.g. https://github.com/prusa3d/PrusaSlicer/issues/2288 was caused
// by too short max_layer_height vector. Calling the necessary function Preset::normalize(...) is not currently possible because there is no
// access to preset. This should be solved when the unit tests are rewritten to C++. For now we just copy-pasted code from Preset.cpp
// to make sure the unit tests pass (functions set_num_extruders and nozzle_options()).
auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(config.option("nozzle_diameter", true));
assert(nozzle_diameter != nullptr);
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : { "nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
"retract_length", "retract_lift", "retract_lift_above", "retract_lift_below", "retract_speed", "deretract_speed",
"retract_before_wipe", "retract_restart_extra", "retract_before_travel", "wipe",
"retract_layer_change", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour" })
{
auto *opt = config.option(key, true);
assert(opt != nullptr);
assert(opt->is_vector());
unsigned int num_extruders = (unsigned int)nozzle_diameter->values.size();
static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key));
}
// we get a copy of the config object so we can modify it safely
config.normalize();
// apply variables to placeholder parser
this->placeholder_parser().apply_config(config);
// handle changes to print config
t_config_option_keys print_diff = m_config.diff(config);
m_config.apply_only(config, print_diff, true);
bool invalidated = this->invalidate_state_by_config_options(print_diff);
// handle changes to object config defaults
m_default_object_config.apply(config, true);
for (PrintObject *object : m_objects) {
// we don't assume that config contains a full ObjectConfig,
// so we base it on the current print-wise default
PrintObjectConfig new_config = this->default_object_config();
// we override the new config with object-specific options
normalize_and_apply_config(new_config, object->model_object()->config);
// check whether the new config is different from the current one
t_config_option_keys diff = object->config().diff(new_config);
object->config_apply_only(new_config, diff, true);
invalidated |= object->invalidate_state_by_config_options(diff);
}
// handle changes to regions config defaults
m_default_region_config.apply(config, true);
// All regions now have distinct settings.
// Check whether applying the new region config defaults we'd get different regions.
bool rearrange_regions = false;
{
// Collect the already visited region configs into other_region_configs,
// so one may check for duplicates.
std::vector<PrintRegionConfig> other_region_configs;
for (size_t region_id = 0; region_id < m_regions.size(); ++ region_id) {
PrintRegion &region = *m_regions[region_id];
PrintRegionConfig this_region_config;
bool this_region_config_set = false;
for (PrintObject *object : m_objects) {
if (region_id < object->region_volumes.size()) {
for (int volume_id : object->region_volumes[region_id]) {
const ModelVolume &volume = *object->model_object()->volumes[volume_id];
if (this_region_config_set) {
// If the new config for this volume differs from the other
// volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
rearrange_regions = true;
goto exit_for_rearrange_regions;
}
} else {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
this_region_config_set = true;
}
for (const PrintRegionConfig &cfg : other_region_configs) {
// If the new config for this volume equals any of the other
// volume configs that are not currently associated to this
// region, it means the region subdivision does not make
// sense anymore.
if (cfg.equals(this_region_config)) {
rearrange_regions = true;
goto exit_for_rearrange_regions;
}
}
}
}
}
if (this_region_config_set) {
t_config_option_keys diff = region.config().diff(this_region_config);
if (! diff.empty()) {
region.config_apply_only(this_region_config, diff, false);
for (PrintObject *object : m_objects)
if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty())
invalidated |= object->invalidate_state_by_config_options(diff);
}
other_region_configs.emplace_back(std::move(this_region_config));
}
}
}
exit_for_rearrange_regions:
if (rearrange_regions) {
// The current subdivision of regions does not make sense anymore.
// We need to remove all objects and re-add them.
ModelObjectPtrs model_objects;
model_objects.reserve(m_objects.size());
for (PrintObject *object : m_objects)
model_objects.push_back(object->model_object());
this->clear();
for (ModelObject *mo : model_objects)
this->add_model_object(mo);
invalidated = true;
}
for (PrintObject *object : m_objects)
object->update_slicing_parameters();
return invalidated;
}
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new // 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. // 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. // It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
@ -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) static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{ {
typedef Transform3d::Scalar T; 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()); 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) Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
{ {
#ifdef _DEBUG #ifdef _DEBUG
@ -724,6 +535,50 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
// Handle changes to regions config defaults // Handle changes to regions config defaults
m_default_region_config.apply_only(config, region_diff, true); 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 { struct ModelObjectStatus {
enum Status { enum Status {
Unknown, Unknown,
@ -733,8 +588,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
Deleted, Deleted,
}; };
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
ModelID id; ModelID id;
Status status; Status status;
LayerRanges layer_ranges;
// Search by id. // Search by id.
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
}; };
@ -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())); auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
assert(it_status != model_object_status.end()); assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted); 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) if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop. // PrintObject instances will be added in the next loop.
continue; continue;
// Update the ModelObject instance, possibly invalidate the linked PrintObjects. // Update the ModelObject instance, possibly invalidate the linked PrintObjects.
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); 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. // 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 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 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_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); bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
if (model_parts_differ || modifiers_differ || if (model_parts_differ || modifiers_differ ||
model_object.origin_translation != model_object_new.origin_translation || 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. // 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())); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
for (auto it = range.first; it != range.second; ++ it) { for (auto it = range.first; it != range.second; ++ it) {
@ -915,7 +773,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
//FIXME What to do with m_material_id? //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::MODEL_PART);
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER); 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.name = model_object_new.name;
model_object.input_file = model_object_new.input_file; model_object.input_file = model_object_new.input_file;
model_object.clear_instances(); model_object.clear_instances();
@ -1027,19 +886,27 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
PrintRegionConfig this_region_config; PrintRegionConfig this_region_config;
bool this_region_config_set = false; bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) { 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()) { if (region_id < print_object->region_volumes.size()) {
for (int volume_id : print_object->region_volumes[region_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_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 (this_region_config_set) {
// If the new config for this volume differs from the other // If the new config for this volume differs from the other
// volume configs currently associated to this region, it means // volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore. // 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. // Regions were split. Reset this print_object.
goto print_object_end; goto print_object_end;
} else { } else {
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders); 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) { for (size_t i = 0; i < region_id; ++ i) {
const PrintRegion &region_other = *m_regions[i]; const PrintRegion &region_other = *m_regions[i];
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
// Regions were merged. Reset this print_object. // 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()); update_apply_status(print_object->invalidate_all_steps());
// Decrease the references to regions from this volume. // Decrease the references to regions from this volume.
int ireg = 0; 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()) if (! volumes.empty())
-- m_regions[ireg]->m_refcnt; -- m_regions[ireg]->m_refcnt;
++ ireg; ++ 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) { for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
PrintObject &print_object0 = *m_objects[idx_print_object]; PrintObject &print_object0 = *m_objects[idx_print_object];
const ModelObject &model_object = *print_object0.model_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) { 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]; PrintObject &print_object = *m_objects[i];
bool fresh = print_object.region_volumes.empty(); bool fresh = print_object.region_volumes.empty();
unsigned int volume_id = 0; unsigned int volume_id = 0;
unsigned int idx_region_in_object = 0;
for (const ModelVolume *volume : model_object.volumes) { for (const ModelVolume *volume : model_object.volumes) {
if (! volume->is_model_part() && ! volume->is_modifier()) { if (! volume->is_model_part() && ! volume->is_modifier()) {
++ volume_id; ++ volume_id;
continue; continue;
} }
int region_id = -1; // Filter the layer ranges, so they do not overlap and they contain at least a single layer.
if (&print_object == &print_object0) { // Now insert a volume with a layer range to its own region.
// Get the config applied to this volume. for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders); int region_id = -1;
// Find an existing print region with the same config. if (&print_object == &print_object0) {
int idx_empty_slot = -1; // Get the config applied to this volume.
for (int i = 0; i < (int)m_regions.size(); ++ i) { PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
if (m_regions[i]->m_refcnt == 0) { // Find an existing print region with the same config.
if (idx_empty_slot == -1) int idx_empty_slot = -1;
idx_empty_slot = i; for (int i = 0; i < (int)m_regions.size(); ++ i) {
} else if (config.equals(m_regions[i]->config())) { if (m_regions[i]->m_refcnt == 0) {
region_id = i; if (idx_empty_slot == -1)
break; 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));
}
} }
} regions_in_object.emplace_back(region_id);
// If no region exists with the same config, create a new one. } else
if (region_id == -1) { region_id = regions_in_object[idx_region_in_object ++];
if (idx_empty_slot == -1) { // Assign volume to a region.
region_id = (int)m_regions.size(); if (fresh) {
this->add_region(config); if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
} else { ++ m_regions[region_id]->m_refcnt;
region_id = idx_empty_slot; print_object.add_region_volume(region_id, volume_id, it_range->first);
m_regions[region_id]->set_config(std::move(config)); }
} }
} ++ volume_id;
map_volume_to_region[volume_id] = region_id; }
} else
region_id = map_volume_to_region[volume_id];
// Assign volume to a region.
if (fresh) {
if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
++ m_regions[region_id]->m_refcnt;
print_object.add_region_volume(region_id, volume_id);
}
++ volume_id;
}
} }
} }
@ -1175,9 +1055,9 @@ std::string Print::validate() const
Polygon convex_hull0 = offset( Polygon convex_hull0 = offset(
print_object->model_object()->convex_hull_2d( print_object->model_object()->convex_hull_2d(
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())), 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. // Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const Point &copy : print_object->m_copies) { for (const Point &copy : print_object->copies()) {
Polygon convex_hull = convex_hull0; Polygon convex_hull = convex_hull0;
convex_hull.translate(copy); convex_hull.translate(copy);
if (! intersection(convex_hulls_other, convex_hull).empty()) if (! intersection(convex_hulls_other, convex_hull).empty())
@ -1227,7 +1107,7 @@ std::string Print::validate() const
bool has_custom_layering = false; bool has_custom_layering = false;
std::vector<std::vector<coordf_t>> layer_height_profiles; std::vector<std::vector<coordf_t>> layer_height_profiles;
for (const PrintObject *object : m_objects) { 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) { if (has_custom_layering) {
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>()); layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
break; break;
@ -1435,9 +1315,9 @@ Flow Print::brim_flow() const
generation as well. */ generation as well. */
return Flow::new_from_config_width( return Flow::new_from_config_width(
frPerimeter, frPerimeter,
width, width,
m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1), (float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
this->skirt_first_layer_height(), (float)this->skirt_first_layer_height(),
0 0
); );
} }
@ -1457,9 +1337,9 @@ Flow Print::skirt_flow() const
generation as well. */ generation as well. */
return Flow::new_from_config_width( return Flow::new_from_config_width(
frPerimeter, frPerimeter,
width, width,
m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1), (float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
this->skirt_first_layer_height(), (float)this->skirt_first_layer_height(),
0 0
); );
} }
@ -1634,20 +1514,20 @@ void Print::_make_skirt()
// Initial offset of the brim inner edge from the object (possible with a support & raft). // 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. // 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()); 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. // 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. // 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.); std::vector<coordf_t> extruded_length(extruders.size(), 0.);
for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
this->throw_if_canceled(); this->throw_if_canceled();
// Offset the skirt outside. // Offset the skirt outside.
distance += coord_t(scale_(spacing)); distance += float(scale_(spacing));
// Generate the skirt centerline. // Generate the skirt centerline.
Polygon loop; 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); Geometry::simplify_polygons(loops, scale_(0.05), &loops);
if (loops.empty()) if (loops.empty())
break; break;
@ -1658,9 +1538,9 @@ void Print::_make_skirt()
eloop.paths.emplace_back(ExtrusionPath( eloop.paths.emplace_back(ExtrusionPath(
ExtrusionPath( ExtrusionPath(
erSkirt, 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, 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(); eloop.paths.back().polyline = loop.split_at_first_point();
m_skirt.append(eloop); m_skirt.append(eloop);
@ -1786,7 +1666,7 @@ void Print::_make_wipe_tower()
// Insert the new support layer. // Insert the new support layer.
double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z; 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. //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; ++ it_layer;
} }
} }
@ -1813,19 +1693,19 @@ void Print::_make_wipe_tower()
WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()), WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
m_config.temperature.get_at(i), m_config.temperature.get_at(i),
m_config.first_layer_temperature.get_at(i), m_config.first_layer_temperature.get_at(i),
m_config.filament_loading_speed.get_at(i), (float)m_config.filament_loading_speed.get_at(i),
m_config.filament_loading_speed_start.get_at(i), (float)m_config.filament_loading_speed_start.get_at(i),
m_config.filament_unloading_speed.get_at(i), (float)m_config.filament_unloading_speed.get_at(i),
m_config.filament_unloading_speed_start.get_at(i), (float)m_config.filament_unloading_speed_start.get_at(i),
m_config.filament_toolchange_delay.get_at(i), (float)m_config.filament_toolchange_delay.get_at(i),
m_config.filament_cooling_moves.get_at(i), m_config.filament_cooling_moves.get_at(i),
m_config.filament_cooling_initial_speed.get_at(i), (float)m_config.filament_cooling_initial_speed.get_at(i),
m_config.filament_cooling_final_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_ramming_parameters.get_at(i),
m_config.nozzle_diameter.get_at(i)); (float)m_config.nozzle_diameter.get_at(i));
m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>( 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)); 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 // 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) // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
@ -1834,21 +1714,21 @@ void Print::_make_wipe_tower()
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
if (!layer_tools.has_wipe_tower) continue; if (!layer_tools.has_wipe_tower) continue;
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front(); 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) { 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) { 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 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: // 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: // 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); 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: // 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 // 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); first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
current_extruder_id = extruder_id; current_extruder_id = extruder_id;
} }

View file

@ -80,8 +80,8 @@ private: // Prevents erroneous use by other classes.
typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited; typedef PrintObjectBaseWithState<Print, PrintObjectStep, posCount> Inherited;
public: public:
// vector of (vectors of volume ids), indexed by region_id // vector of (layer height ranges and vectors of volume ids), indexed by region_id
std::vector<std::vector<int>> region_volumes; 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 // 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 // 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)); } BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
// adds region_id, too, if necessary // 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()) if (region_id >= region_volumes.size())
region_volumes.resize(region_id + 1); 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 is the *total* layer count (including support layers)
// this value is not supposed to be compared with Layer::id // this value is not supposed to be compared with Layer::id
@ -141,8 +141,9 @@ public:
void slice(); void slice();
// Helpers to slice support enforcer / blocker meshes by the support generator. // Helpers to slice support enforcer / blocker meshes by the support generator.
std::vector<ExPolygons> slice_support_enforcers() const; std::vector<ExPolygons> slice_support_volumes(const ModelVolumeType &model_volume_type) const;
std::vector<ExPolygons> slice_support_blockers() 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: protected:
// to be called from Print only. // to be called from Print only.
@ -165,7 +166,7 @@ protected:
void update_slicing_parameters(); void update_slicing_parameters();
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders); 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: private:
void make_perimeters(); void make_perimeters();
@ -201,9 +202,11 @@ private:
LayerPtrs m_layers; LayerPtrs m_layers;
SupportLayerPtrs m_support_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_region(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_modifiers(size_t region_id, const std::vector<float> &z) const;
std::vector<ExPolygons> _slice_volume(const std::vector<float> &z, const ModelVolume &volume) 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 struct WipeTowerData
@ -295,11 +298,6 @@ public:
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override; 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; 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. // 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). // If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).

View file

@ -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 // Translate meshes so that our toolpath generation algorithms work with smaller
// XY coordinates; this translation is an optimization and not strictly required. // 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. // 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 // 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). // (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() 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 bool PrintObject::has_support_material() const
@ -1354,10 +1359,12 @@ PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObject
return config; 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; PrintRegionConfig config = default_region_config;
normalize_and_apply_config(config, volume.get_object()->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); normalize_and_apply_config(config, volume.config);
if (! volume.material_id().empty()) if (! volume.material_id().empty())
normalize_and_apply_config(config, volume.material()->config); normalize_and_apply_config(config, volume.material()->config);
@ -1375,28 +1382,37 @@ void PrintObject::update_slicing_parameters()
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders()); 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; PrintConfig print_config;
PrintObjectConfig object_config; PrintObjectConfig object_config;
PrintRegionConfig default_region_config; PrintRegionConfig default_region_config;
print_config .apply(full_config, true); print_config.apply(full_config, true);
object_config.apply(full_config, true); object_config.apply(full_config, true);
default_region_config.apply(full_config, true); default_region_config.apply(full_config, true);
size_t num_extruders = print_config.nozzle_diameter.size(); size_t num_extruders = print_config.nozzle_diameter.size();
object_config = object_config_from_model_object(object_config, model_object, num_extruders); object_config = object_config_from_model_object(object_config, model_object, num_extruders);
std::vector<unsigned int> object_extruders; std::vector<unsigned int> object_extruders;
for (const ModelVolume *model_volume : model_object.volumes) for (const ModelVolume* model_volume : model_object.volumes)
if (model_volume->is_model_part()) if (model_volume->is_model_part()) {
PrintRegion::collect_object_printing_extruders( PrintRegion::collect_object_printing_extruders(
print_config, print_config,
region_config_from_model_volume(default_region_config, *model_volume, num_extruders), region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders),
object_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); sort_remove_duplicates(object_extruders);
if (object_max_z <= 0.f) 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); return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
} }
@ -1430,12 +1446,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
layer_height_profile.clear(); layer_height_profile.clear();
if (layer_height_profile.empty()) { if (layer_height_profile.empty()) {
if (0) if (0)
// if (this->layer_height_profile.empty()) // 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 else
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges); layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); // #ys_FIXME_experiment
updated = true; updated = true;
} }
return updated; return updated;
} }
@ -1489,22 +1505,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. // 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_volumes = 0;
size_t num_modifiers = 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 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]; const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) { if (model_volume->is_model_part()) {
map_volume_to_region[volume_id] = region_id; if (last_volume_id == volume_id) {
if (single_volume_region == -2) has_z_ranges = true;
// first model volume met } else {
single_volume_region = region_id; last_volume_id = volume_id;
else if (single_volume_region != region_id) if (all_volumes_single_region == -2)
// multiple volumes met and they are not equal // first model volume met
single_volume_region = -1; all_volumes_single_region = region_id;
++ num_volumes; 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()) } else if (model_volume->is_modifier())
++ num_modifiers; ++ num_modifiers;
} }
@ -1514,13 +1536,13 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
// Slice all non-modifier volumes. // Slice all non-modifier volumes.
bool clipped = false; bool clipped = false;
bool upscaled = 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. // 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. // 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) { for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel // 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(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; 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) for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
@ -1541,15 +1563,29 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
}; };
std::vector<SlicedVolume> sliced_volumes; std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes); sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (int volume_id : this->region_volumes[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]; const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) { if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id; 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 // 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. // Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start"; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
tbb::parallel_for( tbb::parallel_for(
@ -1603,7 +1639,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) { for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel // 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(); m_print->throw_if_canceled();
if (expolygons_by_layer.empty()) if (expolygons_by_layer.empty())
continue; continue;
@ -1619,7 +1655,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
Layer *layer = m_layers[layer_id]; Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id]; LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_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; continue;
Polygons other_slices = to_polygons(other_layerm->slices); Polygons other_slices = to_polygons(other_layerm->slices);
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id])); ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
@ -1752,46 +1788,127 @@ end:
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - 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()) { if (region_id < this->region_volumes.size()) {
for (int volume_id : this->region_volumes[region_id]) { 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_id]; const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
if (modifier ? volume->is_modifier() : volume->is_model_part()) if (volume->is_model_part())
volumes.emplace_back(volume); 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; std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes) for (const ModelVolume *volume : this->model_object()->volumes)
if (volume->is_support_enforcer()) if (volume->type() == model_volume_type)
volumes.emplace_back(volume); volumes.emplace_back(volume);
std::vector<float> zs; std::vector<float> zs;
zs.reserve(this->layers().size()); zs.reserve(this->layers().size());
for (const Layer *l : this->layers()) for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z); 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<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) 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> layers; std::vector<ExPolygons> layers;
if (! volumes.empty()) { if (! volumes.empty()) {
@ -1828,34 +1945,71 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
return layers; 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; std::vector<ExPolygons> layers;
// Compose mesh. if (! z.empty()) {
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. // Compose mesh.
TriangleMesh mesh(volume.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.
mesh.transform(volume.get_matrix(), true); TriangleMesh mesh(volume.mesh());
if (mesh.repaired) { mesh.transform(volume.get_matrix(), true);
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it. if (mesh.repaired) {
stl_check_facets_exact(&mesh.stl); //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; 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() std::string PrintObject::_fix_slicing_errors()
{ {
// Collect layers with slicing errors. // Collect layers with slicing errors.
@ -2119,7 +2273,7 @@ void PrintObject::clip_fill_surfaces()
//Should the pw not be half of the current value? //Should the pw not be half of the current value?
float pw = FLT_MAX; float pw = FLT_MAX;
for (const LayerRegion *layerm : layer->m_regions) 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 // Append such thick perimeters to the areas that need support
polygons_append(overhangs, offset2(perimeters, -pw, +pw)); polygons_append(overhangs, offset2(perimeters, -pw, +pw));
} }

View file

@ -153,24 +153,33 @@ SlicingParameters SlicingParameters::create_from_config(
return params; 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. // 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( std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params, 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. // 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; 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()) if (slicing_params.first_object_layer_height_fixed())
ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>( ranges_non_overlapping.push_back(std::pair<t_layer_height_range,coordf_t>(
t_layer_height_range(0., slicing_params.first_object_layer_height), t_layer_height_range(0., slicing_params.first_object_layer_height),
slicing_params.first_object_layer_height)); slicing_params.first_object_layer_height));
// The height ranges are sorted lexicographically by low / high layer boundaries. // 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 lo = it_range->first.first;
coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height()); 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()) if (! ranges_non_overlapping.empty())
// Trim current low with the last high. // Trim current low with the last high.
lo = std::max(lo, ranges_non_overlapping.back().first.second); lo = std::max(lo, ranges_non_overlapping.back().first.second);
@ -219,7 +228,7 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height. // Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
std::vector<coordf_t> layer_height_profile_adaptive( std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params, const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges, const t_layer_config_ranges & /* layer_config_ranges */,
const ModelVolumePtrs &volumes) const ModelVolumePtrs &volumes)
{ {
// 1) Initialize the SlicingAdaptive class with the object meshes. // 1) Initialize the SlicingAdaptive class with the object meshes.

View file

@ -11,6 +11,8 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "Utils.hpp" #include "Utils.hpp"
#include "PrintConfig.hpp"
namespace Slic3r 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::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( extern std::vector<coordf_t> layer_height_profile_from_ranges(
const SlicingParameters &slicing_params, 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( extern std::vector<coordf_t> layer_height_profile_adaptive(
const SlicingParameters &slicing_params, const SlicingParameters &slicing_params,
const t_layer_height_ranges &layer_height_ranges, const t_layer_config_ranges &layer_config_ranges,
const ModelVolumePtrs &volumes); const ModelVolumePtrs &volumes);

View file

@ -829,7 +829,7 @@ namespace SupportMaterialInternal {
assert(expansion_scaled >= 0.f); assert(expansion_scaled >= 0.f);
for (const ExtrusionPath &ep : loop.paths) for (const ExtrusionPath &ep : loop.paths)
if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) { 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.is_closed()) {
if (ep.size() >= 3) { if (ep.size() >= 3) {
// This is a complete loop. // 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. // Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff( columns_base->polygons = diff(
offset(columns_base->polygons, inflate_factor_1st_layer), 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) if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons); columns_base->polygons = diff(columns_base->polygons, interface_polygons);
} }
@ -3226,7 +3226,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// TODO: use brim ordering algorithm // TODO: use brim ordering algorithm
Polygons to_infill_polygons = to_polygons(to_infill); Polygons to_infill_polygons = to_polygons(to_infill);
// TODO: use offset2_ex() // 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( extrusion_entities_append_paths(
base_layer.extrusions, base_layer.extrusions,
to_polylines(std::move(to_infill_polygons)), to_polylines(std::move(to_infill_polygons)),

View file

@ -81,6 +81,8 @@ set(SLIC3R_GUI_SOURCES
GUI/GUI_ObjectManipulation.hpp GUI/GUI_ObjectManipulation.hpp
GUI/GUI_ObjectSettings.cpp GUI/GUI_ObjectSettings.cpp
GUI/GUI_ObjectSettings.hpp GUI/GUI_ObjectSettings.hpp
GUI/GUI_ObjectLayers.cpp
GUI/GUI_ObjectLayers.hpp
GUI/LambdaObjectDialog.cpp GUI/LambdaObjectDialog.cpp
GUI/LambdaObjectDialog.hpp GUI/LambdaObjectDialog.hpp
GUI/Tab.cpp GUI/Tab.cpp

View file

@ -628,12 +628,12 @@ void Bed3D::render_prusa_shader(bool transparent) const
if (position_id != -1) if (position_id != -1)
{ {
glsafe(::glEnableVertexAttribArray(position_id)); 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) if (tex_coords_id != -1)
{ {
glsafe(::glEnableVertexAttribArray(tex_coords_id)); 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())); glsafe(::glDrawArrays(GL_TRIANGLES, 0, (GLsizei)m_triangles.get_vertices_count()));

View file

@ -113,11 +113,19 @@ wxString Field::get_tooltip_text(const wxString& default_string)
wxString tooltip_text(""); wxString tooltip_text("");
wxString tooltip = _(m_opt.tooltip); wxString tooltip = _(m_opt.tooltip);
edit_tooltip(tooltip); edit_tooltip(tooltip);
std::string opt_id = m_opt_id;
auto hash_pos = opt_id.find("#");
if (hash_pos != std::string::npos) {
opt_id.replace(hash_pos, 1,"[");
opt_id += "]";
}
if (tooltip.length() > 0) if (tooltip.length() > 0)
tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " + tooltip_text = tooltip + "\n" + _(L("default value")) + "\t: " +
(boost::iends_with(m_opt_id, "_gcode") ? "\n" : "") + default_string + (boost::iends_with(opt_id, "_gcode") ? "\n" : "") + default_string +
(boost::iends_with(m_opt_id, "_gcode") ? "" : "\n") + (boost::iends_with(opt_id, "_gcode") ? "" : "\n") +
_(L("parameter name")) + "\t: " + m_opt_id; _(L("parameter name")) + "\t: " + opt_id;
return tooltip_text; return tooltip_text;
} }

View file

@ -3324,6 +3324,12 @@ void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool foc
} }
} }
void GLCanvas3D::handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type)
{
std::string field = "layer_" + std::to_string(type) + "_" + std::to_string(range.first) + "_" + std::to_string(range.second);
handle_sidebar_focus_event(field, true);
}
void GLCanvas3D::update_ui_from_settings() void GLCanvas3D::update_ui_from_settings()
{ {
m_camera.set_type(wxGetApp().app_config->get("use_perspective_camera")); m_camera.set_type(wxGetApp().app_config->get("use_perspective_camera"));
@ -4289,13 +4295,7 @@ void GLCanvas3D::_render_sla_slices() const
void GLCanvas3D::_render_selection_sidebar_hints() const void GLCanvas3D::_render_selection_sidebar_hints() const
{ {
if (m_use_VBOs) m_selection.render_sidebar_hints(m_sidebar_field, m_shader);
m_shader.start_using();
m_selection.render_sidebar_hints(m_sidebar_field);
if (m_use_VBOs)
m_shader.stop_using();
} }

View file

@ -12,6 +12,7 @@
#include "Camera.hpp" #include "Camera.hpp"
#include "Selection.hpp" #include "Selection.hpp"
#include "Gizmos/GLGizmosManager.hpp" #include "Gizmos/GLGizmosManager.hpp"
#include "GUI_ObjectLayers.hpp"
#include <float.h> #include <float.h>
@ -608,6 +609,7 @@ public:
void reset_all_gizmos() { m_gizmos.reset_all_states(); } void reset_all_gizmos() { m_gizmos.reset_all_states(); }
void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on); void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on);
void handle_layers_data_focus_event(const t_layer_height_range range, const EditorType type);
void update_ui_from_settings(); void update_ui_from_settings();

View file

@ -934,6 +934,11 @@ ObjectList* GUI_App::obj_list()
return sidebar().obj_list(); return sidebar().obj_list();
} }
ObjectLayers* GUI_App::obj_layers()
{
return sidebar().obj_layers();
}
Plater* GUI_App::plater() Plater* GUI_App::plater()
{ {
return plater_; return plater_;

View file

@ -156,6 +156,7 @@ public:
ObjectManipulation* obj_manipul(); ObjectManipulation* obj_manipul();
ObjectSettings* obj_settings(); ObjectSettings* obj_settings();
ObjectList* obj_list(); ObjectList* obj_list();
ObjectLayers* obj_layers();
Plater* plater(); Plater* plater();
std::vector<ModelObject*> *model_objects(); std::vector<ModelObject*> *model_objects();

View file

@ -0,0 +1,341 @@
#include "GUI_ObjectLayers.hpp"
#include "GUI_ObjectList.hpp"
#include "OptionsGroup.hpp"
#include "PresetBundle.hpp"
#include "libslic3r/Model.hpp"
#include "GLCanvas3D.hpp"
#include <boost/algorithm/string.hpp>
#include "I18N.hpp"
#include <wx/wupdlock.h>
namespace Slic3r
{
namespace GUI
{
ObjectLayers::ObjectLayers(wxWindow* parent) :
OG_Settings(parent, true)
{
m_grid_sizer = new wxFlexGridSizer(3, 5, 5); // "Min Z", "Max Z", "Layer height" & buttons sizer
m_grid_sizer->SetFlexibleDirection(wxHORIZONTAL);
// Legend for object layers
for (const std::string col : { "Min Z", "Max Z", "Layer height" }) {
auto temp = new wxStaticText(m_parent, wxID_ANY, _(L(col)), wxDefaultPosition, /*size*/wxDefaultSize, wxST_ELLIPSIZE_MIDDLE);
temp->SetFont(Slic3r::GUI::wxGetApp().normal_font());
temp->SetBackgroundStyle(wxBG_STYLE_PAINT);
temp->SetFont(wxGetApp().bold_font());
m_grid_sizer->Add(temp);
}
m_og->sizer->Clear(true);
m_og->sizer->Add(m_grid_sizer, 0, wxEXPAND | wxALL, wxOSX ? 0 : 5);
m_bmp_delete = ScalableBitmap(parent, "remove_copies"/*"cross"*/);
m_bmp_add = ScalableBitmap(parent, "add_copies");
}
void ObjectLayers::select_editor(LayerRangeEditor* editor, const bool is_last_edited_range)
{
if (is_last_edited_range && m_selection_type == editor->type()) {
/* Workaround! Under OSX we should use CallAfter() for SetFocus() after LayerEditors "reorganizations",
* because of selected control's strange behavior:
* cursor is set to the control, but blue border - doesn't.
* And as a result we couldn't edit this control.
* */
#ifdef __WXOSX__
wxTheApp->CallAfter([editor]() {
#endif
editor->SetFocus();
editor->SetInsertionPointEnd();
#ifdef __WXOSX__
});
#endif
}
}
wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range)
{
const bool is_last_edited_range = range == m_selectable_range;
auto set_focus_data = [range, this](const EditorType type)
{
m_selectable_range = range;
m_selection_type = type;
};
auto update_focus_data = [range, this](const t_layer_height_range& new_range, EditorType type, bool enter_pressed)
{
// change selectable range for new one, if enter was pressed or if same range was selected
if (enter_pressed || m_selectable_range == range)
m_selectable_range = new_range;
if (enter_pressed)
m_selection_type = type;
};
// Add control for the "Min Z"
auto editor = new LayerRangeEditor(this, double_to_string(range.first), etMinZ,
set_focus_data, [range, update_focus_data, this](coordf_t min_z, bool enter_pressed)
{
if (fabs(min_z - range.first) < EPSILON) {
m_selection_type = etUndef;
return false;
}
// data for next focusing
coordf_t max_z = min_z < range.second ? range.second : min_z + 0.5;
const t_layer_height_range& new_range = { min_z, max_z };
update_focus_data(new_range, etMinZ, enter_pressed);
return wxGetApp().obj_list()->edit_layer_range(range, new_range);
});
select_editor(editor, is_last_edited_range);
m_grid_sizer->Add(editor);
// Add control for the "Max Z"
editor = new LayerRangeEditor(this, double_to_string(range.second), etMaxZ,
set_focus_data, [range, update_focus_data, this](coordf_t max_z, bool enter_pressed)
{
if (fabs(max_z - range.second) < EPSILON || range.first > max_z) {
m_selection_type = etUndef;
return false; // LayersList would not be updated/recreated
}
// data for next focusing
const t_layer_height_range& new_range = { range.first, max_z };
update_focus_data(new_range, etMaxZ, enter_pressed);
return wxGetApp().obj_list()->edit_layer_range(range, new_range);
});
select_editor(editor, is_last_edited_range);
m_grid_sizer->Add(editor);
// Add control for the "Layer height"
editor = new LayerRangeEditor(this,
double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()),
etLayerHeight, set_focus_data, [range, this](coordf_t layer_height, bool)
{
return wxGetApp().obj_list()->edit_layer_range(range, layer_height);
});
select_editor(editor, is_last_edited_range);
auto sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(editor);
m_grid_sizer->Add(sizer);
return sizer;
}
void ObjectLayers::create_layers_list()
{
for (const auto layer : m_object->layer_config_ranges)
{
const t_layer_height_range& range = layer.first;
auto sizer = create_layer(range);
auto del_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_delete);
del_btn->SetToolTip(_(L("Remove layer")));
sizer->Add(del_btn, 0, wxRIGHT | wxLEFT, em_unit(m_parent));
del_btn->Bind(wxEVT_BUTTON, [this, range](wxEvent &event) {
wxGetApp().obj_list()->del_layer_range(range);
});
auto add_btn = new ScalableButton(m_parent, wxID_ANY, m_bmp_add);
add_btn->SetToolTip(_(L("Add layer")));
sizer->Add(add_btn, 0, wxRIGHT, em_unit(m_parent));
add_btn->Bind(wxEVT_BUTTON, [this, range](wxEvent &event) {
wxGetApp().obj_list()->add_layer_range_after_current(range);
});
}
}
void ObjectLayers::update_layers_list()
{
ObjectList* objects_ctrl = wxGetApp().obj_list();
if (objects_ctrl->multiple_selection()) return;
const auto item = objects_ctrl->GetSelection();
if (!item) return;
const int obj_idx = objects_ctrl->get_selected_obj_idx();
if (obj_idx < 0) return;
const ItemType type = objects_ctrl->GetModel()->GetItemType(item);
if (!(type & (itLayerRoot | itLayer))) return;
m_object = objects_ctrl->object(obj_idx);
if (!m_object || m_object->layer_config_ranges.empty()) return;
// Delete all controls from options group except of the legends
const int cols = m_grid_sizer->GetEffectiveColsCount();
const int rows = m_grid_sizer->GetEffectiveRowsCount();
for (int idx = cols*rows-1; idx >= cols; idx--) {
wxSizerItem* t = m_grid_sizer->GetItem(idx);
if (t->IsSizer())
t->GetSizer()->Clear(true);
else
t->DeleteWindows();
m_grid_sizer->Remove(idx);
}
// Add new control according to the selected item
if (type & itLayerRoot)
create_layers_list();
else
create_layer(objects_ctrl->GetModel()->GetLayerRangeByItem(item));
m_parent->Layout();
}
void ObjectLayers::update_scene_from_editor_selection() const
{
// needed to show the visual hints in 3D scene
wxGetApp().plater()->canvas3D()->handle_layers_data_focus_event(m_selectable_range, m_selection_type);
}
void ObjectLayers::UpdateAndShow(const bool show)
{
if (show)
update_layers_list();
OG_Settings::UpdateAndShow(show);
}
void ObjectLayers::msw_rescale()
{
m_bmp_delete.msw_rescale();
m_bmp_add.msw_rescale();
}
void ObjectLayers::reset_selection()
{
m_selectable_range = { 0.0, 0.0 };
m_selection_type = etLayerHeight;
}
LayerRangeEditor::LayerRangeEditor( ObjectLayers* parent,
const wxString& value,
EditorType type,
std::function<void(EditorType)> set_focus_data_fn,
std::function<bool(coordf_t, bool enter_pressed)> edit_fn
) :
m_valid_value(value),
m_type(type),
m_set_focus_data(set_focus_data_fn),
wxTextCtrl(parent->m_parent, wxID_ANY, value, wxDefaultPosition,
wxSize(8 * em_unit(parent->m_parent), wxDefaultCoord), wxTE_PROCESS_ENTER)
{
this->SetFont(wxGetApp().normal_font());
this->Bind(wxEVT_TEXT_ENTER, [this, edit_fn](wxEvent&)
{
m_enter_pressed = true;
// If LayersList wasn't updated/recreated, we can call wxEVT_KILL_FOCUS.Skip()
if (m_type&etLayerHeight) {
if (!edit_fn(get_value(), true))
SetValue(m_valid_value);
else
m_valid_value = double_to_string(get_value());
m_call_kill_focus = true;
}
else if (!edit_fn(get_value(), true)) {
SetValue(m_valid_value);
m_call_kill_focus = true;
}
}, this->GetId());
this->Bind(wxEVT_KILL_FOCUS, [this, edit_fn](wxFocusEvent& e)
{
if (!m_enter_pressed) {
#ifndef __WXGTK__
/* Update data for next editor selection.
* But under GTK it lucks like there is no information about selected control at e.GetWindow(),
* so we'll take it from wxEVT_LEFT_DOWN event
* */
LayerRangeEditor* new_editor = dynamic_cast<LayerRangeEditor*>(e.GetWindow());
if (new_editor)
new_editor->set_focus_data();
#endif // not __WXGTK__
// If LayersList wasn't updated/recreated, we should call e.Skip()
if (m_type & etLayerHeight) {
if (!edit_fn(get_value(), false))
SetValue(m_valid_value);
else
m_valid_value = double_to_string(get_value());
e.Skip();
}
else if (!edit_fn(get_value(), false)) {
SetValue(m_valid_value);
e.Skip();
}
}
else if (m_call_kill_focus) {
m_call_kill_focus = false;
e.Skip();
}
}, this->GetId());
this->Bind(wxEVT_SET_FOCUS, [this, parent](wxFocusEvent& e)
{
set_focus_data();
parent->update_scene_from_editor_selection();
e.Skip();
}, this->GetId());
#ifdef __WXGTK__ // Workaround! To take information about selectable range
this->Bind(wxEVT_LEFT_DOWN, [this](wxEvent& e)
{
set_focus_data();
e.Skip();
}, this->GetId());
#endif //__WXGTK__
this->Bind(wxEVT_CHAR, ([this](wxKeyEvent& event)
{
// select all text using Ctrl+A
if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL))
this->SetSelection(-1, -1); //select all
event.Skip();
}));
}
coordf_t LayerRangeEditor::get_value()
{
wxString str = GetValue();
coordf_t layer_height;
// Replace the first occurence of comma in decimal number.
str.Replace(",", ".", false);
if (str == ".")
layer_height = 0.0;
else
{
if (!str.ToCDouble(&layer_height) || layer_height < 0.0f)
{
show_error(m_parent, _(L("Invalid numeric input.")));
SetValue(double_to_string(layer_height));
}
}
return layer_height;
}
} //namespace GUI
} //namespace Slic3r

View file

@ -0,0 +1,88 @@
#ifndef slic3r_GUI_ObjectLayers_hpp_
#define slic3r_GUI_ObjectLayers_hpp_
#include "GUI_ObjectSettings.hpp"
#include "wxExtensions.hpp"
#ifdef __WXOSX__
#include "../libslic3r/PrintConfig.hpp"
#endif
class wxBoxSizer;
namespace Slic3r {
class ModelObject;
namespace GUI {
class ConfigOptionsGroup;
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
class ObjectLayers;
enum EditorType
{
etUndef = 0,
etMinZ = 1,
etMaxZ = 2,
etLayerHeight = 4,
};
class LayerRangeEditor : public wxTextCtrl
{
bool m_enter_pressed { false };
bool m_call_kill_focus { false };
wxString m_valid_value;
EditorType m_type;
std::function<void(EditorType)> m_set_focus_data;
public:
LayerRangeEditor( ObjectLayers* parent,
const wxString& value = wxEmptyString,
EditorType type = etUndef,
std::function<void(EditorType)> set_focus_data_fn = [](EditorType) {;},
std::function<bool(coordf_t, bool)> edit_fn = [](coordf_t, bool) {return false; }
);
~LayerRangeEditor() {}
EditorType type() const {return m_type;}
void set_focus_data() const { m_set_focus_data(m_type);}
private:
coordf_t get_value();
};
class ObjectLayers : public OG_Settings
{
ScalableBitmap m_bmp_delete;
ScalableBitmap m_bmp_add;
ModelObject* m_object {nullptr};
wxFlexGridSizer* m_grid_sizer;
t_layer_height_range m_selectable_range;
EditorType m_selection_type {etUndef};
public:
ObjectLayers(wxWindow* parent);
~ObjectLayers() {}
void select_editor(LayerRangeEditor* editor, const bool is_last_edited_range);
wxSizer* create_layer(const t_layer_height_range& range); // without_buttons
void create_layers_list();
void update_layers_list();
void update_scene_from_editor_selection() const;
void UpdateAndShow(const bool show) override;
void msw_rescale();
void reset_selection();
void set_selectable_range(const t_layer_height_range& range) { m_selectable_range = range; }
friend class LayerRangeEditor;
};
}}
#endif // slic3r_GUI_ObjectLayers_hpp_

View file

@ -1,6 +1,7 @@
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "GUI_ObjectList.hpp" #include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectLayers.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "I18N.hpp" #include "I18N.hpp"
@ -147,10 +148,10 @@ ObjectList::ObjectList(wxWindow* parent) :
wxAcceleratorTable accel(6, entries); wxAcceleratorTable accel(6, entries);
SetAcceleratorTable(accel); SetAcceleratorTable(accel);
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); }, wxID_COPY); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY);
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); }, wxID_PASTE); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->paste(); }, wxID_PASTE);
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
} }
#else __WXOSX__ #else __WXOSX__
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
@ -350,12 +351,13 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons
const ItemType type = m_objects_model->GetItemType(item); const ItemType type = m_objects_model->GetItemType(item);
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) : const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item)); m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1; const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0)); assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0));
return type & itVolume ?(*m_objects)[obj_idx]->volumes[vol_idx]->config : return type & itVolume ?(*m_objects)[obj_idx]->volumes[vol_idx]->config :
type & itLayer ?(*m_objects)[obj_idx]->layer_config_ranges[m_objects_model->GetLayerRangeByItem(item)] :
(*m_objects)[obj_idx]->config; (*m_objects)[obj_idx]->config;
} }
@ -441,16 +443,23 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item)
{ {
if (m_prevent_update_extruder_in_config) if (m_prevent_update_extruder_in_config)
return; return;
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
const ItemType item_type = m_objects_model->GetItemType(item);
if (item_type & itObject) {
const int obj_idx = m_objects_model->GetIdByItem(item); const int obj_idx = m_objects_model->GetIdByItem(item);
m_config = &(*m_objects)[obj_idx]->config; m_config = &(*m_objects)[obj_idx]->config;
} }
else { else {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item)); const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
if (item_type & itVolume)
{
const int volume_id = m_objects_model->GetVolumeIdByItem(item); const int volume_id = m_objects_model->GetVolumeIdByItem(item);
if (obj_idx < 0 || volume_id < 0) if (obj_idx < 0 || volume_id < 0)
return; return;
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
}
else if (item_type & itLayer)
m_config = &get_item_config(item);
} }
wxVariant variant; wxVariant variant;
@ -569,9 +578,75 @@ void ObjectList::selection_changed()
wxPostEvent(this, event); wxPostEvent(this, event);
} }
if (const wxDataViewItem item = GetSelection())
{
const ItemType type = m_objects_model->GetItemType(item);
// to correct visual hints for layers editing on the Scene
if (type & (itLayer|itLayerRoot)) {
wxGetApp().obj_layers()->reset_selection();
if (type & itLayerRoot)
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
else {
wxGetApp().obj_layers()->set_selectable_range(m_objects_model->GetLayerRangeByItem(item));
wxGetApp().obj_layers()->update_scene_from_editor_selection();
}
}
}
part_selection_changed(); part_selection_changed();
} }
void ObjectList::fill_layer_config_ranges_cache()
{
wxDataViewItemArray sel_layers;
GetSelections(sel_layers);
const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers[0]);
if (obj_idx < 0 || (int)m_objects->size() <= obj_idx)
return;
const t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
m_layer_config_ranges_cache.clear();
for (const auto layer_item : sel_layers)
if (m_objects_model->GetItemType(layer_item) & itLayer) {
auto range = m_objects_model->GetLayerRangeByItem(layer_item);
auto it = ranges.find(range);
if (it != ranges.end())
m_layer_config_ranges_cache[it->first] = it->second;
}
}
void ObjectList::paste_layers_into_list()
{
const int obj_idx = m_objects_model->GetObjectIdByItem(GetSelection());
if (obj_idx < 0 || (int)m_objects->size() <= obj_idx ||
m_layer_config_ranges_cache.empty() || printer_technology() == ptSLA)
return;
const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item);
if (layers_item)
m_objects_model->Delete(layers_item);
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
// and create Layer item(s) according to the layer_config_ranges
for (const auto range : m_layer_config_ranges_cache)
ranges.emplace(range);
layers_item = add_layer_root_item(object_item);
changed_object(obj_idx);
select_item(layers_item);
#ifndef __WXOSX__
selection_changed();
#endif //no __WXOSX__
}
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes) void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
{ {
if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx)) if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
@ -653,7 +728,7 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
const wxPoint pt = get_mouse_position_in_control(); const wxPoint pt = get_mouse_position_in_control();
HitTest(pt, item, col); HitTest(pt, item, col);
if (!item) if (!item)
#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX #ifdef __WXOSX__ // temporary workaround for OSX
// after Yosemite OS X version, HitTest return undefined item // after Yosemite OS X version, HitTest return undefined item
item = GetSelection(); item = GetSelection();
if (item) if (item)
@ -699,10 +774,11 @@ void ObjectList::show_context_menu()
if (item) if (item)
{ {
const ItemType type = m_objects_model->GetItemType(item); const ItemType type = m_objects_model->GetItemType(item);
if (!(type & (itObject | itVolume | itInstance))) if (!(type & (itObject | itVolume | itLayer | itInstance)))
return; return;
wxMenu* menu = type & itInstance ? &m_menu_instance : wxMenu* menu = type & itInstance ? &m_menu_instance :
type & itLayer ? &m_menu_layer :
m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part : m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object; printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
@ -713,6 +789,22 @@ void ObjectList::show_context_menu()
} }
} }
void ObjectList::copy()
{
if (m_selection_mode & smLayer)
fill_layer_config_ranges_cache();
else
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
}
void ObjectList::paste()
{
if (!m_layer_config_ranges_cache.empty())
paste_layers_into_list();
else
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
}
#ifndef __WXOSX__ #ifndef __WXOSX__
void ObjectList::key_event(wxKeyEvent& event) void ObjectList::key_event(wxKeyEvent& event)
{ {
@ -727,10 +819,10 @@ void ObjectList::key_event(wxKeyEvent& event)
} }
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
select_item_all_children(); select_item_all_children();
else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL)) else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); copy();
else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL)) else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL))
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); paste();
else else
event.Skip(); event.Skip();
} }
@ -1033,7 +1125,17 @@ void ObjectList::get_settings_choice(const wxString& category_name)
void ObjectList::get_freq_settings_choice(const wxString& bundle_name) void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
{ {
const std::vector<std::string>& options = get_options_for_bundle(bundle_name); std::vector<std::string> options = get_options_for_bundle(bundle_name);
/* Because of we couldn't edited layer_height for ItVolume from settings list,
* correct options according to the selected item type :
* remove "layer_height" option
*/
if ((m_objects_model->GetItemType(GetSelection()) & itVolume) && bundle_name == _("Layers and Perimeters")) {
const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height");
if (layer_height_it != options.end())
options.erase(layer_height_it);
}
assert(m_config); assert(m_config);
auto opt_keys = m_config->keys(); auto opt_keys = m_config->keys();
@ -1137,6 +1239,12 @@ wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu)
[this]() { return is_splittable(); }, wxGetApp().plater()); [this]() { return is_splittable(); }, wxGetApp().plater());
} }
wxMenuItem* ObjectList::append_menu_item_layers_editing(wxMenu* menu)
{
return append_menu_item(menu, wxID_ANY, _(L("Edit Layers")), "",
[this](wxCommandEvent&) { layers_editing(); }, "layers", menu);
}
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
{ {
MenuWithSeparators* menu = dynamic_cast<MenuWithSeparators*>(menu_); MenuWithSeparators* menu = dynamic_cast<MenuWithSeparators*>(menu_);
@ -1301,7 +1409,11 @@ void ObjectList::create_object_popupmenu(wxMenu *menu)
append_menu_item_scale_selection_to_fit_print_volume(menu); append_menu_item_scale_selection_to_fit_print_volume(menu);
// Split object to parts // Split object to parts
m_menu_item_split = append_menu_item_split(menu); append_menu_item_split(menu);
menu->AppendSeparator();
// Layers Editing for object
append_menu_item_layers_editing(menu);
menu->AppendSeparator(); menu->AppendSeparator();
// rest of a object_menu will be added later in: // rest of a object_menu will be added later in:
@ -1330,7 +1442,7 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
append_menu_item_fix_through_netfabb(menu); append_menu_item_fix_through_netfabb(menu);
append_menu_item_export_stl(menu); append_menu_item_export_stl(menu);
m_menu_item_split_part = append_menu_item_split(menu); append_menu_item_split(menu);
// Append change part type // Append change part type
menu->AppendSeparator(); menu->AppendSeparator();
@ -1576,38 +1688,52 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
ItemType type; ItemType type;
m_objects_model->GetItemInfo(item, type, obj_idx, idx); m_objects_model->GetItemInfo(item, type, obj_idx, idx);
if (type == itUndef) if (type & itUndef)
return; return;
if (type == itSettings) if (type & itSettings)
del_settings_from_config(); del_settings_from_config(m_objects_model->GetParent(item));
else if (type == itInstanceRoot && obj_idx != -1) else if (type & itInstanceRoot && obj_idx != -1)
del_instances_from_object(obj_idx); del_instances_from_object(obj_idx);
else if (type & itLayerRoot && obj_idx != -1)
del_layers_from_object(obj_idx);
else if (type & itLayer && obj_idx != -1)
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
else if (idx == -1) else if (idx == -1)
return; return;
else if (!del_subobject_from_object(obj_idx, idx, type)) else if (!del_subobject_from_object(obj_idx, idx, type))
return; return;
// If last volume item with warning was deleted, unmark object item // If last volume item with warning was deleted, unmark object item
if (type == itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0) if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item)); m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
m_objects_model->Delete(item); m_objects_model->Delete(item);
} }
void ObjectList::del_settings_from_config() void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
{ {
auto opt_keys = m_config->keys(); const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer;
if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
const int opt_cnt = m_config->keys().size();
if (opt_cnt == 1 && m_config->has("extruder") ||
is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height"))
return; return;
int extruder = -1; int extruder = -1;
if (m_config->has("extruder")) if (m_config->has("extruder"))
extruder = m_config->option<ConfigOptionInt>("extruder")->value; extruder = m_config->option<ConfigOptionInt>("extruder")->value;
coordf_t layer_height = 0.0;
if (is_layer_settings)
layer_height = m_config->opt_float("layer_height");
m_config->clear(); m_config->clear();
if (extruder >= 0) if (extruder >= 0)
m_config->set_key_value("extruder", new ConfigOptionInt(extruder)); m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
if (is_layer_settings)
m_config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
} }
void ObjectList::del_instances_from_object(const int obj_idx) void ObjectList::del_instances_from_object(const int obj_idx)
@ -1624,6 +1750,24 @@ void ObjectList::del_instances_from_object(const int obj_idx)
changed_object(obj_idx); changed_object(obj_idx);
} }
void ObjectList::del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range)
{
const auto del_range = object(obj_idx)->layer_config_ranges.find(layer_range);
if (del_range == object(obj_idx)->layer_config_ranges.end())
return;
object(obj_idx)->layer_config_ranges.erase(del_range);
changed_object(obj_idx);
}
void ObjectList::del_layers_from_object(const int obj_idx)
{
object(obj_idx)->layer_config_ranges.clear();
changed_object(obj_idx);
}
bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type)
{ {
if (obj_idx == 1000) if (obj_idx == 1000)
@ -1719,6 +1863,70 @@ void ObjectList::split()
changed_object(obj_idx); changed_object(obj_idx);
} }
void ObjectList::layers_editing()
{
const auto item = GetSelection();
const int obj_idx = get_selected_obj_idx();
if (!item || obj_idx < 0)
return;
const wxDataViewItem obj_item = m_objects_model->GetTopParent(item);
wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(obj_item);
// if it doesn't exist now
if (!layers_item.IsOk())
{
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
// set some default value
if (ranges.empty())
ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx);
// create layer root item
layers_item = add_layer_root_item(obj_item);
}
if (!layers_item.IsOk())
return;
// to correct visual hints for layers editing on the Scene, reset previous selection
wxGetApp().obj_layers()->reset_selection();
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
// select LayerRoor item and expand
select_item(layers_item);
Expand(layers_item);
}
wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item)
{
const int obj_idx = m_objects_model->GetIdByItem(obj_item);
if (obj_idx < 0 ||
object(obj_idx)->layer_config_ranges.empty() ||
printer_technology() == ptSLA)
return wxDataViewItem(0);
// create LayerRoot item
wxDataViewItem layers_item = m_objects_model->AddLayersRoot(obj_item);
// and create Layer item(s) according to the layer_config_ranges
for (const auto range : object(obj_idx)->layer_config_ranges)
add_layer_item(range.first, layers_item);
return layers_item;
}
DynamicPrintConfig ObjectList::get_default_layer_config(const int obj_idx)
{
DynamicPrintConfig config;
coordf_t layer_height = object(obj_idx)->config.has("layer_height") ?
object(obj_idx)->config.opt_float("layer_height") :
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_float("layer_height");
config.set_key_value("layer_height",new ConfigOptionFloat(layer_height));
config.set_key_value("extruder", new ConfigOptionInt(0));
return config;
}
bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume) bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume)
{ {
auto obj_idx = get_selected_obj_idx(); auto obj_idx = get_selected_obj_idx();
@ -1788,6 +1996,7 @@ void ObjectList::part_selection_changed()
bool update_and_show_manipulations = false; bool update_and_show_manipulations = false;
bool update_and_show_settings = false; bool update_and_show_settings = false;
bool update_and_show_layers = false;
const auto item = GetSelection(); const auto item = GetSelection();
@ -1810,36 +2019,47 @@ void ObjectList::part_selection_changed()
update_and_show_manipulations = true; update_and_show_manipulations = true;
} }
else { else {
auto parent = m_objects_model->GetParent(item); obj_idx = m_objects_model->GetObjectIdByItem(item);
// Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
obj_idx = m_objects_model->GetIdByItem(parent); const ItemType type = m_objects_model->GetItemType(item);
if (m_objects_model->GetItemType(item) == itSettings) { if (type & itSettings) {
if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) { const auto parent = m_objects_model->GetParent(item);
const ItemType parent_type = m_objects_model->GetItemType(parent);
if (parent_type & itObject) {
og_name = _(L("Object Settings to modify")); og_name = _(L("Object Settings to modify"));
m_config = &(*m_objects)[obj_idx]->config; m_config = &(*m_objects)[obj_idx]->config;
} }
else { else if (parent_type & itVolume) {
og_name = _(L("Part Settings to modify")); og_name = _(L("Part Settings to modify"));
auto main_parent = m_objects_model->GetParent(parent); volume_id = m_objects_model->GetVolumeIdByItem(parent);
obj_idx = m_objects_model->GetIdByItem(main_parent);
const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
} }
else if (parent_type & itLayer) {
og_name = _(L("Layer range Settings to modify"));
m_config = &get_item_config(parent);
}
update_and_show_settings = true; update_and_show_settings = true;
} }
else if (m_objects_model->GetItemType(item) == itVolume) { else if (type & itVolume) {
og_name = _(L("Part manipulation")); og_name = _(L("Part manipulation"));
volume_id = m_objects_model->GetVolumeIdByItem(item); volume_id = m_objects_model->GetVolumeIdByItem(item);
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config; m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
update_and_show_manipulations = true; update_and_show_manipulations = true;
} }
else if (m_objects_model->GetItemType(item) == itInstance) { else if (type & itInstance) {
og_name = _(L("Instance manipulation")); og_name = _(L("Instance manipulation"));
update_and_show_manipulations = true; update_and_show_manipulations = true;
// fill m_config by object's values // fill m_config by object's values
const int obj_idx_ = m_objects_model->GetObjectIdByItem(item); m_config = &(*m_objects)[obj_idx]->config;
m_config = &(*m_objects)[obj_idx_]->config; }
else if (type & (itLayerRoot|itLayer)) {
og_name = type & itLayerRoot ? _(L("Layers Editing")) : _(L("Layer Editing"));
update_and_show_layers = true;
if (type & itLayer)
m_config = &get_item_config(item);
} }
} }
} }
@ -1859,11 +2079,18 @@ void ObjectList::part_selection_changed()
if (update_and_show_settings) if (update_and_show_settings)
wxGetApp().obj_settings()->get_og()->set_name(" " + og_name + " "); wxGetApp().obj_settings()->get_og()->set_name(" " + og_name + " ");
if (printer_technology() == ptSLA)
update_and_show_layers = false;
else if (update_and_show_layers)
wxGetApp().obj_layers()->get_og()->set_name(" " + og_name + " ");
Sidebar& panel = wxGetApp().sidebar(); Sidebar& panel = wxGetApp().sidebar();
panel.Freeze(); panel.Freeze();
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations); wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings); wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers);
wxGetApp().sidebar().show_info_sizer(); wxGetApp().sidebar().show_info_sizer();
panel.Layout(); panel.Layout();
@ -1909,6 +2136,9 @@ void ObjectList::add_object_to_list(size_t obj_idx)
Expand(item); Expand(item);
} }
// Add layers if it has
add_layer_root_item(item);
#ifndef __WXOSX__ #ifndef __WXOSX__
selection_changed(); selection_changed();
#endif //__WXMSW__ #endif //__WXMSW__
@ -2053,16 +2283,196 @@ void ObjectList::remove()
wxDataViewItemArray sels; wxDataViewItemArray sels;
GetSelections(sels); GetSelections(sels);
wxDataViewItem parent = wxDataViewItem(0);
for (auto& item : sels) for (auto& item : sels)
{ {
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) if (m_objects_model->GetParent(item) == wxDataViewItem(0))
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1); delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
else { else {
if (sels.size() == 1) if (m_objects_model->GetItemType(item) & itLayer) {
parent = m_objects_model->GetParent(item);
wxDataViewItemArray children;
if (m_objects_model->GetChildren(parent, children) == 1)
parent = m_objects_model->GetTopParent(item);
}
else if (sels.size() == 1)
select_item(m_objects_model->GetParent(item)); select_item(m_objects_model->GetParent(item));
del_subobject_item(item); del_subobject_item(item);
} }
} }
if (parent)
select_item(parent);
}
void ObjectList::del_layer_range(const t_layer_height_range& range)
{
const int obj_idx = get_selected_obj_idx();
if (obj_idx < 0) return;
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
wxDataViewItem selectable_item = GetSelection();
if (ranges.size() == 1)
selectable_item = m_objects_model->GetParent(selectable_item);
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, range);
del_subobject_item(layer_item);
select_item(selectable_item);
}
double get_min_layer_height(const int extruder_idx)
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
return config.opt_float("min_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
}
double get_max_layer_height(const int extruder_idx)
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
return config.opt_float("max_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
}
void ObjectList::add_layer_range_after_current(const t_layer_height_range& current_range)
{
const int obj_idx = get_selected_obj_idx();
if (obj_idx < 0) return;
const wxDataViewItem layers_item = GetSelection();
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
const t_layer_height_range& last_range = (--ranges.end())->first;
if (current_range == last_range)
{
const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f };
ranges[new_range] = get_default_layer_config(obj_idx);
add_layer_item(new_range, layers_item);
}
else
{
const t_layer_height_range& next_range = (++ranges.find(current_range))->first;
if (current_range.second > next_range.first)
return; // range division has no sense
const int layer_idx = m_objects_model->GetItemIdByLayerRange(obj_idx, next_range);
if (layer_idx < 0)
return;
if (current_range.second == next_range.first)
{
const auto old_config = ranges.at(next_range);
const coordf_t delta = (next_range.second - next_range.first);
if (delta < get_min_layer_height(old_config.opt_int("extruder"))/*0.05f*/) // next range division has no sense
return;
const coordf_t midl_layer = next_range.first + 0.5f * delta;
t_layer_height_range new_range = { midl_layer, next_range.second };
// delete old layer
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range);
del_subobject_item(layer_item);
// create new 2 layers instead of deleted one
ranges[new_range] = old_config;
add_layer_item(new_range, layers_item, layer_idx);
new_range = { current_range.second, midl_layer };
ranges[new_range] = get_default_layer_config(obj_idx);
add_layer_item(new_range, layers_item, layer_idx);
}
else
{
const t_layer_height_range new_range = { current_range.second, next_range.first };
ranges[new_range] = get_default_layer_config(obj_idx);
add_layer_item(new_range, layers_item, layer_idx);
}
}
changed_object(obj_idx);
// select item to update layers sizer
select_item(layers_item);
}
void ObjectList::add_layer_item(const t_layer_height_range& range,
const wxDataViewItem layers_item,
const int layer_idx /* = -1*/)
{
const int obj_idx = m_objects_model->GetObjectIdByItem(layers_item);
if (obj_idx < 0) return;
const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range];
if (!config.has("extruder"))
return;
const auto layer_item = m_objects_model->AddLayersChild(layers_item,
range,
config.opt_int("extruder"),
layer_idx);
if (config.keys().size() > 2)
select_item(m_objects_model->AddSettingsChild(layer_item));
}
bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height)
{
const int obj_idx = get_selected_obj_idx();
if (obj_idx < 0)
return false;
DynamicPrintConfig* config = &object(obj_idx)->layer_config_ranges[range];
if (fabs(layer_height - config->opt_float("layer_height")) < EPSILON)
return false;
const int extruder_idx = config->opt_int("extruder");
if (layer_height >= get_min_layer_height(extruder_idx) &&
layer_height <= get_max_layer_height(extruder_idx))
{
config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
return true;
}
return false;
}
bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_layer_height_range& new_range)
{
const int obj_idx = get_selected_obj_idx();
if (obj_idx < 0) return false;
const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
const DynamicPrintConfig config = ranges[range];
ranges.erase(range);
ranges[new_range] = config;
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
m_objects_model->DeleteChildren(root_item);
if (root_item.IsOk())
// create Layer item(s) according to the layer_config_ranges
for (const auto r : ranges)
add_layer_item(r.first, root_item);
select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item);
Expand(root_item);
return true;
} }
void ObjectList::init_objects() void ObjectList::init_objects()
@ -2092,11 +2502,12 @@ void ObjectList::update_selections()
m_selection_mode = smInstance; m_selection_mode = smInstance;
// We doesn't update selection if SettingsItem for the current object/part is selected // We doesn't update selection if SettingsItem for the current object/part is selected
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings ) // if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) & (itSettings | itLayerRoot | itLayer))
{ {
const auto item = GetSelection(); const auto item = GetSelection();
if (selection.is_single_full_object()) { if (selection.is_single_full_object()) {
if ( m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx()) if (m_objects_model->GetObjectIdByItem(item) == selection.get_object_idx())
return; return;
sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
} }
@ -2217,22 +2628,18 @@ void ObjectList::update_selections_on_canvas()
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection) auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
{ {
const ItemType& type = m_objects_model->GetItemType(item); const ItemType& type = m_objects_model->GetItemType(item);
if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) { const int obj_idx = m_objects_model->GetObjectIdByItem(item);
wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item;
selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection);
return;
}
if (type == itVolume) { if (type == itVolume) {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
const int vol_idx = m_objects_model->GetVolumeIdByItem(item); const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection); selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
} }
else if (type == itInstance) { else if (type == itInstance) {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
const int inst_idx = m_objects_model->GetInstanceIdByItem(item); const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
selection.add_instance(obj_idx, inst_idx, as_single_selection); selection.add_instance(obj_idx, inst_idx, as_single_selection);
} }
else
selection.add_object(obj_idx, as_single_selection);
}; };
// stores current instance idx before to clear the selection // stores current instance idx before to clear the selection
@ -2240,7 +2647,7 @@ void ObjectList::update_selections_on_canvas()
if (sel_cnt == 1) { if (sel_cnt == 1) {
wxDataViewItem item = GetSelection(); wxDataViewItem item = GetSelection();
if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot)) if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true); add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true);
else else
add_to_selection(item, selection, instance_idx, true); add_to_selection(item, selection, instance_idx, true);
@ -2303,11 +2710,13 @@ void ObjectList::select_item_all_children()
} }
else { else {
const auto item = GetSelection(); const auto item = GetSelection();
// Some volume(instance) is selected => select all volumes(instances) inside the current object const ItemType item_type = m_objects_model->GetItemType(item);
if (m_objects_model->GetItemType(item) & (itVolume | itInstance)) // Some volume/layer/instance is selected => select all volumes/layers/instances inside the current object
if (item_type & (itVolume | itInstance | itLayer))
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels); m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance; m_selection_mode = item_type&itVolume ? smVolume :
item_type&itLayer ? smLayer : smInstance;
} }
SetSelections(sels); SetSelections(sels);
@ -2326,8 +2735,9 @@ void ObjectList::update_selection_mode()
} }
const ItemType type = m_objects_model->GetItemType(GetSelection()); const ItemType type = m_objects_model->GetItemType(GetSelection());
m_selection_mode = type&itSettings ? smUndef : m_selection_mode = type & itSettings ? smUndef :
type&itVolume ? smVolume : smInstance; type & itLayer ? smLayer :
type & itVolume ? smVolume : smInstance;
} }
// check last selected item. If is it possible to select it // check last selected item. If is it possible to select it
@ -2338,33 +2748,37 @@ bool ObjectList::check_last_selection(wxString& msg_str)
const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT); const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT);
/* We can't mix Parts and Objects/Instances. /* We can't mix Volumes, Layers and Objects/Instances.
* So, show information about it * So, show information about it
*/ */
const ItemType type = m_objects_model->GetItemType(m_last_selected_item); const ItemType type = m_objects_model->GetItemType(m_last_selected_item);
// check a case of a selection of the Parts from different Objects // check a case of a selection of the same type items from different Objects
bool impossible_multipart_selection = false; auto impossible_multi_selection = [type, this](const ItemType item_type, const SELECTION_MODE selection_mode) {
if (type & itVolume && m_selection_mode == smVolume) if (!(type & item_type && m_selection_mode & selection_mode))
{ return false;
wxDataViewItemArray sels; wxDataViewItemArray sels;
GetSelections(sels); GetSelections(sels);
for (const auto& sel: sels) for (const auto& sel : sels)
if (sel != m_last_selected_item && if (sel != m_last_selected_item &&
m_objects_model->GetParent(sel) != m_objects_model->GetParent(m_last_selected_item)) m_objects_model->GetTopParent(sel) != m_objects_model->GetTopParent(m_last_selected_item))
{ return true;
impossible_multipart_selection = true;
break;
}
}
if (impossible_multipart_selection || return false;
};
if (impossible_multi_selection(itVolume, smVolume) ||
impossible_multi_selection(itLayer, smLayer ) ||
type & itSettings || type & itSettings ||
type & itVolume && m_selection_mode == smInstance || type & itVolume && !(m_selection_mode & smVolume ) ||
!(type & itVolume) && m_selection_mode == smVolume) type & itLayer && !(m_selection_mode & smLayer ) ||
type & itInstance && !(m_selection_mode & smInstance)
)
{ {
// Inform user why selection isn't complited // Inform user why selection isn't complited
const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part")); const wxString item_type = m_selection_mode & smInstance ? _(L("Object or Instance")) :
m_selection_mode & smVolume ? _(L("Part")) : _(L("Layer"));
msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" + msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" +
_(L("You started your selection with %s Item.")) + "\n" + _(L("You started your selection with %s Item.")) + "\n" +
@ -2401,7 +2815,7 @@ void ObjectList::fix_multiselection_conflicts()
wxDataViewItemArray sels; wxDataViewItemArray sels;
GetSelections(sels); GetSelections(sels);
if (m_selection_mode == smVolume) if (m_selection_mode & (smVolume|smLayer))
{ {
// identify correct parent of the initial selected item // identify correct parent of the initial selected item
const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front()); const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front());
@ -2410,8 +2824,10 @@ void ObjectList::fix_multiselection_conflicts()
wxDataViewItemArray children; // selected volumes from current parent wxDataViewItemArray children; // selected volumes from current parent
m_objects_model->GetChildren(parent, children); m_objects_model->GetChildren(parent, children);
const ItemType item_type = m_selection_mode & smVolume ? itVolume : itLayer;
for (const auto child : children) for (const auto child : children)
if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume) if (IsSelected(child) && m_objects_model->GetItemType(child) & item_type)
sels.Add(child); sels.Add(child);
// If some part is selected, unselect all items except of selected parts of the current object // If some part is selected, unselect all items except of selected parts of the current object
@ -2576,6 +2992,87 @@ void ObjectList::update_settings_items()
m_prevent_canvas_selection_update = false; m_prevent_canvas_selection_update = false;
} }
// Update settings item for item had it
void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections)
{
const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item);
select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item));
// If settings item was deleted from the list,
// it's need to be deleted from selection array, if it was there
if (settings_item != m_objects_model->GetSettingsItem(item) &&
selections.Index(settings_item) != wxNOT_FOUND) {
selections.Remove(settings_item);
// Select item, if settings_item doesn't exist for item anymore, but was selected
if (selections.Index(item) == wxNOT_FOUND)
selections.Add(item);
}
}
void ObjectList::update_object_list_by_printer_technology()
{
m_prevent_canvas_selection_update = true;
wxDataViewItemArray sel;
GetSelections(sel); // stash selection
wxDataViewItemArray object_items;
m_objects_model->GetChildren(wxDataViewItem(0), object_items);
for (auto& object_item : object_items) {
// Update Settings Item for object
update_settings_item_and_selection(object_item, sel);
// Update settings for Volumes
wxDataViewItemArray all_object_subitems;
m_objects_model->GetChildren(object_item, all_object_subitems);
for (auto item : all_object_subitems)
if (m_objects_model->GetItemType(item) & itVolume)
// update settings for volume
update_settings_item_and_selection(item, sel);
// Update Layers Items
wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item);
if (!layers_item)
layers_item = add_layer_root_item(object_item);
else if (printer_technology() == ptSLA) {
// If layers root item will be deleted from the list, so
// it's need to be deleted from selection array, if it was there
wxDataViewItemArray del_items;
bool some_layers_was_selected = false;
m_objects_model->GetAllChildren(layers_item, del_items);
for (auto& del_item:del_items)
if (sel.Index(del_item) != wxNOT_FOUND) {
some_layers_was_selected = true;
sel.Remove(del_item);
}
if (sel.Index(layers_item) != wxNOT_FOUND) {
some_layers_was_selected = true;
sel.Remove(layers_item);
}
// delete all "layers" items
m_objects_model->Delete(layers_item);
// Select object_item, if layers_item doesn't exist for item anymore, but was some of layer items was/were selected
if (some_layers_was_selected)
sel.Add(object_item);
}
else {
wxDataViewItemArray all_obj_layers;
m_objects_model->GetChildren(layers_item, all_obj_layers);
for (auto item : all_obj_layers)
// update settings for layer
update_settings_item_and_selection(item, sel);
}
}
// restore selection:
SetSelections(sel);
m_prevent_canvas_selection_update = false;
}
void ObjectList::update_object_menu() void ObjectList::update_object_menu()
{ {
append_menu_items_add_volume(&m_menu_object); append_menu_items_add_volume(&m_menu_object);
@ -2749,7 +3246,8 @@ void ObjectList::msw_rescale()
for (MenuWithSeparators* menu : { &m_menu_object, for (MenuWithSeparators* menu : { &m_menu_object,
&m_menu_part, &m_menu_part,
&m_menu_sla_object, &m_menu_sla_object,
&m_menu_instance }) &m_menu_instance,
&m_menu_layer })
msw_rescale_menu(menu); msw_rescale_menu(menu);
Layout(); Layout();
@ -2862,5 +3360,13 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
wxGetApp().plater()->update(); wxGetApp().plater()->update();
} }
ModelObject* ObjectList::object(const int obj_idx) const
{
if (obj_idx < 0)
return nullptr;
return (*m_objects)[obj_idx];
}
} //namespace GUI } //namespace GUI
} //namespace Slic3r } //namespace Slic3r

View file

@ -33,6 +33,10 @@ typedef std::map< std::string, std::vector< std::pair<std::string, std::string>
typedef std::vector<ModelVolume*> ModelVolumePtrs; typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
namespace GUI { namespace GUI {
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
@ -64,9 +68,10 @@ class ObjectList : public wxDataViewCtrl
{ {
enum SELECTION_MODE enum SELECTION_MODE
{ {
smUndef, smUndef = 0,
smVolume, smVolume = 1,
smInstance smInstance = 2,
smLayer = 4
} m_selection_mode {smUndef}; } m_selection_mode {smUndef};
struct dragged_item_data struct dragged_item_data
@ -119,12 +124,17 @@ class ObjectList : public wxDataViewCtrl
MenuWithSeparators m_menu_part; MenuWithSeparators m_menu_part;
MenuWithSeparators m_menu_sla_object; MenuWithSeparators m_menu_sla_object;
MenuWithSeparators m_menu_instance; MenuWithSeparators m_menu_instance;
wxMenuItem* m_menu_item_split { nullptr }; MenuWithSeparators m_menu_layer;
wxMenuItem* m_menu_item_split_part { nullptr };
wxMenuItem* m_menu_item_settings { nullptr }; wxMenuItem* m_menu_item_settings { nullptr };
wxMenuItem* m_menu_item_split_instances { nullptr }; wxMenuItem* m_menu_item_split_instances { nullptr };
std::vector<wxBitmap*> m_bmp_vector; ObjectDataViewModel *m_objects_model{ nullptr };
DynamicPrintConfig *m_config {nullptr};
std::vector<ModelObject*> *m_objects{ nullptr };
std::vector<wxBitmap*> m_bmp_vector;
t_layer_config_ranges m_layer_config_ranges_cache;
int m_selected_object_id = -1; int m_selected_object_id = -1;
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select() bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
@ -153,11 +163,11 @@ public:
std::map<std::string, wxBitmap> CATEGORY_ICON; std::map<std::string, wxBitmap> CATEGORY_ICON;
ObjectDataViewModel *m_objects_model{ nullptr }; ObjectDataViewModel* GetModel() const { return m_objects_model; }
DynamicPrintConfig *m_config {nullptr}; DynamicPrintConfig* config() const { return m_config; }
std::vector<ModelObject*>* objects() const { return m_objects; }
std::vector<ModelObject*> *m_objects{ nullptr };
ModelObject* object(const int obj_idx) const ;
void create_objects_ctrl(); void create_objects_ctrl();
void create_popup_menus(); void create_popup_menus();
@ -192,6 +202,9 @@ public:
void key_event(wxKeyEvent& event); void key_event(wxKeyEvent& event);
#endif /* __WXOSX__ */ #endif /* __WXOSX__ */
void copy();
void paste();
void get_settings_choice(const wxString& category_name); void get_settings_choice(const wxString& category_name);
void get_freq_settings_choice(const wxString& bundle_name); void get_freq_settings_choice(const wxString& bundle_name);
void update_settings_item(); void update_settings_item();
@ -199,6 +212,7 @@ public:
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type); wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
void append_menu_items_add_volume(wxMenu* menu); void append_menu_items_add_volume(wxMenu* menu);
wxMenuItem* append_menu_item_split(wxMenu* menu); wxMenuItem* append_menu_item_split(wxMenu* menu);
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
wxMenuItem* append_menu_item_settings(wxMenu* menu); wxMenuItem* append_menu_item_settings(wxMenu* menu);
wxMenuItem* append_menu_item_change_type(wxMenu* menu); wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent); wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
@ -222,10 +236,17 @@ public:
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type); void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void del_object(const int obj_idx); void del_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item); void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(); void del_settings_from_config(const wxDataViewItem& parent_item);
void del_instances_from_object(const int obj_idx); void del_instances_from_object(const int obj_idx);
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
void del_layers_from_object(const int obj_idx);
bool del_subobject_from_object(const int obj_idx, const int idx, const int type); bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
void split(); void split();
void layers_editing();
wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item);
DynamicPrintConfig get_default_layer_config(const int obj_idx);
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume); bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
bool is_splittable(); bool is_splittable();
bool selected_instances_of_same_object(); bool selected_instances_of_same_object();
@ -265,6 +286,14 @@ public:
// Remove objects/sub-object from the list // Remove objects/sub-object from the list
void remove(); void remove();
void del_layer_range(const t_layer_height_range& range);
void add_layer_range_after_current(const t_layer_height_range& current_range);
void add_layer_item (const t_layer_height_range& range,
const wxDataViewItem layers_item,
const int layer_idx = -1);
bool edit_layer_range(const t_layer_height_range& range, coordf_t layer_height);
bool edit_layer_range(const t_layer_height_range& range,
const t_layer_height_range& new_range);
void init_objects(); void init_objects();
bool multiple_selection() const ; bool multiple_selection() const ;
@ -286,6 +315,8 @@ public:
void last_volume_is_deleted(const int obj_idx); void last_volume_is_deleted(const int obj_idx);
bool has_multi_part_objects(); bool has_multi_part_objects();
void update_settings_items(); void update_settings_items();
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
void update_object_list_by_printer_technology();
void update_object_menu(); void update_object_menu();
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx); void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
@ -295,6 +326,8 @@ public:
void fix_through_netfabb(); void fix_through_netfabb();
void update_item_error_icon(const int obj_idx, int vol_idx) const ; void update_item_error_icon(const int obj_idx, int vol_idx) const ;
void fill_layer_config_ranges_cache();
void paste_layers_into_list();
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes); void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
void paste_objects_into_list(const std::vector<size_t>& object_idxs); void paste_objects_into_list(const std::vector<size_t>& object_idxs);

View file

@ -68,10 +68,12 @@ void ObjectSettings::update_settings_list()
m_settings_list_sizer->Clear(true); m_settings_list_sizer->Clear(true);
auto objects_ctrl = wxGetApp().obj_list(); auto objects_ctrl = wxGetApp().obj_list();
auto objects_model = wxGetApp().obj_list()->m_objects_model; auto objects_model = wxGetApp().obj_list()->GetModel();
auto config = wxGetApp().obj_list()->m_config; auto config = wxGetApp().obj_list()->config();
const auto item = objects_ctrl->GetSelection(); const auto item = objects_ctrl->GetSelection();
const bool is_layers_range_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itLayer;
if (item && !objects_ctrl->multiple_selection() && if (item && !objects_ctrl->multiple_selection() &&
config && objects_model->IsSettingsItem(item)) config && objects_model->IsSettingsItem(item))
{ {
@ -119,7 +121,8 @@ void ObjectSettings::update_settings_list()
} }
for (auto& cat : cat_options) { for (auto& cat : cat_options) {
if (cat.second.size() == 1 && cat.second[0] == "extruder") if (cat.second.size() == 1 &&
(cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height"))
continue; continue;
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column); auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column);
@ -129,14 +132,14 @@ void ObjectSettings::update_settings_list()
optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) { optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) {
wxGetApp().obj_list()->changed_object(); }; wxGetApp().obj_list()->changed_object(); };
const bool is_extriders_cat = cat.first == "Extruders"; const bool is_extruders_cat = cat.first == "Extruders";
for (auto& opt : cat.second) for (auto& opt : cat.second)
{ {
if (opt == "extruder") if (opt == "extruder" || is_layers_range_settings && opt == "layer_height")
continue; continue;
Option option = optgroup->get_option(opt); Option option = optgroup->get_option(opt);
option.opt.width = 12; option.opt.width = 12;
if (is_extriders_cat) if (is_extruders_cat)
option.opt.max = wxGetApp().extruders_cnt(); option.opt.max = wxGetApp().extruders_cnt();
optgroup->append_single_option_line(option); optgroup->append_single_option_line(option);
} }

View file

@ -320,6 +320,17 @@ Line OptionsGroup::create_single_option_line(const Option& option) const {
return retval; return retval;
} }
void OptionsGroup::clear_fields_except_of(const std::vector<std::string> left_fields)
{
auto it = m_fields.begin();
while (it != m_fields.end()) {
if (std::find(left_fields.begin(), left_fields.end(), it->first) == left_fields.end())
it = m_fields.erase(it);
else
it++;
}
}
void OptionsGroup::on_set_focus(const std::string& opt_key) void OptionsGroup::on_set_focus(const std::string& opt_key)
{ {
if (m_set_focus != nullptr) if (m_set_focus != nullptr)

View file

@ -160,6 +160,8 @@ public:
m_show_modified_btns = show; m_show_modified_btns = show;
} }
void clear_fields_except_of(const std::vector<std::string> left_fields);
OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false,
column_t extra_clmn = nullptr) : column_t extra_clmn = nullptr) :
m_parent(_parent), title(title), m_parent(_parent), title(title),

View file

@ -50,6 +50,7 @@
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "GUI_ObjectList.hpp" #include "GUI_ObjectList.hpp"
#include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectLayers.hpp"
#include "GUI_Utils.hpp" #include "GUI_Utils.hpp"
#include "wxExtensions.hpp" #include "wxExtensions.hpp"
#include "MainFrame.hpp" #include "MainFrame.hpp"
@ -618,6 +619,7 @@ struct Sidebar::priv
ObjectList *object_list{ nullptr }; ObjectList *object_list{ nullptr };
ObjectManipulation *object_manipulation{ nullptr }; ObjectManipulation *object_manipulation{ nullptr };
ObjectSettings *object_settings{ nullptr }; ObjectSettings *object_settings{ nullptr };
ObjectLayers *object_layers{ nullptr };
ObjectInfo *object_info; ObjectInfo *object_info;
SlicedInfo *sliced_info; SlicedInfo *sliced_info;
@ -641,6 +643,9 @@ Sidebar::priv::~priv()
if (frequently_changed_parameters != nullptr) if (frequently_changed_parameters != nullptr)
delete frequently_changed_parameters; delete frequently_changed_parameters;
if (object_layers != nullptr)
delete object_layers;
} }
void Sidebar::priv::show_preset_comboboxes() void Sidebar::priv::show_preset_comboboxes()
@ -749,6 +754,11 @@ Sidebar::Sidebar(Plater *parent)
p->object_settings = new ObjectSettings(p->scrolled); p->object_settings = new ObjectSettings(p->scrolled);
p->object_settings->Hide(); p->object_settings->Hide();
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5); p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
// Object Layers
p->object_layers = new ObjectLayers(p->scrolled);
p->object_layers->Hide();
p->sizer_params->Add(p->object_layers->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
// Info boxes // Info boxes
p->object_info = new ObjectInfo(p->scrolled); p->object_info = new ObjectInfo(p->scrolled);
@ -942,6 +952,7 @@ void Sidebar::msw_rescale()
p->object_list->msw_rescale(); p->object_list->msw_rescale();
p->object_manipulation->msw_rescale(); p->object_manipulation->msw_rescale();
p->object_settings->msw_rescale(); p->object_settings->msw_rescale();
p->object_layers->msw_rescale();
p->object_info->msw_rescale(); p->object_info->msw_rescale();
@ -963,6 +974,11 @@ ObjectSettings* Sidebar::obj_settings()
return p->object_settings; return p->object_settings;
} }
ObjectLayers* Sidebar::obj_layers()
{
return p->object_layers;
}
wxScrolledWindow* Sidebar::scrolled_panel() wxScrolledWindow* Sidebar::scrolled_panel()
{ {
return p->scrolled; return p->scrolled;
@ -2187,9 +2203,6 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
} }
object->ensure_on_bed(); object->ensure_on_bed();
// print.auto_assign_extruders(object);
// print.add_model_object(object);
} }
#ifdef AUTOPLACEMENT_ON_LOAD #ifdef AUTOPLACEMENT_ON_LOAD
@ -2959,8 +2972,14 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
// update plater with new config // update plater with new config
wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config()); wxGetApp().plater()->on_config_change(wxGetApp().preset_bundle->full_config());
/* Settings list can be changed after printer preset changing, so
* update all settings items for all item had it.
* Furthermore, Layers editing is implemented only for FFF printers
* and for SLA presets they should be deleted
*/
if (preset_type == Preset::TYPE_PRINTER) if (preset_type == Preset::TYPE_PRINTER)
wxGetApp().obj_list()->update_settings_items(); // wxGetApp().obj_list()->update_settings_items();
wxGetApp().obj_list()->update_object_list_by_printer_technology();
} }
void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
@ -3305,6 +3324,10 @@ bool Plater::priv::complit_init_object_menu()
[this]() { return can_split() && wxGetApp().get_mode() > comSimple; }, q); [this]() { return can_split() && wxGetApp().get_mode() > comSimple; }, q);
object_menu.AppendSeparator(); object_menu.AppendSeparator();
// Layers Editing for object
sidebar->obj_list()->append_menu_item_layers_editing(&object_menu);
object_menu.AppendSeparator();
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume() // "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
return true; return true;
@ -4263,6 +4286,11 @@ void Plater::msw_rescale()
GetParent()->Layout(); GetParent()->Layout();
} }
const Camera& Plater::get_camera() const
{
return p->camera;
}
bool Plater::can_delete() const { return p->can_delete(); } bool Plater::can_delete() const { return p->can_delete(); }
bool Plater::can_delete_all() const { return p->can_delete_all(); } bool Plater::can_delete_all() const { return p->can_delete_all(); }
bool Plater::can_increase_instances() const { return p->can_increase_instances(); } bool Plater::can_increase_instances() const { return p->can_increase_instances(); }

View file

@ -33,6 +33,7 @@ class MainFrame;
class ConfigOptionsGroup; class ConfigOptionsGroup;
class ObjectManipulation; class ObjectManipulation;
class ObjectSettings; class ObjectSettings;
class ObjectLayers;
class ObjectList; class ObjectList;
class GLCanvas3D; class GLCanvas3D;
@ -93,6 +94,7 @@ public:
ObjectManipulation* obj_manipul(); ObjectManipulation* obj_manipul();
ObjectList* obj_list(); ObjectList* obj_list();
ObjectSettings* obj_settings(); ObjectSettings* obj_settings();
ObjectLayers* obj_layers();
wxScrolledWindow* scrolled_panel(); wxScrolledWindow* scrolled_panel();
wxPanel* presets_panel(); wxPanel* presets_panel();
@ -218,6 +220,8 @@ public:
void msw_rescale(); void msw_rescale();
const Camera& get_camera() const;
private: private:
struct priv; struct priv;
std::unique_ptr<priv> p; std::unique_ptr<priv> p;

View file

@ -824,11 +824,25 @@ const Preset* PresetCollection::get_selected_preset_parent() const
if (this->get_selected_idx() == -1) if (this->get_selected_idx() == -1)
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid. // This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr; return nullptr;
const std::string &inherits = this->get_edited_preset().inherits(); // const std::string &inherits = this->get_edited_preset().inherits();
// if (inherits.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
std::string inherits = this->get_edited_preset().inherits();
if (inherits.empty()) if (inherits.empty())
return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr; {
if (this->get_selected_preset().is_system || this->get_selected_preset().is_default)
return &this->get_selected_preset();
if (this->get_selected_preset().is_external)
return nullptr;
inherits = m_type != Preset::Type::TYPE_PRINTER ? "- default -" :
this->get_edited_preset().printer_technology() == ptFFF ?
"- default FFF -" : "- default SLA -" ;
}
const Preset* preset = this->find_preset(inherits, false); const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr || preset->is_default || preset->is_external) ? nullptr : preset; return (preset == nullptr/* || preset->is_default*/ || preset->is_external) ? nullptr : preset;
} }
const Preset* PresetCollection::get_preset_parent(const Preset& child) const const Preset* PresetCollection::get_preset_parent(const Preset& child) const

View file

@ -6,7 +6,8 @@
#include "GUI_ObjectManipulation.hpp" #include "GUI_ObjectManipulation.hpp"
#include "GUI_ObjectList.hpp" #include "GUI_ObjectList.hpp"
#include "Gizmos/GLGizmoBase.hpp" #include "Gizmos/GLGizmoBase.hpp"
#include "slic3r/GUI/3DScene.hpp" #include "3DScene.hpp"
#include "Camera.hpp"
#include <GL/glew.h> #include <GL/glew.h>
@ -331,6 +332,9 @@ void Selection::clear()
// resets the cache in the sidebar // resets the cache in the sidebar
wxGetApp().obj_manipul()->reset_cache(); wxGetApp().obj_manipul()->reset_cache();
// #et_FIXME fake KillFocus from sidebar
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
} }
// Update the selection based on the new instance IDs. // Update the selection based on the new instance IDs.
@ -1070,61 +1074,68 @@ void Selection::render_center(bool gizmo_is_dragging) const
} }
#endif // ENABLE_RENDER_SELECTION_CENTER #endif // ENABLE_RENDER_SELECTION_CENTER
void Selection::render_sidebar_hints(const std::string& sidebar_field) const void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const
{ {
if (sidebar_field.empty()) if (sidebar_field.empty())
return; return;
glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); if (!boost::starts_with(sidebar_field, "layer"))
glsafe(::glEnable(GL_DEPTH_TEST)); {
shader.start_using();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_LIGHTING));
}
glsafe(::glEnable(GL_LIGHTING)); glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
const Vec3d& center = get_bounding_box().center(); if (!boost::starts_with(sidebar_field, "layer"))
if (is_single_full_instance() && ! wxGetApp().obj_manipul()->get_world_coordinates())
{ {
glsafe(::glTranslated(center(0), center(1), center(2))); const Vec3d& center = get_bounding_box().center();
if (!boost::starts_with(sidebar_field, "position"))
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates())
{ {
Transform3d orient_matrix = Transform3d::Identity(); glsafe(::glTranslated(center(0), center(1), center(2)));
if (boost::starts_with(sidebar_field, "scale")) if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::starts_with(sidebar_field, "rotation"))
{ {
if (boost::ends_with(sidebar_field, "x")) Transform3d orient_matrix = Transform3d::Identity();
if (boost::starts_with(sidebar_field, "scale"))
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else if (boost::ends_with(sidebar_field, "y")) else if (boost::starts_with(sidebar_field, "rotation"))
{ {
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation(); if (boost::ends_with(sidebar_field, "x"))
if (rotation(0) == 0.0)
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else else if (boost::ends_with(sidebar_field, "y"))
orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ())); {
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
if (rotation(0) == 0.0)
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
else
orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
}
} }
glsafe(::glMultMatrixd(orient_matrix.data()));
} }
}
else if (is_single_volume() || is_single_modifier())
{
glsafe(::glTranslated(center(0), center(1), center(2)));
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data())); glsafe(::glMultMatrixd(orient_matrix.data()));
} }
} else
else if (is_single_volume() || is_single_modifier())
{
glsafe(::glTranslated(center(0), center(1), center(2)));
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
if (!boost::starts_with(sidebar_field, "position"))
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
else
{
glsafe(::glTranslated(center(0), center(1), center(2)));
if (requires_local_axes())
{ {
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); glsafe(::glTranslated(center(0), center(1), center(2)));
glsafe(::glMultMatrixd(orient_matrix.data())); if (requires_local_axes())
{
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data()));
}
} }
} }
@ -1136,10 +1147,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
render_sidebar_scale_hints(sidebar_field); render_sidebar_scale_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "size")) else if (boost::starts_with(sidebar_field, "size"))
render_sidebar_size_hints(sidebar_field); render_sidebar_size_hints(sidebar_field);
else if (boost::starts_with(sidebar_field, "layer"))
render_sidebar_layers_hints(sidebar_field);
glsafe(::glPopMatrix()); glsafe(::glPopMatrix());
glsafe(::glDisable(GL_LIGHTING)); if (!boost::starts_with(sidebar_field, "layer"))
{
glsafe(::glDisable(GL_LIGHTING));
shader.stop_using();
}
} }
bool Selection::requires_local_axes() const bool Selection::requires_local_axes() const
@ -1163,7 +1180,7 @@ void Selection::copy_to_clipboard()
dst_object->config = src_object->config; dst_object->config = src_object->config;
dst_object->sla_support_points = src_object->sla_support_points; dst_object->sla_support_points = src_object->sla_support_points;
dst_object->sla_points_status = src_object->sla_points_status; dst_object->sla_points_status = src_object->sla_points_status;
dst_object->layer_height_ranges = src_object->layer_height_ranges; dst_object->layer_config_ranges = src_object->layer_config_ranges; // #ys_FIXME_experiment
dst_object->layer_height_profile = src_object->layer_height_profile; dst_object->layer_height_profile = src_object->layer_height_profile;
dst_object->origin_translation = src_object->origin_translation; dst_object->origin_translation = src_object->origin_translation;
@ -1709,6 +1726,78 @@ void Selection::render_sidebar_size_hints(const std::string& sidebar_field) cons
render_sidebar_scale_hints(sidebar_field); render_sidebar_scale_hints(sidebar_field);
} }
void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const
{
static const double Margin = 10.0;
std::string field = sidebar_field;
// extract max_z
std::string::size_type pos = field.rfind("_");
if (pos == std::string::npos)
return;
double max_z = std::stod(field.substr(pos + 1));
// extract min_z
field = field.substr(0, pos);
pos = field.rfind("_");
if (pos == std::string::npos)
return;
double min_z = std::stod(field.substr(pos + 1));
// extract type
field = field.substr(0, pos);
pos = field.rfind("_");
if (pos == std::string::npos)
return;
int type = std::stoi(field.substr(pos + 1));
const BoundingBoxf3& box = get_bounding_box();
const float min_x = box.min(0) - Margin;
const float max_x = box.max(0) + Margin;
const float min_y = box.min(1) - Margin;
const float max_y = box.max(1) + Margin;
// view dependend order of rendering to keep correct transparency
bool camera_on_top = wxGetApp().plater()->get_camera().get_theta() <= 90.0f;
float z1 = camera_on_top ? min_z : max_z;
float z2 = camera_on_top ? max_z : min_z;
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
::glBegin(GL_QUADS);
if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2)))
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
else
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
::glVertex3f(min_x, min_y, z1);
::glVertex3f(max_x, min_y, z1);
::glVertex3f(max_x, max_y, z1);
::glVertex3f(min_x, max_y, z1);
glsafe(::glEnd());
::glBegin(GL_QUADS);
if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1)))
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
else
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
::glVertex3f(min_x, min_y, z2);
::glVertex3f(max_x, min_y, z2);
::glVertex3f(max_x, max_y, z2);
::glVertex3f(min_x, max_y, z2);
glsafe(::glEnd());
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
}
void Selection::render_sidebar_position_hint(Axis axis) const void Selection::render_sidebar_position_hint(Axis axis) const
{ {
m_arrow.set_color(AXES_COLOR[axis], 3); m_arrow.set_color(AXES_COLOR[axis], 3);

View file

@ -11,8 +11,8 @@ typedef class GLUquadric GLUquadricObj;
#endif // ENABLE_RENDER_SELECTION_CENTER #endif // ENABLE_RENDER_SELECTION_CENTER
namespace Slic3r { namespace Slic3r {
class Shader;
namespace GUI { namespace GUI {
class TransformationType class TransformationType
{ {
public: public:
@ -302,7 +302,7 @@ public:
#if ENABLE_RENDER_SELECTION_CENTER #if ENABLE_RENDER_SELECTION_CENTER
void render_center(bool gizmo_is_dragging) const; void render_center(bool gizmo_is_dragging) const;
#endif // ENABLE_RENDER_SELECTION_CENTER #endif // ENABLE_RENDER_SELECTION_CENTER
void render_sidebar_hints(const std::string& sidebar_field) const; void render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const;
bool requires_local_axes() const; bool requires_local_axes() const;
@ -332,6 +332,7 @@ private:
void render_sidebar_rotation_hints(const std::string& sidebar_field) const; void render_sidebar_rotation_hints(const std::string& sidebar_field) const;
void render_sidebar_scale_hints(const std::string& sidebar_field) const; void render_sidebar_scale_hints(const std::string& sidebar_field) const;
void render_sidebar_size_hints(const std::string& sidebar_field) const; void render_sidebar_size_hints(const std::string& sidebar_field) const;
void render_sidebar_layers_hints(const std::string& sidebar_field) const;
void render_sidebar_position_hint(Axis axis) const; void render_sidebar_position_hint(Axis axis) const;
void render_sidebar_rotation_hint(Axis axis) const; void render_sidebar_rotation_hint(Axis axis) const;
void render_sidebar_scale_hint(Axis axis) const; void render_sidebar_scale_hint(Axis axis) const;

View file

@ -423,7 +423,7 @@ void Tab::update_changed_ui()
const ScalableBitmap *sys_icon = &m_bmp_value_lock; const ScalableBitmap *sys_icon = &m_bmp_value_lock;
const ScalableBitmap *icon = &m_bmp_value_revert; const ScalableBitmap *icon = &m_bmp_value_revert;
const wxColour *color = &m_sys_label_clr; const wxColour *color = m_is_default_preset ? &m_default_text_clr : &m_sys_label_clr;
const wxString *sys_tt = &m_tt_value_lock; const wxString *sys_tt = &m_tt_value_lock;
const wxString *tt = &m_tt_value_revert; const wxString *tt = &m_tt_value_revert;
@ -590,7 +590,7 @@ void Tab::update_changed_tree_ui()
} }
} }
const wxColor *clr = sys_page ? &m_sys_label_clr : const wxColor *clr = sys_page ? (m_is_default_preset ? &m_default_text_clr : &m_sys_label_clr) :
modified_page ? &m_modified_label_clr : modified_page ? &m_modified_label_clr :
&m_default_text_clr; &m_default_text_clr;
@ -2584,11 +2584,14 @@ void Tab::load_current_preset()
// Reload preset pages with the new configuration values. // Reload preset pages with the new configuration values.
reload_config(); reload_config();
m_bmp_non_system = m_presets->get_selected_preset_parent() ? &m_bmp_value_unlock : &m_bmp_white_bullet; const Preset* selected_preset_parent = m_presets->get_selected_preset_parent();
m_ttg_non_system = m_presets->get_selected_preset_parent() ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns; m_is_default_preset = selected_preset_parent != nullptr && selected_preset_parent->is_default;
m_tt_non_system = m_presets->get_selected_preset_parent() ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
m_undo_to_sys_btn->Enable(!preset.is_default); m_bmp_non_system = selected_preset_parent ? &m_bmp_value_unlock : &m_bmp_white_bullet;
m_ttg_non_system = selected_preset_parent ? &m_ttg_value_unlock : &m_ttg_white_bullet_ns;
m_tt_non_system = selected_preset_parent ? &m_tt_value_unlock : &m_ttg_white_bullet_ns;
// m_undo_to_sys_btn->Enable(!preset.is_default);
#if 0 #if 0
// use CallAfter because some field triggers schedule on_change calls using CallAfter, // use CallAfter because some field triggers schedule on_change calls using CallAfter,
@ -3174,18 +3177,18 @@ void Tab::fill_icon_descriptions()
{ {
m_icon_descriptions.emplace_back(&m_bmp_value_lock, L("LOCKED LOCK"), m_icon_descriptions.emplace_back(&m_bmp_value_lock, L("LOCKED LOCK"),
// TRN Description for "LOCKED LOCK" // TRN Description for "LOCKED LOCK"
L("indicates that the settings are the same as the system values for the current option group")); L("indicates that the settings are the same as the system (or default) values for the current option group"));
m_icon_descriptions.emplace_back(&m_bmp_value_unlock, L("UNLOCKED LOCK"), m_icon_descriptions.emplace_back(&m_bmp_value_unlock, L("UNLOCKED LOCK"),
// TRN Description for "UNLOCKED LOCK" // TRN Description for "UNLOCKED LOCK"
L("indicates that some settings were changed and are not equal to the system values for " L("indicates that some settings were changed and are not equal to the system (or default) values for "
"the current option group.\n" "the current option group.\n"
"Click the UNLOCKED LOCK icon to reset all settings for current option group to " "Click the UNLOCKED LOCK icon to reset all settings for current option group to "
"the system values.")); "the system (or default) values."));
m_icon_descriptions.emplace_back(&m_bmp_white_bullet, L("WHITE BULLET"), m_icon_descriptions.emplace_back(&m_bmp_white_bullet, L("WHITE BULLET"),
// TRN Description for "WHITE BULLET" // TRN Description for "WHITE BULLET"
L("for the left button: \tindicates a non-system preset,\n" L("for the left button: \tindicates a non-system (or non-default) preset,\n"
"for the right button: \tindicates that the settings hasn't been modified.")); "for the right button: \tindicates that the settings hasn't been modified."));
m_icon_descriptions.emplace_back(&m_bmp_value_revert, L("BACK ARROW"), m_icon_descriptions.emplace_back(&m_bmp_value_revert, L("BACK ARROW"),
@ -3198,29 +3201,14 @@ void Tab::fill_icon_descriptions()
void Tab::set_tooltips_text() void Tab::set_tooltips_text()
{ {
// m_undo_to_sys_btn->SetToolTip(_(L( "LOCKED LOCK icon indicates that the settings are the same as the system values "
// "for the current option group.\n"
// "UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
// "to the system values for the current option group.\n"
// "WHITE BULLET icon indicates a non system preset.\n\n"
// "Click the UNLOCKED LOCK icon to reset all settings for current option group to "
// "the system values.")));
//
// m_undo_btn->SetToolTip(_(L( "WHITE BULLET icon indicates that the settings are the same as in the last saved"
// "preset for the current option group.\n"
// "BACK ARROW icon indicates that the settings were changed and are not equal to "
// "the last saved preset for the current option group.\n\n"
// "Click the BACK ARROW icon to reset all settings for the current option group to "
// "the last saved preset.")));
// --- Tooltip text for reset buttons (for whole options group) // --- Tooltip text for reset buttons (for whole options group)
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field. // Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system values " m_ttg_value_lock = _(L("LOCKED LOCK icon indicates that the settings are the same as the system (or default) values "
"for the current option group")); "for the current option group"));
m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal " m_ttg_value_unlock = _(L("UNLOCKED LOCK icon indicates that some settings were changed and are not equal "
"to the system values for the current option group.\n" "to the system (or default) values for the current option group.\n"
"Click to reset all settings for current option group to the system values.")); "Click to reset all settings for current option group to the system (or default) values."));
m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system preset.")); m_ttg_white_bullet_ns = _(L("WHITE BULLET icon indicates a non system (or non default) preset."));
m_ttg_non_system = &m_ttg_white_bullet_ns; m_ttg_non_system = &m_ttg_white_bullet_ns;
// Text to be shown on the "Undo user changes" button next to each input field. // Text to be shown on the "Undo user changes" button next to each input field.
m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved " m_ttg_white_bullet = _(L("WHITE BULLET icon indicates that the settings are the same as in the last saved "
@ -3231,10 +3219,10 @@ void Tab::set_tooltips_text()
// --- Tooltip text for reset buttons (for each option in group) // --- Tooltip text for reset buttons (for each option in group)
// Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field. // Text to be shown on the "Revert to system" aka "Lock to system" button next to each input field.
m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system value.")); m_tt_value_lock = _(L("LOCKED LOCK icon indicates that the value is the same as the system (or default) value."));
m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal " m_tt_value_unlock = _(L("UNLOCKED LOCK icon indicates that the value was changed and is not equal "
"to the system value.\n" "to the system (or default) value.\n"
"Click to reset current value to the system value.")); "Click to reset current value to the system (or default) value."));
// m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset.")); // m_tt_white_bullet_ns= _(L("WHITE BULLET icon indicates a non system preset."));
m_tt_non_system = &m_ttg_white_bullet_ns; m_tt_non_system = &m_ttg_white_bullet_ns;
// Text to be shown on the "Undo user changes" button next to each input field. // Text to be shown on the "Undo user changes" button next to each input field.
@ -3488,9 +3476,9 @@ void TabSLAMaterial::reload_config()
void TabSLAMaterial::update() void TabSLAMaterial::update()
{ {
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
return; // #ys_FIXME return;
// #ys_FIXME // #ys_FIXME. Just a template for this function
// m_update_cnt++; // m_update_cnt++;
// ! something to update // ! something to update
// m_update_cnt--; // m_update_cnt--;
@ -3588,9 +3576,8 @@ void TabSLAPrint::reload_config()
void TabSLAPrint::update() void TabSLAPrint::update()
{ {
if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF) if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
return; // #ys_FIXME return;
// #ys_FIXME
m_update_cnt++; m_update_cnt++;
double head_penetration = m_config->opt_float("support_head_penetration"); double head_penetration = m_config->opt_float("support_head_penetration");

View file

@ -142,6 +142,12 @@ protected:
PresetDependencies m_compatible_printers; PresetDependencies m_compatible_printers;
PresetDependencies m_compatible_prints; PresetDependencies m_compatible_prints;
/* Indicates, that default preset or preset inherited from default is selected
* This value is used for a options color updating
* (use green color only for options, which values are equal to system values)
*/
bool m_is_default_preset {false};
ScalableButton* m_undo_btn; ScalableButton* m_undo_btn;
ScalableButton* m_undo_to_sys_btn; ScalableButton* m_undo_to_sys_btn;
ScalableButton* m_question_btn; ScalableButton* m_question_btn;

View file

@ -437,27 +437,69 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
m_type(type), m_type(type),
m_extruder(wxEmptyString) m_extruder(wxEmptyString)
{ {
if (type == itSettings) { if (type == itSettings)
m_name = "Settings to modified"; m_name = "Settings to modified";
} else if (type == itInstanceRoot)
else if (type == itInstanceRoot) {
m_name = _(L("Instances")); m_name = _(L("Instances"));
#ifdef __WXGTK__ else if (type == itInstance)
m_container = true; {
#endif //__WXGTK__
}
else if (type == itInstance) {
m_idx = parent->GetChildCount(); m_idx = parent->GetChildCount();
m_name = wxString::Format(_(L("Instance %d")), m_idx + 1); m_name = wxString::Format(_(L("Instance %d")), m_idx + 1);
set_action_icon(); set_action_icon();
} }
else if (type == itLayerRoot)
{
m_bmp = create_scaled_bitmap(nullptr, "layers"); // FIXME: pass window ptr
m_name = _(L("Layers"));
}
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
if (type & (itInstanceRoot | itLayerRoot))
m_container = true;
#endif //__WXGTK__
}
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
const int idx /*= -1 */,
const wxString& extruder) :
m_parent(parent),
m_type(itLayer),
m_idx(idx),
m_layer_range(layer_range),
m_extruder(extruder)
{
const int children_cnt = parent->GetChildCount();
if (idx < 0)
m_idx = children_cnt;
else
{
// update indexes for another Laeyr Nodes
for (int i = m_idx; i < children_cnt; i++)
parent->GetNthChild(i)->SetIdx(i + 1);
}
const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str();
m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")";
m_bmp = create_scaled_bitmap(nullptr, "layers_white"); // FIXME: pass window ptr
#ifdef __WXGTK__
// it's necessary on GTK because of control have to know if this item will be container
// in another case you couldn't to add subitem for this item
// it will be produce "segmentation fault"
m_container = true;
#endif //__WXGTK__
set_action_icon();
} }
void ObjectDataViewModelNode::set_action_icon() void ObjectDataViewModelNode::set_action_icon()
{ {
m_action_icon_name = m_type == itObject ? "advanced_plus" : m_action_icon_name = m_type & itObject ? "advanced_plus" :
m_type == itVolume ? "cog" : "set_separate_obj"; m_type & (itVolume | itLayer) ? "cog" : /*m_type & itInstance*/ "set_separate_obj";
m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); // FIXME: pass window ptr m_action_icon = create_scaled_bitmap(nullptr, m_action_icon_name); // FIXME: pass window ptr
} }
@ -523,6 +565,22 @@ void ObjectDataViewModelNode::SetIdx(const int& idx)
// ObjectDataViewModel // ObjectDataViewModel
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType root_type)
{
// because of istance_root and layers_root are at the end of the list, so
// start locking from the end
for (int root_idx = parent_node->GetChildCount() - 1; root_idx >= 0; root_idx--)
{
// if there is SettingsItem or VolumeItem, then RootItems don't exist in current ObjectItem
if (parent_node->GetNthChild(root_idx)->GetType() & (itSettings | itVolume))
break;
if (parent_node->GetNthChild(root_idx)->GetType() & root_type)
return root_idx;
}
return -1;
}
ObjectDataViewModel::ObjectDataViewModel() ObjectDataViewModel::ObjectDataViewModel()
{ {
m_bitmap_cache = new Slic3r::GUI::BitmapCache; m_bitmap_cache = new Slic3r::GUI::BitmapCache;
@ -567,10 +625,10 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder); wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
// because of istance_root is a last item of the object // get insertion position according to the existed Layers and/or Instances Items
int insert_position = root->GetChildCount() - 1; int insert_position = get_root_idx(root, itLayerRoot);
if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot) if (insert_position < 0)
insert_position = -1; insert_position = get_root_idx(root, itInstanceRoot);
const bool obj_errors = root->m_bmp.IsOk(); const bool obj_errors = root->m_bmp.IsOk();
@ -619,15 +677,30 @@ wxDataViewItem ObjectDataViewModel::AddSettingsChild(const wxDataViewItem &paren
return child; return child;
} }
int get_istances_root_idx(ObjectDataViewModelNode *parent_node) /* return values:
* true => root_node is created and added to the parent_root
* false => root node alredy exists
*/
static bool append_root_node(ObjectDataViewModelNode *parent_node,
ObjectDataViewModelNode **root_node,
const ItemType root_type)
{ {
// because of istance_root is a last item of the object const int inst_root_id = get_root_idx(parent_node, root_type);
const int inst_root_idx = parent_node->GetChildCount()-1;
if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->GetType() == itInstanceRoot) *root_node = inst_root_id < 0 ?
return inst_root_idx; new ObjectDataViewModelNode(parent_node, root_type) :
parent_node->GetNthChild(inst_root_id);
return -1; if (inst_root_id < 0) {
if ((root_type&itInstanceRoot) ||
(root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0)
parent_node->Append(*root_node);
else if (root_type&itLayerRoot)
parent_node->Insert(*root_node, static_cast<unsigned int>(get_root_idx(parent_node, itInstanceRoot)));
return true;
}
return false;
} }
wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num) wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num)
@ -635,20 +708,15 @@ wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &paren
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return wxDataViewItem(0); if (!parent_node) return wxDataViewItem(0);
// Check and create/get instances root node // get InstanceRoot node
const int inst_root_id = get_istances_root_idx(parent_node); ObjectDataViewModelNode *inst_root_node { nullptr };
ObjectDataViewModelNode *inst_root_node = inst_root_id < 0 ? const bool appended = append_root_node(parent_node, &inst_root_node, itInstanceRoot);
new ObjectDataViewModelNode(parent_node, itInstanceRoot) :
parent_node->GetNthChild(inst_root_id);
const wxDataViewItem inst_root_item((void*)inst_root_node); const wxDataViewItem inst_root_item((void*)inst_root_node);
if (!inst_root_node) return wxDataViewItem(0);
if (inst_root_id < 0) { if (appended)
parent_node->Append(inst_root_node); ItemAdded(parent_item, inst_root_item);// notify control
// notify control
ItemAdded(parent_item, inst_root_item);
// if (num == 1) num++;
}
// Add instance nodes // Add instance nodes
ObjectDataViewModelNode *instance_node = nullptr; ObjectDataViewModelNode *instance_node = nullptr;
@ -665,6 +733,63 @@ wxDataViewItem ObjectDataViewModel::AddInstanceChild(const wxDataViewItem &paren
return wxDataViewItem((void*)instance_node); return wxDataViewItem((void*)instance_node);
} }
wxDataViewItem ObjectDataViewModel::AddLayersRoot(const wxDataViewItem &parent_item)
{
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return wxDataViewItem(0);
// get LayerRoot node
ObjectDataViewModelNode *layer_root_node{ nullptr };
const bool appended = append_root_node(parent_node, &layer_root_node, itLayerRoot);
if (!layer_root_node) return wxDataViewItem(0);
const wxDataViewItem layer_root_item((void*)layer_root_node);
if (appended)
ItemAdded(parent_item, layer_root_item);// notify control
return layer_root_item;
}
wxDataViewItem ObjectDataViewModel::AddLayersChild(const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder/* = 0*/,
const int index /* = -1*/)
{
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return wxDataViewItem(0);
wxString extruder_str = extruder == 0 ? _(L("default")) : wxString::Format("%d", extruder);
// get LayerRoot node
ObjectDataViewModelNode *layer_root_node;
wxDataViewItem layer_root_item;
if (parent_node->GetType() & itLayerRoot) {
layer_root_node = parent_node;
layer_root_item = parent_item;
}
else {
const int root_idx = get_root_idx(parent_node, itLayerRoot);
if (root_idx < 0) return wxDataViewItem(0);
layer_root_node = parent_node->GetNthChild(root_idx);
layer_root_item = wxDataViewItem((void*)layer_root_node);
}
// Add layer node
ObjectDataViewModelNode *layer_node = new ObjectDataViewModelNode(layer_root_node, layer_range, index, extruder_str);
if (index < 0)
layer_root_node->Append(layer_node);
else
layer_root_node->Insert(layer_node, index);
// notify control
const wxDataViewItem layer_item((void*)layer_node);
ItemAdded(layer_root_item, layer_item);
return layer_item;
}
wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
{ {
auto ret_item = wxDataViewItem(0); auto ret_item = wxDataViewItem(0);
@ -679,9 +804,9 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_ // NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it // thus removing the node from it doesn't result in freeing it
if (node_parent) { if (node_parent) {
if (node->m_type == itInstanceRoot) if (node->m_type & (itInstanceRoot|itLayerRoot))
{ {
for (int i = node->GetChildCount() - 1; i > 0; i--) for (int i = node->GetChildCount() - 1; i >= (node->m_type & itInstanceRoot ? 1 : 0); i--)
Delete(wxDataViewItem(node->GetNthChild(i))); Delete(wxDataViewItem(node->GetNthChild(i)));
return parent; return parent;
} }
@ -690,7 +815,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
auto idx = node->GetIdx(); auto idx = node->GetIdx();
if (node->m_type == itVolume) { if (node->m_type & (itVolume|itLayer)) {
node_parent->m_volumes_cnt--; node_parent->m_volumes_cnt--;
DeleteSettings(item); DeleteSettings(item);
} }
@ -726,6 +851,22 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
delete node_parent; delete node_parent;
ret_item = wxDataViewItem(obj_node); ret_item = wxDataViewItem(obj_node);
#ifndef __WXGTK__
if (obj_node->GetChildCount() == 0)
obj_node->m_container = false;
#endif //__WXGTK__
ItemDeleted(ret_item, wxDataViewItem(node_parent));
return ret_item;
}
// if there was last layer item, delete this one and layers root item
if (node_parent->GetChildCount() == 0 && node_parent->m_type == itLayerRoot)
{
ObjectDataViewModelNode *obj_node = node_parent->GetParent();
obj_node->GetChildren().Remove(node_parent);
delete node_parent;
ret_item = wxDataViewItem(obj_node);
#ifndef __WXGTK__ #ifndef __WXGTK__
if (obj_node->GetChildCount() == 0) if (obj_node->GetChildCount() == 0)
obj_node->m_container = false; obj_node->m_container = false;
@ -735,7 +876,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item)
} }
// if there is last volume item after deleting, delete this last volume too // if there is last volume item after deleting, delete this last volume too
if (node_parent->GetChildCount() <= 3) if (node_parent->GetChildCount() <= 3) // 3??? #ys_FIXME
{ {
int vol_cnt = 0; int vol_cnt = 0;
int vol_idx = 0; int vol_idx = 0;
@ -817,7 +958,7 @@ wxDataViewItem ObjectDataViewModel::DeleteLastInstance(const wxDataViewItem &par
ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID(); ObjectDataViewModelNode *parent_node = (ObjectDataViewModelNode*)parent_item.GetID();
if (!parent_node) return ret_item; if (!parent_node) return ret_item;
const int inst_root_id = get_istances_root_idx(parent_node); const int inst_root_id = get_root_idx(parent_node, itInstanceRoot);
if (inst_root_id < 0) return ret_item; if (inst_root_id < 0) return ret_item;
wxDataViewItemArray items; wxDataViewItemArray items;
@ -974,28 +1115,67 @@ wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_id
return wxDataViewItem(0); return wxDataViewItem(0);
} }
wxDataViewItem ObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx) wxDataViewItem ObjectDataViewModel::GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type)
{ {
if (obj_idx >= m_objects.size() || obj_idx < 0) { if (obj_idx >= m_objects.size() || obj_idx < 0) {
printf("Error! Out of objects range.\n"); printf("Error! Out of objects range.\n");
return wxDataViewItem(0); return wxDataViewItem(0);
} }
auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx])); auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), parent_type);
if (!instances_item) if (!item)
return wxDataViewItem(0); return wxDataViewItem(0);
auto parent = (ObjectDataViewModelNode*)instances_item.GetID();; auto parent = (ObjectDataViewModelNode*)item.GetID();
for (size_t i = 0; i < parent->GetChildCount(); i++) for (size_t i = 0; i < parent->GetChildCount(); i++)
if (parent->GetNthChild(i)->m_idx == inst_idx) if (parent->GetNthChild(i)->m_idx == sub_obj_idx)
return wxDataViewItem(parent->GetNthChild(i)); return wxDataViewItem(parent->GetNthChild(i));
return wxDataViewItem(0); return wxDataViewItem(0);
} }
wxDataViewItem ObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx)
{
return GetItemById(obj_idx, inst_idx, itInstanceRoot);
}
wxDataViewItem ObjectDataViewModel::GetItemByLayerId(int obj_idx, int layer_idx)
{
return GetItemById(obj_idx, layer_idx, itLayerRoot);
}
wxDataViewItem ObjectDataViewModel::GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range)
{
if (obj_idx >= m_objects.size() || obj_idx < 0) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
auto item = GetItemByType(wxDataViewItem(m_objects[obj_idx]), itLayerRoot);
if (!item)
return wxDataViewItem(0);
auto parent = (ObjectDataViewModelNode*)item.GetID();
for (size_t i = 0; i < parent->GetChildCount(); i++)
if (parent->GetNthChild(i)->m_layer_range == layer_range)
return wxDataViewItem(parent->GetNthChild(i));
return wxDataViewItem(0);
}
int ObjectDataViewModel::GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range)
{
wxDataViewItem item = GetItemByLayerRange(obj_idx, layer_range);
if (!item)
return -1;
return GetLayerIdByItem(item);
}
int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const int ObjectDataViewModel::GetIdByItem(const wxDataViewItem& item) const
{ {
wxASSERT(item.IsOk()); if(!item.IsOk())
return -1;
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
auto it = find(m_objects.begin(), m_objects.end(), node); auto it = find(m_objects.begin(), m_objects.end(), node);
@ -1030,13 +1210,28 @@ int ObjectDataViewModel::GetInstanceIdByItem(const wxDataViewItem& item) const
return GetIdByItemAndType(item, itInstance); return GetIdByItemAndType(item, itInstance);
} }
int ObjectDataViewModel::GetLayerIdByItem(const wxDataViewItem& item) const
{
return GetIdByItemAndType(item, itLayer);
}
t_layer_height_range ObjectDataViewModel::GetLayerRangeByItem(const wxDataViewItem& item) const
{
wxASSERT(item.IsOk());
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
if (!node || node->m_type != itLayer)
return { 0.0f, 0.0f };
return node->GetLayerRange();
}
void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx) void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx)
{ {
wxASSERT(item.IsOk()); wxASSERT(item.IsOk());
type = itUndef; type = itUndef;
ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID();
if (!node || node->GetIdx() <-1 || node->GetIdx() ==-1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot))) if (!node || node->GetIdx() <-1 || node->GetIdx() == -1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)))
return; return;
idx = node->GetIdx(); idx = node->GetIdx();
@ -1044,9 +1239,10 @@ void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type
ObjectDataViewModelNode *parent_node = node->GetParent(); ObjectDataViewModelNode *parent_node = node->GetParent();
if (!parent_node) return; if (!parent_node) return;
if (type == itInstance)
parent_node = node->GetParent()->GetParent(); // get top parent (Object) node
if (!parent_node || parent_node->m_type != itObject) { type = itUndef; return; } while (parent_node->m_type != itObject)
parent_node = parent_node->GetParent();
auto it = find(m_objects.begin(), m_objects.end(), parent_node); auto it = find(m_objects.begin(), m_objects.end(), parent_node);
if (it != m_objects.end()) if (it != m_objects.end())
@ -1214,10 +1410,7 @@ wxDataViewItem ObjectDataViewModel::GetTopParent(const wxDataViewItem &item) con
ObjectDataViewModelNode *parent_node = node->GetParent(); ObjectDataViewModelNode *parent_node = node->GetParent();
while (parent_node->m_type != itObject) while (parent_node->m_type != itObject)
{ parent_node = parent_node->GetParent();
node = parent_node;
parent_node = node->GetParent();
}
return wxDataViewItem((void*)parent_node); return wxDataViewItem((void*)parent_node);
} }
@ -1318,6 +1511,11 @@ wxDataViewItem ObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &it
return GetItemByType(item, itInstanceRoot); return GetItemByType(item, itInstanceRoot);
} }
wxDataViewItem ObjectDataViewModel::GetLayerRootItem(const wxDataViewItem &item) const
{
return GetItemByType(item, itLayerRoot);
}
bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const bool ObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
{ {
if (!item.IsOk()) if (!item.IsOk())
@ -2653,7 +2851,7 @@ ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 10*/) :
m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first));; m_mode_btns.push_back(new ModeButton(parent, wxID_ANY, button.second, button.first));;
#endif // __WXOSX__ #endif // __WXOSX__
m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, m_mode_btns.size() - 1)); m_mode_btns.back()->Bind(wxEVT_BUTTON, std::bind(modebtnfn, std::placeholders::_1, int(m_mode_btns.size() - 1)));
Add(m_mode_btns.back()); Add(m_mode_btns.back());
} }
} }

View file

@ -20,6 +20,9 @@ namespace Slic3r {
enum class ModelVolumeType : int; enum class ModelVolumeType : int;
}; };
typedef double coordf_t;
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
#ifdef __WXMSW__ #ifdef __WXMSW__
void msw_rescale_menu(wxMenu* menu); void msw_rescale_menu(wxMenu* menu);
#else /* __WXMSW__ */ #else /* __WXMSW__ */
@ -159,12 +162,14 @@ DECLARE_VARIANT_OBJECT(DataViewBitmapText)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
enum ItemType { enum ItemType {
itUndef = 0, itUndef = 0,
itObject = 1, itObject = 1,
itVolume = 2, itVolume = 2,
itInstanceRoot = 4, itInstanceRoot = 4,
itInstance = 8, itInstance = 8,
itSettings = 16 itSettings = 16,
itLayerRoot = 32,
itLayer = 64,
}; };
class ObjectDataViewModelNode; class ObjectDataViewModelNode;
@ -177,6 +182,7 @@ class ObjectDataViewModelNode
wxBitmap m_empty_bmp; wxBitmap m_empty_bmp;
size_t m_volumes_cnt = 0; size_t m_volumes_cnt = 0;
std::vector< std::string > m_opt_categories; std::vector< std::string > m_opt_categories;
t_layer_height_range m_layer_range = { 0.0f, 0.0f };
wxString m_name; wxString m_name;
wxBitmap& m_bmp = m_empty_bmp; wxBitmap& m_bmp = m_empty_bmp;
@ -229,6 +235,11 @@ public:
set_action_icon(); set_action_icon();
} }
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
const t_layer_height_range& layer_range,
const int idx = -1,
const wxString& extruder = wxEmptyString );
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type); ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
~ObjectDataViewModelNode() ~ObjectDataViewModelNode()
@ -318,6 +329,7 @@ public:
ItemType GetType() const { return m_type; } ItemType GetType() const { return m_type; }
void SetIdx(const int& idx); void SetIdx(const int& idx);
int GetIdx() const { return m_idx; } int GetIdx() const { return m_idx; }
t_layer_height_range GetLayerRange() const { return m_layer_range; }
// use this function only for childrens // use this function only for childrens
void AssignAllVal(ObjectDataViewModelNode& from_node) void AssignAllVal(ObjectDataViewModelNode& from_node)
@ -348,7 +360,7 @@ public:
} }
// Set action icons for node // Set action icons for node
void set_action_icon(); void set_action_icon();
void update_settings_digest_bitmaps(); void update_settings_digest_bitmaps();
bool update_settings_digest(const std::vector<std::string>& categories); bool update_settings_digest(const std::vector<std::string>& categories);
@ -388,6 +400,11 @@ public:
const bool create_frst_child = true); const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item); wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num); wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
const t_layer_height_range& layer_range,
const int extruder = 0,
const int index = -1);
wxDataViewItem Delete(const wxDataViewItem &item); wxDataViewItem Delete(const wxDataViewItem &item);
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num); wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll(); void DeleteAll();
@ -395,13 +412,18 @@ public:
void DeleteVolumeChildren(wxDataViewItem& parent); void DeleteVolumeChildren(wxDataViewItem& parent);
void DeleteSettings(const wxDataViewItem& parent); void DeleteSettings(const wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx); wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx); wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx); wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
int GetIdByItem(const wxDataViewItem& item) const; int GetIdByItem(const wxDataViewItem& item) const;
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const; int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetObjectIdByItem(const wxDataViewItem& item) const; int GetObjectIdByItem(const wxDataViewItem& item) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const; int GetVolumeIdByItem(const wxDataViewItem& item) const;
int GetInstanceIdByItem(const wxDataViewItem& item) const; int GetInstanceIdByItem(const wxDataViewItem& item) const;
int GetLayerIdByItem(const wxDataViewItem& item) const;
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx); void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
int GetRowByItem(const wxDataViewItem& item) const; int GetRowByItem(const wxDataViewItem& item) const;
bool IsEmpty() { return m_objects.empty(); } bool IsEmpty() { return m_objects.empty(); }
@ -450,6 +472,7 @@ public:
ItemType type) const; ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const; wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const; wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const; bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest( const wxDataViewItem &item, void UpdateSettingsDigest( const wxDataViewItem &item,
const std::vector<std::string>& categories); const std::vector<std::string>& categories);
@ -465,6 +488,7 @@ public:
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
const bool is_marked = false); const bool is_marked = false);
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false); void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View file

@ -89,7 +89,7 @@ plan tests => 8;
# we disable combination after infill has been generated # we disable combination after infill has been generated
$config->set('infill_every_layers', 1); $config->set('infill_every_layers', 1);
$print->apply_config_perl_tests_only($config); $print->apply($print->print->model->clone, $config);
$print->process; $print->process;
ok !(defined first { @{$_->get_region(0)->fill_surfaces} == 0 } ok !(defined first { @{$_->get_region(0)->fill_surfaces} == 0 }

View file

@ -34,25 +34,30 @@ use Slic3r::Test;
{ {
# this represents the aggregate config from presets # this represents the aggregate config from presets
my $config = Slic3r::Config::new_from_defaults; my $config = Slic3r::Config::new_from_defaults;
# Define 4 extruders.
$config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]);
# user adds one object to the plater # user adds one object to the plater
my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config); my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config);
# user sets a per-region option # user sets a per-region option
$print->print->objects->[0]->model_object->config->set('fill_density', 100); my $model2 = $model->clone;
$print->print->reload_object(0); $model2->get_object(0)->config->set('fill_density', 100);
$print->apply($model2, $config);
is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config'; is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config';
# user exports G-code, thus the default config is reapplied # user exports G-code, thus the default config is reapplied
$print->print->apply_config_perl_tests_only($config); $model2->get_object(0)->config->erase('fill_density');
$print->apply($model2, $config);
is $print->print->regions->[0]->config->fill_density, 100, 'apply_config() does not override per-object settings';
is $print->print->regions->[0]->config->fill_density, 20, 'region config is resetted';
# user assigns object extruders # user assigns object extruders
$print->print->objects->[0]->model_object->config->set('extruder', 3); $model2->get_object(0)->config->set('extruder', 3);
$print->print->objects->[0]->model_object->config->set('perimeter_extruder', 2); $model2->get_object(0)->config->set('perimeter_extruder', 2);
$print->print->reload_object(0); $print->apply($model2, $config);
is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded'; is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded';
is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders'; is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders';
} }

View file

@ -91,6 +91,8 @@ use Slic3r::Test;
{ {
my $config = Slic3r::Config::new_from_defaults; my $config = Slic3r::Config::new_from_defaults;
# Define 4 extruders.
$config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]);
$config->set('layer_height', 0.4); $config->set('layer_height', 0.4);
$config->set('first_layer_height', 0.4); $config->set('first_layer_height', 0.4);
$config->set('skirts', 1); $config->set('skirts', 1);
@ -106,7 +108,7 @@ use Slic3r::Test;
# we enable support material after skirt has been generated # we enable support material after skirt has been generated
$config->set('support_material', 1); $config->set('support_material', 1);
$print->apply_config_perl_tests_only($config); $print->apply($print->print->model->clone, $config);
my $skirt_length = 0; my $skirt_length = 0;
my @extrusion_points = (); my @extrusion_points = ();

View file

@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use Slic3r::XS; use Slic3r::XS;
use Test::More tests => 4; use Test::More tests => 3;
{ {
my $model = Slic3r::Model->new; my $model = Slic3r::Model->new;
@ -14,9 +14,9 @@ use Test::More tests => 4;
$object->origin_translation->translate(10,0,0); $object->origin_translation->translate(10,0,0);
is_deeply \@{$object->origin_translation}, [10,0,0], 'origin_translation is modified by ref'; is_deeply \@{$object->origin_translation}, [10,0,0], 'origin_translation is modified by ref';
my $lhr = [ [ 5, 10, 0.1 ] ]; # my $lhr = [ [ 5, 10, 0.1 ] ];
$object->set_layer_height_ranges($lhr); # $object->set_layer_height_ranges($lhr);
is_deeply $object->layer_height_ranges, $lhr, 'layer_height_ranges roundtrip'; # is_deeply $object->layer_height_ranges, $lhr, 'layer_height_ranges roundtrip';
} }
__END__ __END__

View file

@ -49,7 +49,7 @@
void erase(t_config_option_key opt_key); void erase(t_config_option_key opt_key);
void normalize(); void normalize();
%name{setenv} void setenv_(); %name{setenv} void setenv_();
double min_object_distance() %code{% RETVAL = PrintConfig::min_object_distance(THIS); %}; double min_object_distance() %code{% PrintConfig cfg; cfg.apply(*THIS, true); RETVAL = cfg.min_object_distance(); %};
static DynamicPrintConfig* load(char *path) static DynamicPrintConfig* load(char *path)
%code%{ %code%{
auto config = new DynamicPrintConfig(); auto config = new DynamicPrintConfig();

View file

@ -206,16 +206,12 @@ ModelMaterial::attributes()
Ref<Model> model() Ref<Model> model()
%code%{ RETVAL = THIS->get_model(); %}; %code%{ RETVAL = THIS->get_model(); %};
t_layer_height_ranges layer_height_ranges()
%code%{ RETVAL = THIS->layer_height_ranges; %};
void set_layer_height_ranges(t_layer_height_ranges ranges)
%code%{ THIS->layer_height_ranges = ranges; %};
Ref<Vec3d> origin_translation() Ref<Vec3d> origin_translation()
%code%{ RETVAL = &THIS->origin_translation; %}; %code%{ RETVAL = &THIS->origin_translation; %};
void set_origin_translation(Vec3d* point) void set_origin_translation(Vec3d* point)
%code%{ THIS->origin_translation = *point; %}; %code%{ THIS->origin_translation = *point; %};
void ensure_on_bed();
bool needed_repair() const; bool needed_repair() const;
int materials_count() const; int materials_count() const;
int facets_count(); int facets_count();

View file

@ -70,6 +70,8 @@ _constant()
Print(); Print();
~Print(); ~Print();
Ref<Model> model()
%code%{ RETVAL = const_cast<Model*>(&THIS->model()); %};
Ref<StaticPrintConfig> config() Ref<StaticPrintConfig> config()
%code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %}; %code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %};
Ref<PlaceholderParser> placeholder_parser() Ref<PlaceholderParser> placeholder_parser()
@ -100,7 +102,6 @@ _constant()
%code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %}; %code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %};
Ref<PrintObject> get_object(int idx) Ref<PrintObject> get_object(int idx)
%code%{ RETVAL = THIS->objects()[idx]; %}; %code%{ RETVAL = THIS->objects()[idx]; %};
void reload_object(int idx);
size_t object_count() size_t object_count()
%code%{ RETVAL = THIS->objects().size(); %}; %code%{ RETVAL = THIS->objects().size(); %};
@ -141,9 +142,8 @@ _constant()
} }
%}; %};
void add_model_object(ModelObject* model_object, int idx = -1); bool apply(Model *model, DynamicPrintConfig* config)
bool apply_config_perl_tests_only(DynamicPrintConfig* config) %code%{ RETVAL = THIS->apply(*model, *config); %};
%code%{ RETVAL = THIS->apply_config_perl_tests_only(*config); %};
bool has_infinite_skirt(); bool has_infinite_skirt();
std::vector<unsigned int> extruders() const; std::vector<unsigned int> extruders() const;
int validate() %code%{ int validate() %code%{