Merge branch 'master' into tm_builtin_pad

This commit is contained in:
tamasmeszaros 2019-07-09 13:21:26 +02:00
commit af89bcee53
65 changed files with 2903 additions and 1706 deletions

2
deps/CMakeLists.txt vendored
View file

@ -105,7 +105,7 @@ else()
dep_gtest dep_gtest
dep_nlopt dep_nlopt
dep_qhull dep_qhull
dep_libigl # dep_libigl # Not working, static build has different Eigen
) )
endif() endif()

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,13 +146,16 @@ sub mesh {
} }
sub model { sub model {
my ($model_name, %params) = @_; my ($model_names, %params) = @_;
$model_names = [ $model_names ] if ! ref($model_names);
my $model = Slic3r::Model->new;
for my $model_name (@$model_names) {
my $input_file = "${model_name}.stl"; my $input_file = "${model_name}.stl";
my $mesh = mesh($model_name, %params); my $mesh = mesh($model_name, %params);
# $mesh->write_ascii("out/$input_file"); # $mesh->write_ascii("out/$input_file");
my $model = Slic3r::Model->new;
my $object = $model->add_object(input_file => $input_file); my $object = $model->add_object(input_file => $input_file);
$model->set_material($model_name); $model->set_material($model_name);
$object->add_volume(mesh => $mesh, material_id => $model_name); $object->add_volume(mesh => $mesh, material_id => $model_name);
@ -165,41 +168,44 @@ sub model {
# rotation => $params{rotation} // 0, # rotation => $params{rotation} // 0,
# scaling_factor => $params{scale} // 1, # 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);
$models = [$models] if ref($models) ne 'ARRAY';
$models = [ map { ref($_) ? $_ : model($_, %params) } @$models ];
for my $model (@$models) {
die "Unknown model in test" if !defined $model; die "Unknown model in test" if !defined $model;
if (defined $params{duplicate} && $params{duplicate} > 1) { if (defined $params{duplicate} && $params{duplicate} > 1) {
$model->duplicate($params{duplicate} // 1, $print->config->min_object_distance); $model->duplicate($params{duplicate} // 1, $config->min_object_distance);
} }
$model->arrange_objects($print->config->min_object_distance); $model->arrange_objects($config->min_object_distance);
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100)); $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}) { foreach my $model_object (@{$model->objects}) {
$model_object->ensure_on_bed;
$print->auto_assign_extruders($model_object); $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. $print->apply($model, $config);
$print->apply_config_perl_tests_only($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
@ -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

@ -1405,7 +1405,9 @@ void GCode::process_layer(
m_colorprint_heights.erase(m_colorprint_heights.begin()); m_colorprint_heights.erase(m_colorprint_heights.begin());
colorprint_change = true; colorprint_change = true;
} }
if (colorprint_change && print.extruders().size()==1)
// we should add or not colorprint_change in respect to nozzle_diameter count instead of really used extruders count
if (colorprint_change && print./*extruders()*/config().nozzle_diameter.size()==1)
gcode += "M600\n"; gcode += "M600\n";

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);

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,
@ -735,6 +590,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
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,18 +886,26 @@ 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))
@ -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,20 +943,32 @@ 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;
} }
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
// Now insert a volume with a layer range to its own region.
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
int region_id = -1; int region_id = -1;
if (&print_object == &print_object0) { if (&print_object == &print_object0) {
// Get the config applied to this volume. // Get the config applied to this volume.
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders); PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
// Find an existing print region with the same config. // Find an existing print region with the same config.
int idx_empty_slot = -1; int idx_empty_slot = -1;
for (int i = 0; i < (int)m_regions.size(); ++ i) { for (int i = 0; i < (int)m_regions.size(); ++ i) {
@ -1111,14 +990,15 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
m_regions[region_id]->set_config(std::move(config)); m_regions[region_id]->set_config(std::move(config));
} }
} }
map_volume_to_region[volume_id] = region_id; regions_in_object.emplace_back(region_id);
} else } else
region_id = map_volume_to_region[volume_id]; region_id = regions_in_object[idx_region_in_object ++];
// Assign volume to a region. // Assign volume to a region.
if (fresh) { if (fresh) {
if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
++ m_regions[region_id]->m_refcnt; ++ m_regions[region_id]->m_refcnt;
print_object.add_region_volume(region_id, volume_id); print_object.add_region_volume(region_id, volume_id, it_range->first);
}
} }
++ 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;
@ -1436,8 +1316,8 @@ Flow Print::brim_flow() const
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
); );
} }
@ -1458,8 +1338,8 @@ Flow Print::skirt_flow() const
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
); );
} }
@ -1636,18 +1516,18 @@ void Print::_make_skirt()
// 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
@ -291,11 +294,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);
@ -1388,15 +1395,24 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full
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);
} }
@ -1432,9 +1448,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
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;
} else {
last_volume_id = volume_id;
if (all_volumes_single_region == -2)
// first model volume met // first model volume met
single_volume_region = region_id; all_volumes_single_region = region_id;
else if (single_volume_region != region_id) else if (all_volumes_single_region != region_id)
// multiple volumes met and they are not equal // multiple volumes met and they are not equal
single_volume_region = -1; all_volumes_single_region = -1;
++ num_volumes; ++ 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,13 +1563,27 @@ 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.
@ -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,11 +1945,12 @@ 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;
if (! z.empty()) {
// Compose mesh. // Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. //FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh()); TriangleMesh mesh(volume.mesh());
mesh.transform(volume.get_matrix(), true); mesh.transform(volume.get_matrix(), true);
if (mesh.repaired) { if (mesh.repaired) {
@ -1853,9 +1971,45 @@ std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z,
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
m_print->throw_if_canceled(); 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

@ -673,6 +673,7 @@ void base_plate(const TriangleMesh & mesh,
const std::vector<float> &heights, const std::vector<float> &heights,
ThrowOnCancel thrfn) ThrowOnCancel thrfn)
{ {
if (mesh.empty()) return;
// m.require_shared_vertices(); // TriangleMeshSlicer needs this // m.require_shared_vertices(); // TriangleMeshSlicer needs this
TriangleMeshSlicer slicer(&mesh); TriangleMeshSlicer slicer(&mesh);

View file

@ -896,7 +896,6 @@ public:
merged.merge(bs.mesh); merged.merge(bs.mesh);
} }
if(m_ctl.stopcondition()) { if(m_ctl.stopcondition()) {
// In case of failure we have to return an empty mesh // In case of failure we have to return an empty mesh
meshcache = TriangleMesh(); meshcache = TriangleMesh();
@ -907,7 +906,7 @@ public:
// The mesh will be passed by const-pointer to TriangleMeshSlicer, // The mesh will be passed by const-pointer to TriangleMeshSlicer,
// which will need this. // which will need this.
meshcache.require_shared_vertices(); if (!meshcache.empty()) meshcache.require_shared_vertices();
// TODO: Is this necessary? // TODO: Is this necessary?
//meshcache.repair(); //meshcache.repair();
@ -2514,7 +2513,7 @@ std::vector<ExPolygons> SLASupportTree::slice(float layerh, float init_layerh) c
TriangleMesh fullmesh = m_impl->merged_mesh(); TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad()); fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh); TriangleMeshSlicer slicer(&fullmesh);
std::vector<ExPolygons> ret; std::vector<ExPolygons> ret;
slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn); slicer.slice(heights, 0.f, &ret, get().ctl().cancelfn);
@ -2527,7 +2526,7 @@ std::vector<ExPolygons> SLASupportTree::slice(const std::vector<float> &heights,
{ {
TriangleMesh fullmesh = m_impl->merged_mesh(); TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad()); fullmesh.merge(get_pad());
fullmesh.require_shared_vertices(); // TriangleMeshSlicer needs this if (!fullmesh.empty()) fullmesh.require_shared_vertices();
TriangleMeshSlicer slicer(&fullmesh); TriangleMeshSlicer slicer(&fullmesh);
std::vector<ExPolygons> ret; std::vector<ExPolygons> ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn); slicer.slice(heights, cr, &ret, get().ctl().cancelfn);

View file

@ -787,17 +787,26 @@ void SLAPrint::process()
mit->set_model_slice_idx(po, id); ++mit; mit->set_model_slice_idx(po, id); ++mit;
} }
if(po.m_config.supports_enable.getBool() ||
po.m_config.pad_enable.getBool())
{
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
}
}; };
// In this step we check the slices, identify island and cover them with // In this step we check the slices, identify island and cover them with
// support points. Then we sprinkle the rest of the mesh. // support points. Then we sprinkle the rest of the mesh.
auto support_points = [this, ostepd](SLAPrintObject& po) { auto support_points = [this, ostepd](SLAPrintObject& po) {
const ModelObject& mo = *po.m_model_object; // If supports are disabled, we can skip the model scan.
if(!po.m_config.supports_enable.getBool()) return;
if (!po.m_supportdata)
po.m_supportdata.reset( po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh())); new SLAPrintObject::SupportData(po.transformed_mesh()));
// If supports are disabled, we can skip the model scan. const ModelObject& mo = *po.m_model_object;
if(!po.m_config.supports_enable.getBool()) return;
BOOST_LOG_TRIVIAL(debug) << "Support point count " BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size(); << mo.sla_support_points.size();
@ -807,7 +816,7 @@ void SLAPrint::process()
// into the backend cache. // into the backend cache.
if (mo.sla_points_status != sla::PointsStatus::UserModified) { if (mo.sla_points_status != sla::PointsStatus::UserModified) {
// Hypotetical use of the slice index: // Hypothetical use of the slice index:
// auto bb = po.transformed_mesh().bounding_box(); // auto bb = po.transformed_mesh().bounding_box();
// auto range = po.get_slice_records(bb.min(Z)); // auto range = po.get_slice_records(bb.min(Z));
// std::vector<float> heights; heights.reserve(range.size()); // std::vector<float> heights; heights.reserve(range.size());
@ -960,12 +969,6 @@ void SLAPrint::process()
// and before the supports had been sliced. (or the slicing has to be // and before the supports had been sliced. (or the slicing has to be
// repeated) // repeated)
if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) {
BOOST_LOG_TRIVIAL(error) << "Uninitialized support data at "
<< "pad creation.";
return;
}
if(po.m_config.pad_enable.getBool()) if(po.m_config.pad_enable.getBool())
{ {
// Get the distilled pad configuration from the config // Get the distilled pad configuration from the config
@ -992,7 +995,7 @@ void SLAPrint::process()
pcfg.throw_on_cancel = thrfn; pcfg.throw_on_cancel = thrfn;
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
} else { } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) {
po.m_supportdata->support_tree_ptr->remove_pad(); po.m_supportdata->support_tree_ptr->remove_pad();
} }
@ -1009,6 +1012,11 @@ void SLAPrint::process()
if(sd) sd->support_slices.clear(); if(sd) sd->support_slices.clear();
// Don't bother if no supports and no pad is present.
if (!po.m_config.supports_enable.getBool() &&
!po.m_config.pad_enable.getBool())
return;
if(sd && sd->support_tree_ptr) { if(sd && sd->support_tree_ptr) {
std::vector<float> heights; heights.reserve(po.m_slice_index.size()); std::vector<float> heights; heights.reserve(po.m_slice_index.size());
@ -1035,7 +1043,8 @@ void SLAPrint::process()
po.m_slice_index[i].set_support_slice_idx(po, i); po.m_slice_index[i].set_support_slice_idx(po, i);
} }
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices. // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
// status to the 3D preview to load the SLA slices.
m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); m_report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
}; };
@ -1607,14 +1616,17 @@ bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
return true; return true;
} }
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object): SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object)
Inherited(print, model_object), : Inherited(print, model_object)
m_stepmask(slaposCount, true), , m_stepmask(slaposCount, true)
m_transformed_rmesh( [this](TriangleMesh& obj){ , m_transformed_rmesh([this](TriangleMesh &obj) {
obj = m_model_object->raw_mesh(); obj.transform(m_trafo); obj.require_shared_vertices(); obj = m_model_object->raw_mesh();
}) if (!obj.empty()) {
{ obj.transform(m_trafo);
obj.require_shared_vertices();
} }
})
{}
SLAPrintObject::~SLAPrintObject() {} SLAPrintObject::~SLAPrintObject() {}
@ -1758,13 +1770,14 @@ namespace { // dummy empty static containers for return values in some methods
const std::vector<ExPolygons> EMPTY_SLICES; const std::vector<ExPolygons> EMPTY_SLICES;
const TriangleMesh EMPTY_MESH; const TriangleMesh EMPTY_MESH;
const ExPolygons EMPTY_SLICE; const ExPolygons EMPTY_SLICE;
const std::vector<sla::SupportPoint> EMPTY_SUPPORT_POINTS;
} }
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f); const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
{ {
return m_supportdata->support_points; return m_supportdata? m_supportdata->support_points : EMPTY_SUPPORT_POINTS;
} }
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const

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

@ -547,9 +547,9 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
#if REALfloat #if REALfloat
qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt"); qhull.runQhull("", 3, (int)this->its.vertices.size(), (const realT*)(this->its.vertices.front().data()), "Qt");
#else #else
src_vertices.reserve(this->its.vertices() * 3); src_vertices.reserve(this->its.vertices.size() * 3);
// We will now fill the vector with input points for computation: // We will now fill the vector with input points for computation:
for (const stl_vertex &v : ths->its.vertices.size()) for (const stl_vertex &v : this->its.vertices)
for (int i = 0; i < 3; ++ i) for (int i = 0; i < 3; ++ i)
src_vertices.emplace_back(v(i)); src_vertices.emplace_back(v(i));
qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt"); qhull.runQhull("", 3, (int)src_vertices.size() / 3, src_vertices.data(), "Qt");

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

@ -274,8 +274,8 @@ void Bed3D::Axes::render_axis(double length) const
Bed3D::Bed3D() Bed3D::Bed3D()
: m_type(Custom) : m_type(Custom)
, m_requires_canvas_update(false)
#if ENABLE_TEXTURES_FROM_SVG #if ENABLE_TEXTURES_FROM_SVG
, m_requires_canvas_update(false)
, m_vbo_id(0) , m_vbo_id(0)
#endif // ENABLE_TEXTURES_FROM_SVG #endif // ENABLE_TEXTURES_FROM_SVG
, m_scale_factor(1.0f) , m_scale_factor(1.0f)
@ -330,12 +330,11 @@ Point Bed3D::point_projection(const Point& point) const
} }
#if ENABLE_TEXTURES_FROM_SVG #if ENABLE_TEXTURES_FROM_SVG
void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const void Bed3D::render(GLCanvas3D* canvas, float theta, float scale_factor) const
{ {
m_scale_factor = scale_factor; m_scale_factor = scale_factor;
EType type = useVBOs ? m_type : Custom; switch (m_type)
switch (type)
{ {
case MK2: case MK2:
{ {
@ -361,7 +360,7 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_fa
} }
} }
#else #else
void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const void Bed3D::render(float theta, float scale_factor) const
{ {
m_scale_factor = scale_factor; m_scale_factor = scale_factor;
@ -372,17 +371,17 @@ void Bed3D::render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_fa
{ {
case MK2: case MK2:
{ {
render_prusa(canvas, "mk2", theta, useVBOs); render_prusa("mk2", theta);
break; break;
} }
case MK3: case MK3:
{ {
render_prusa(canvas, "mk3", theta, useVBOs); render_prusa("mk3", theta);
break; break;
} }
case SL1: case SL1:
{ {
render_prusa(canvas, "sl1", theta, useVBOs); render_prusa("sl1", theta);
break; break;
} }
default: default:
@ -546,7 +545,7 @@ void Bed3D::render_prusa(GLCanvas3D* canvas, const std::string &key, bool bottom
if (!bottom) if (!bottom)
{ {
filename = model_path + "_bed.stl"; filename = model_path + "_bed.stl";
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, true)) { if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2") if (key == "mk2")
// hardcoded value to match the stl model // hardcoded value to match the stl model
@ -628,12 +627,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()));
@ -651,7 +650,7 @@ void Bed3D::render_prusa_shader(bool transparent) const
} }
} }
#else #else
void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) const void Bed3D::render_prusa(const std::string& key, float theta) const
{ {
std::string tex_path = resources_dir() + "/icons/bed/" + key; std::string tex_path = resources_dir() + "/icons/bed/" + key;
@ -677,7 +676,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
std::string filename = tex_path + "_top.png"; std::string filename = tex_path + "_top.png";
if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename)) if ((m_top_texture.get_id() == 0) || (m_top_texture.get_source() != filename))
{ {
if (!m_top_texture.load_from_file(filename, true)) if (!m_top_texture.load_from_file(filename, true, true))
{ {
render_custom(); render_custom();
return; return;
@ -694,7 +693,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
filename = tex_path + "_bottom.png"; filename = tex_path + "_bottom.png";
if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename)) if ((m_bottom_texture.get_id() == 0) || (m_bottom_texture.get_source() != filename))
{ {
if (!m_bottom_texture.load_from_file(filename, true)) if (!m_bottom_texture.load_from_file(filename, true, true))
{ {
render_custom(); render_custom();
return; return;
@ -711,7 +710,7 @@ void Bed3D::render_prusa(const std::string &key, float theta, bool useVBOs) cons
if (theta <= 90.0f) if (theta <= 90.0f)
{ {
filename = model_path + "_bed.stl"; filename = model_path + "_bed.stl";
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename, useVBOs)) { if ((m_model.get_filename() != filename) && m_model.init_from_file(filename)) {
Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2)); Vec3d offset = m_bounding_box.center() - Vec3d(0.0, 0.0, 0.5 * m_model.get_bounding_box().size()(2));
if (key == "mk2") if (key == "mk2")
// hardcoded value to match the stl model // hardcoded value to match the stl model

View file

@ -44,8 +44,8 @@ public:
const float* get_vertices_data() const; const float* get_vertices_data() const;
unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); } unsigned int get_vertices_data_size() const { return (unsigned int)m_vertices.size() * get_vertex_data_size(); }
unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); } unsigned int get_vertex_data_size() const { return (unsigned int)(5 * sizeof(float)); }
unsigned int get_position_offset() const { return 0; } size_t get_position_offset() const { return 0; }
unsigned int get_tex_coords_offset() const { return (unsigned int)(3 * sizeof(float)); } size_t get_tex_coords_offset() const { return (size_t)(3 * sizeof(float)); }
unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); } unsigned int get_vertices_count() const { return (unsigned int)m_vertices.size(); }
#else #else
const float* get_vertices() const { return m_vertices.data(); } const float* get_vertices() const { return m_vertices.data(); }
@ -128,7 +128,11 @@ public:
bool contains(const Point& point) const; bool contains(const Point& point) const;
Point point_projection(const Point& point) const; Point point_projection(const Point& point) const;
void render(GLCanvas3D* canvas, float theta, bool useVBOs, float scale_factor) const; #if ENABLE_TEXTURES_FROM_SVG
void render(GLCanvas3D* canvas, float theta, float scale_factor) const;
#else
void render(float theta, float scale_factor) const;
#endif // ENABLE_TEXTURES_FROM_SVG
void render_axes() const; void render_axes() const;
private: private:
@ -140,7 +144,7 @@ private:
void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const; void render_prusa(GLCanvas3D* canvas, const std::string& key, bool bottom) const;
void render_prusa_shader(bool transparent) const; void render_prusa_shader(bool transparent) const;
#else #else
void render_prusa(const std::string &key, float theta, bool useVBOs) const; void render_prusa(const std::string& key, float theta) const;
#endif // ENABLE_TEXTURES_FROM_SVG #endif // ENABLE_TEXTURES_FROM_SVG
void render_custom() const; void render_custom() const;
#if ENABLE_TEXTURES_FROM_SVG #if ENABLE_TEXTURES_FROM_SVG

View file

@ -55,21 +55,6 @@ void glAssertRecentCallImpl(const char *file_name, unsigned int line, const char
namespace Slic3r { namespace Slic3r {
void GLIndexedVertexArray::load_mesh_flat_shading(const TriangleMesh &mesh)
{
assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
assert(quad_indices.empty() && triangle_indices_size == 0);
assert(vertices_and_normals_interleaved.size() % 6 == 0 && quad_indices_size == vertices_and_normals_interleaved.size());
this->vertices_and_normals_interleaved.reserve(this->vertices_and_normals_interleaved.size() + 3 * 3 * 2 * mesh.facets_count());
for (int i = 0; i < (int)mesh.stl.stats.number_of_facets; ++i) {
const stl_facet &facet = mesh.stl.facet_start[i];
for (int j = 0; j < 3; ++ j)
this->push_geometry(facet.vertex[j](0), facet.vertex[j](1), facet.vertex[j](2), facet.normal(0), facet.normal(1), facet.normal(2));
}
}
void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh) void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
{ {
assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0); assert(triangle_indices.empty() && vertices_and_normals_interleaved_size == 0);
@ -89,15 +74,14 @@ void GLIndexedVertexArray::load_mesh_full_shading(const TriangleMesh &mesh)
} }
} }
void GLIndexedVertexArray::finalize_geometry(bool use_VBOs) void GLIndexedVertexArray::finalize_geometry() const
{ {
assert(this->vertices_and_normals_interleaved_VBO_id == 0); assert(this->vertices_and_normals_interleaved_VBO_id == 0);
assert(this->triangle_indices_VBO_id == 0); assert(this->triangle_indices_VBO_id == 0);
assert(this->quad_indices_VBO_id == 0); assert(this->quad_indices_VBO_id == 0);
this->setup_sizes(); this->shrink_to_fit();
if (use_VBOs) {
if (! empty()) { if (! empty()) {
glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glGenBuffers(1, &this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
@ -109,17 +93,16 @@ void GLIndexedVertexArray::finalize_geometry(bool use_VBOs)
glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id)); glsafe(::glGenBuffers(1, &this->triangle_indices_VBO_id));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW)); glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices.size() * 4, this->triangle_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
this->triangle_indices.clear(); this->triangle_indices.clear();
} }
if (! this->quad_indices.empty()) { if (! this->quad_indices.empty()) {
glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id)); glsafe(::glGenBuffers(1, &this->quad_indices_VBO_id));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW)); glsafe(::glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices.size() * 4, this->quad_indices.data(), GL_STATIC_DRAW));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
this->quad_indices.clear(); this->quad_indices.clear();
} }
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
}
this->shrink_to_fit();
} }
void GLIndexedVertexArray::release_geometry() void GLIndexedVertexArray::release_geometry()
@ -137,89 +120,78 @@ void GLIndexedVertexArray::release_geometry()
this->quad_indices_VBO_id = 0; this->quad_indices_VBO_id = 0;
} }
this->clear(); this->clear();
this->shrink_to_fit();
} }
void GLIndexedVertexArray::render() const void GLIndexedVertexArray::render() const
{ {
if (this->vertices_and_normals_interleaved_VBO_id) { if (this->vertices_and_normals_interleaved_VBO_id == 0)
{
// sends data to gpu, if not done yet
finalize_geometry();
if (this->vertices_and_normals_interleaved_VBO_id == 0)
return;
}
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr)); glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
} else {
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
}
glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
if (this->indexed()) {
if (this->vertices_and_normals_interleaved_VBO_id) {
// Render using the Vertex Buffer Objects. // Render using the Vertex Buffer Objects.
if (this->triangle_indices_size > 0) { if (this->triangle_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr)); glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, nullptr));
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
} }
if (this->quad_indices_size > 0) { if (this->quad_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr)); glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, nullptr));
}
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
} else {
// Render in an immediate mode.
if (! this->triangle_indices.empty())
glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(this->triangle_indices_size), GL_UNSIGNED_INT, this->triangle_indices.data()));
if (! this->quad_indices.empty())
glsafe(::glDrawElements(GL_QUADS, GLsizei(this->quad_indices_size), GL_UNSIGNED_INT, this->quad_indices.data()));
} }
} else
glsafe(::glDrawArrays(GL_TRIANGLES, 0, GLsizei(this->vertices_and_normals_interleaved_size / 6)));
if (this->vertices_and_normals_interleaved_VBO_id)
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
} }
void GLIndexedVertexArray::render( void GLIndexedVertexArray::render(
const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& tverts_range,
const std::pair<size_t, size_t>& qverts_range) const const std::pair<size_t, size_t>& qverts_range) const
{ {
assert(this->indexed()); if (this->vertices_and_normals_interleaved_VBO_id == 0)
if (! this->indexed()) {
// sends data to gpu, if not done yet
finalize_geometry();
if (this->vertices_and_normals_interleaved_VBO_id == 0)
return; return;
}
if (this->vertices_and_normals_interleaved_VBO_id) {
// Render using the Vertex Buffer Objects. // Render using the Vertex Buffer Objects.
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, this->vertices_and_normals_interleaved_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float)))); glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr)); glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY)); glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
if (this->triangle_indices_size > 0) { if (this->triangle_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->triangle_indices_VBO_id));
glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4))); glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
} }
if (this->quad_indices_size > 0) { if (this->quad_indices_size > 0) {
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->quad_indices_VBO_id));
glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4))); glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
}
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
} else {
// Render in an immediate mode.
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data() + 3));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), this->vertices_and_normals_interleaved.data()));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
if (! this->triangle_indices.empty())
glsafe(::glDrawElements(GL_TRIANGLES, GLsizei(std::min(this->triangle_indices_size, tverts_range.second - tverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->triangle_indices.data() + tverts_range.first)));
if (! this->quad_indices.empty())
glsafe(::glDrawElements(GL_QUADS, GLsizei(std::min(this->quad_indices_size, qverts_range.second - qverts_range.first)), GL_UNSIGNED_INT, (const void*)(this->quad_indices.data() + qverts_range.first)));
} }
glsafe(::glDisableClientState(GL_VERTEX_ARRAY)); glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY)); glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
} }
const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
@ -343,11 +315,12 @@ bool GLVolume::is_left_handed() const
const BoundingBoxf3& GLVolume::transformed_bounding_box() const const BoundingBoxf3& GLVolume::transformed_bounding_box() const
{ {
assert(bounding_box.defined || bounding_box.min(0) >= bounding_box.max(0) || bounding_box.min(1) >= bounding_box.max(1) || bounding_box.min(2) >= bounding_box.max(2)); const BoundingBoxf3& box = bounding_box();
assert(box.defined || box.min(0) >= box.max(0) || box.min(1) >= box.max(1) || box.min(2) >= box.max(2));
if (m_transformed_bounding_box_dirty) if (m_transformed_bounding_box_dirty)
{ {
m_transformed_bounding_box = bounding_box.transformed(world_matrix()); m_transformed_bounding_box = box.transformed(world_matrix());
m_transformed_bounding_box_dirty = false; m_transformed_bounding_box_dirty = false;
} }
@ -365,9 +338,10 @@ BoundingBoxf3 GLVolume::transformed_convex_hull_bounding_box(const Transform3d &
{ {
return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ? return (m_convex_hull && m_convex_hull->stl.stats.number_of_facets > 0) ?
m_convex_hull->transformed_bounding_box(trafo) : m_convex_hull->transformed_bounding_box(trafo) :
bounding_box.transformed(trafo); bounding_box().transformed(trafo);
} }
void GLVolume::set_range(double min_z, double max_z) void GLVolume::set_range(double min_z, double max_z)
{ {
this->qverts_range.first = 0; this->qverts_range.first = 0;
@ -412,58 +386,17 @@ void GLVolume::render() const
glFrontFace(GL_CW); glFrontFace(GL_CW);
glsafe(::glCullFace(GL_BACK)); glsafe(::glCullFace(GL_BACK));
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(world_matrix().data())); glsafe(::glMultMatrixd(world_matrix().data()));
if (this->indexed_vertex_array.indexed())
this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); this->indexed_vertex_array.render(this->tverts_range, this->qverts_range);
else
this->indexed_vertex_array.render();
glsafe(::glPopMatrix()); glsafe(::glPopMatrix());
if (this->is_left_handed()) if (this->is_left_handed())
glFrontFace(GL_CCW); glFrontFace(GL_CCW);
} }
void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) const void GLVolume::render(int color_id, int detection_id, int worldmatrix_id) const
{ {
if (!is_active)
return;
if (!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)
return;
if (this->is_left_handed())
glFrontFace(GL_CW);
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
if (n_triangles + n_quads == 0)
{
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
if (color_id >= 0)
{
float color[4];
::memcpy((void*)color, (const void*)render_color, 4 * sizeof(float));
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)color));
}
else
glsafe(::glColor4fv(render_color));
if (detection_id != -1)
glsafe(::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0));
if (worldmatrix_id != -1)
glsafe(::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data()));
render();
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
return;
}
if (color_id >= 0) if (color_id >= 0)
glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)render_color)); glsafe(::glUniform4fv(color_id, 1, (const GLfloat*)render_color));
else else
@ -475,74 +408,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c
if (worldmatrix_id != -1) if (worldmatrix_id != -1)
glsafe(::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data())); glsafe(::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast<float>().data()));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), nullptr));
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(world_matrix().data()));
if (n_triangles > 0)
{
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.triangle_indices_VBO_id));
glsafe(::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, (const void*)(tverts_range.first * 4)));
}
if (n_quads > 0)
{
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexed_vertex_array.quad_indices_VBO_id));
glsafe(::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, (const void*)(qverts_range.first * 4)));
}
glsafe(::glPopMatrix());
if (this->is_left_handed())
glFrontFace(GL_CCW);
}
void GLVolume::render_legacy() const
{
assert(!indexed_vertex_array.vertices_and_normals_interleaved_VBO_id);
if (!is_active)
return;
if (this->is_left_handed())
glFrontFace(GL_CW);
GLsizei n_triangles = GLsizei(std::min(indexed_vertex_array.triangle_indices_size, tverts_range.second - tverts_range.first));
GLsizei n_quads = GLsizei(std::min(indexed_vertex_array.quad_indices_size, qverts_range.second - qverts_range.first));
if (n_triangles + n_quads == 0)
{
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glColor4fv(render_color));
render(); render();
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
return;
}
glsafe(::glColor4fv(render_color));
glsafe(::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data() + 3));
glsafe(::glNormalPointer(GL_FLOAT, 6 * sizeof(float), indexed_vertex_array.vertices_and_normals_interleaved.data()));
glsafe(::glPushMatrix());
glsafe(::glMultMatrixd(world_matrix().data()));
if (n_triangles > 0)
glsafe(::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first));
if (n_quads > 0)
glsafe(::glDrawElements(GL_QUADS, n_quads, GL_UNSIGNED_INT, indexed_vertex_array.quad_indices.data() + qverts_range.first));
glsafe(::glPopMatrix());
if (this->is_left_handed())
glFrontFace(GL_CCW);
} }
bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); } bool GLVolume::is_sla_support() const { return this->composite_id.volume_id == -int(slaposSupportTree); }
@ -552,13 +418,12 @@ std::vector<int> GLVolumeCollection::load_object(
const ModelObject* model_object, const ModelObject* model_object,
int obj_idx, int obj_idx,
const std::vector<int>& instance_idxs, const std::vector<int>& instance_idxs,
const std::string &color_by, const std::string& color_by)
bool use_VBOs)
{ {
std::vector<int> volumes_idx; std::vector<int> volumes_idx;
for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx) for (int volume_idx = 0; volume_idx < int(model_object->volumes.size()); ++volume_idx)
for (int instance_idx : instance_idxs) for (int instance_idx : instance_idxs)
volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by, use_VBOs)); volumes_idx.emplace_back(this->GLVolumeCollection::load_object_volume(model_object, obj_idx, volume_idx, instance_idx, color_by));
return volumes_idx; return volumes_idx;
} }
@ -567,8 +432,7 @@ int GLVolumeCollection::load_object_volume(
int obj_idx, int obj_idx,
int volume_idx, int volume_idx,
int instance_idx, int instance_idx,
const std::string &color_by, const std::string& color_by)
bool use_VBOs)
{ {
const ModelVolume* model_volume = model_object->volumes[volume_idx]; const ModelVolume* model_volume = model_object->volumes[volume_idx];
const int extruder_id = model_volume->extruder_id(); const int extruder_id = model_volume->extruder_id();
@ -590,11 +454,7 @@ int GLVolumeCollection::load_object_volume(
this->volumes.emplace_back(new GLVolume(color)); this->volumes.emplace_back(new GLVolume(color));
GLVolume& v = *this->volumes.back(); GLVolume& v = *this->volumes.back();
v.set_color_from_model_volume(model_volume); v.set_color_from_model_volume(model_volume);
v.indexed_vertex_array.load_mesh(mesh, use_VBOs); v.indexed_vertex_array.load_mesh(mesh);
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx); v.composite_id = GLVolume::CompositeID(obj_idx, volume_idx, instance_idx);
if (model_volume->is_model_part()) if (model_volume->is_model_part())
{ {
@ -621,8 +481,7 @@ void GLVolumeCollection::load_object_auxiliary(
const std::vector<std::pair<size_t, size_t>>& instances, const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone, SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone // Timestamp of the last change of the milestone
size_t timestamp, size_t timestamp)
bool use_VBOs)
{ {
assert(print_object->is_step_done(milestone)); assert(print_object->is_step_done(milestone));
Transform3d mesh_trafo_inv = print_object->trafo().inverse(); Transform3d mesh_trafo_inv = print_object->trafo().inverse();
@ -635,10 +494,7 @@ void GLVolumeCollection::load_object_auxiliary(
const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first]; const ModelInstance& model_instance = *print_object->model_object()->instances[instance_idx.first];
this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR)); this->volumes.emplace_back(new GLVolume((milestone == slaposBasePool) ? GLVolume::SLA_PAD_COLOR : GLVolume::SLA_SUPPORT_COLOR));
GLVolume& v = *this->volumes.back(); GLVolume& v = *this->volumes.back();
v.indexed_vertex_array.load_mesh(mesh, use_VBOs); v.indexed_vertex_array.load_mesh(mesh);
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first); v.composite_id = GLVolume::CompositeID(obj_idx, -int(milestone), (int)instance_idx.first);
v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id); v.geometry_id = std::pair<size_t, size_t>(timestamp, model_instance.id().id);
// Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance. // Create a copy of the convex hull mesh for each instance. Use a move operator on the last instance.
@ -655,7 +511,7 @@ void GLVolumeCollection::load_object_auxiliary(
} }
int GLVolumeCollection::load_wipe_tower_preview( int GLVolumeCollection::load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width) int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width)
{ {
if (depth < 0.01f) if (depth < 0.01f)
return int(this->volumes.size() - 1); return int(this->volumes.size() - 1);
@ -707,13 +563,9 @@ int GLVolumeCollection::load_wipe_tower_preview(
this->volumes.emplace_back(new GLVolume(color)); this->volumes.emplace_back(new GLVolume(color));
GLVolume& v = *this->volumes.back(); GLVolume& v = *this->volumes.back();
v.indexed_vertex_array.load_mesh(mesh, use_VBOs); v.indexed_vertex_array.load_mesh(mesh);
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle)); v.set_volume_rotation(Vec3d(0., 0., (M_PI / 180.) * rotation_angle));
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(use_VBOs);
v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0); v.composite_id = GLVolume::CompositeID(obj_idx, 0, 0);
v.geometry_id.first = 0; v.geometry_id.first = 0;
v.geometry_id.second = wipe_tower_instance_id().id; v.geometry_id.second = wipe_tower_instance_id().id;
@ -742,7 +594,7 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
{ {
for (GLVolumeWithIdAndZ& volume : list) for (GLVolumeWithIdAndZ& volume : list)
{ {
volume.second.second = volume.first->bounding_box.transformed(view_matrix * volume.first->world_matrix()).max(2); volume.second.second = volume.first->bounding_box().transformed(view_matrix * volume.first->world_matrix()).max(2);
} }
std::sort(list.begin(), list.end(), std::sort(list.begin(), list.end(),
@ -759,7 +611,7 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo
return list; return list;
} }
void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
{ {
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@ -797,7 +649,7 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func); GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render) { for (GLVolumeWithIdAndZ& volume : to_render) {
volume.first->set_render_color(); volume.first->set_render_color();
volume.first->render_VBOs(color_id, print_box_detection_id, print_box_worldmatrix_id); volume.first->render(color_id, print_box_detection_id, print_box_worldmatrix_id);
} }
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
@ -812,34 +664,6 @@ void GLVolumeCollection::render_VBOs(GLVolumeCollection::ERenderType type, bool
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
void GLVolumeCollection::render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func) const
{
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glCullFace(GL_BACK));
if (disable_cullface)
glsafe(::glDisable(GL_CULL_FACE));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
GLVolumeWithIdAndZList to_render = volumes_to_render(this->volumes, type, view_matrix, filter_func);
for (GLVolumeWithIdAndZ& volume : to_render)
{
volume.first->set_render_color();
volume.first->render_legacy();
}
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
if (disable_cullface)
glsafe(::glEnable(GL_CULL_FACE));
glsafe(::glDisable(GL_BLEND));
}
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state) bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, ModelInstance::EPrintVolumeState* out_state)
{ {
if (config == nullptr) if (config == nullptr)
@ -1025,14 +849,13 @@ static void thick_lines_to_indexed_vertex_array(
int idx_initial[4] = { -1, -1, -1, -1 }; int idx_initial[4] = { -1, -1, -1, -1 };
double width_initial = 0.; double width_initial = 0.;
double bottom_z_initial = 0.0; double bottom_z_initial = 0.0;
double len_prev = 0.0;
// loop once more in case of closed loops // loop once more in case of closed loops
size_t lines_end = closed ? (lines.size() + 1) : lines.size(); size_t lines_end = closed ? (lines.size() + 1) : lines.size();
for (size_t ii = 0; ii < lines_end; ++ ii) { for (size_t ii = 0; ii < lines_end; ++ ii) {
size_t i = (ii == lines.size()) ? 0 : ii; size_t i = (ii == lines.size()) ? 0 : ii;
const Line &line = lines[i]; const Line &line = lines[i];
double len = unscale<double>(line.length());
double inv_len = 1.0 / len;
double bottom_z = top_z - heights[i]; double bottom_z = top_z - heights[i];
double middle_z = 0.5 * (top_z + bottom_z); double middle_z = 0.5 * (top_z + bottom_z);
double width = widths[i]; double width = widths[i];
@ -1041,8 +864,8 @@ static void thick_lines_to_indexed_vertex_array(
bool is_last = (ii == lines_end - 1); bool is_last = (ii == lines_end - 1);
bool is_closing = closed && is_last; bool is_closing = closed && is_last;
Vec2d v = unscale(line.vector()); Vec2d v = unscale(line.vector()).normalized();
v *= inv_len; double len = unscale<double>(line.length());
Vec2d a = unscale(line.a); Vec2d a = unscale(line.a);
Vec2d b = unscale(line.b); Vec2d b = unscale(line.b);
@ -1061,9 +884,7 @@ static void thick_lines_to_indexed_vertex_array(
} }
// calculate new XY normals // calculate new XY normals
Vector n = line.normal(); Vec2d xy_right_normal = unscale(line.normal()).normalized();
Vec3d xy_right_normal = unscale(n(0), n(1), 0);
xy_right_normal *= inv_len;
int idx_a[4]; int idx_a[4];
int idx_b[4]; int idx_b[4];
@ -1091,9 +912,9 @@ static void thick_lines_to_indexed_vertex_array(
idx_a[BOTTOM] = idx_last ++; idx_a[BOTTOM] = idx_last ++;
volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.); volume.push_geometry(a(0), a(1), bottom_z, 0., 0., -1.);
idx_a[LEFT ] = idx_last ++; idx_a[LEFT ] = idx_last ++;
volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
idx_a[RIGHT] = idx_last ++; idx_a[RIGHT] = idx_last ++;
volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
} }
else { else {
idx_a[BOTTOM] = idx_prev[BOTTOM]; idx_a[BOTTOM] = idx_prev[BOTTOM];
@ -1108,18 +929,36 @@ static void thick_lines_to_indexed_vertex_array(
// Continuing a previous segment. // Continuing a previous segment.
// Share left / right vertices if possible. // Share left / right vertices if possible.
double v_dot = v_prev.dot(v); double v_dot = v_prev.dot(v);
bool sharp = v_dot < 0.707; // sin(45 degrees) // To reduce gpu memory usage, we try to reuse vertices
// To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
// is longer than a fixed threshold.
// The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
double len_threshold = 2.5;
// Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
bool sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
if (sharp) { if (sharp) {
if (!bottom_z_different) if (!bottom_z_different)
{ {
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
idx_a[RIGHT] = idx_last++; idx_a[RIGHT] = idx_last++;
volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); volume.push_geometry(a1(0), a1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
idx_a[LEFT] = idx_last++; idx_a[LEFT] = idx_last++;
volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); volume.push_geometry(a2(0), a2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
if (cross2(v_prev, v) > 0.) {
// Right turn. Fill in the right turn wedge.
volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a[RIGHT]);
}
else {
// Left turn. Fill in the left turn wedge.
volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
} }
} }
if (v_dot > 0.9) { }
else
{
if (!bottom_z_different) if (!bottom_z_different)
{ {
// The two successive segments are nearly collinear. // The two successive segments are nearly collinear.
@ -1127,45 +966,6 @@ static void thick_lines_to_indexed_vertex_array(
idx_a[RIGHT] = idx_prev[RIGHT]; idx_a[RIGHT] = idx_prev[RIGHT];
} }
} }
else if (!sharp) {
if (!bottom_z_different)
{
// Create a sharp corner with an overshot and average the left / right normals.
// At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
Vec2d intersection(Vec2d::Zero());
Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection);
a1 = intersection;
a2 = 2. * a - intersection;
assert((a - a1).norm() < width);
assert((a - a2).norm() < width);
float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6;
float *p_left_prev = n_left_prev + 3;
float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6;
float *p_right_prev = n_right_prev + 3;
p_left_prev [0] = float(a2(0));
p_left_prev [1] = float(a2(1));
p_right_prev[0] = float(a1(0));
p_right_prev[1] = float(a1(1));
xy_right_normal(0) += n_right_prev[0];
xy_right_normal(1) += n_right_prev[1];
xy_right_normal *= 1. / xy_right_normal.norm();
n_left_prev [0] = float(-xy_right_normal(0));
n_left_prev [1] = float(-xy_right_normal(1));
n_right_prev[0] = float( xy_right_normal(0));
n_right_prev[1] = float( xy_right_normal(1));
idx_a[LEFT ] = idx_prev[LEFT ];
idx_a[RIGHT] = idx_prev[RIGHT];
}
}
else if (cross2(v_prev, v) > 0.) {
// Right turn. Fill in the right turn wedge.
volume.push_triangle(idx_prev[RIGHT], idx_a [RIGHT], idx_prev[TOP] );
volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a [RIGHT] );
} else {
// Left turn. Fill in the left turn wedge.
volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a [LEFT] );
volume.push_triangle(idx_prev[LEFT], idx_a [LEFT], idx_prev[BOTTOM]);
}
if (is_closing) { if (is_closing) {
if (!sharp) { if (!sharp) {
if (!bottom_z_different) if (!bottom_z_different)
@ -1204,14 +1004,15 @@ static void thick_lines_to_indexed_vertex_array(
} }
// Generate new vertices for the end of this line segment. // Generate new vertices for the end of this line segment.
idx_b[LEFT ] = idx_last ++; idx_b[LEFT ] = idx_last ++;
volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), -xy_right_normal(2)); volume.push_geometry(b2(0), b2(1), middle_z, -xy_right_normal(0), -xy_right_normal(1), 0.0);
idx_b[RIGHT ] = idx_last ++; idx_b[RIGHT ] = idx_last ++;
volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), xy_right_normal(2)); volume.push_geometry(b1(0), b1(1), middle_z, xy_right_normal(0), xy_right_normal(1), 0.0);
memcpy(idx_prev, idx_b, 4 * sizeof(int)); memcpy(idx_prev, idx_b, 4 * sizeof(int));
bottom_z_prev = bottom_z; bottom_z_prev = bottom_z;
b1_prev = b1; b1_prev = b1;
v_prev = v; v_prev = v;
len_prev = len;
if (bottom_z_different && (closed || (!is_first && !is_last))) if (bottom_z_different && (closed || (!is_first && !is_last)))
{ {
@ -1265,6 +1066,7 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
int idx_initial[4] = { -1, -1, -1, -1 }; int idx_initial[4] = { -1, -1, -1, -1 };
int idx_prev[4] = { -1, -1, -1, -1 }; int idx_prev[4] = { -1, -1, -1, -1 };
double z_prev = 0.0; double z_prev = 0.0;
double len_prev = 0.0;
Vec3d n_right_prev = Vec3d::Zero(); Vec3d n_right_prev = Vec3d::Zero();
Vec3d n_top_prev = Vec3d::Zero(); Vec3d n_top_prev = Vec3d::Zero();
Vec3d unit_v_prev = Vec3d::Zero(); Vec3d unit_v_prev = Vec3d::Zero();
@ -1286,21 +1088,23 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
double width = widths[i]; double width = widths[i];
Vec3d unit_v = unscale(line.vector()).normalized(); Vec3d unit_v = unscale(line.vector()).normalized();
double len = unscale<double>(line.length());
Vec3d n_top = Vec3d::Zero(); Vec3d n_top = Vec3d::Zero();
Vec3d n_right = Vec3d::Zero(); Vec3d n_right = Vec3d::Zero();
Vec3d unit_positive_z(0.0, 0.0, 1.0);
if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1))) if ((line.a(0) == line.b(0)) && (line.a(1) == line.b(1)))
{ {
// vertical segment // vertical segment
n_right = (line.a(2) < line.b(2)) ? Vec3d(-1.0, 0.0, 0.0) : Vec3d(1.0, 0.0, 0.0); n_top = Vec3d::UnitY();
n_top = Vec3d(0.0, 1.0, 0.0); n_right = Vec3d::UnitX();
if (line.a(2) < line.b(2))
n_right = -n_right;
} }
else else
{ {
// generic segment // horizontal segment
n_right = unit_v.cross(unit_positive_z).normalized(); n_right = unit_v.cross(Vec3d::UnitZ()).normalized();
n_top = n_right.cross(unit_v).normalized(); n_top = n_right.cross(unit_v).normalized();
} }
@ -1361,9 +1165,16 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
// Continuing a previous segment. // Continuing a previous segment.
// Share left / right vertices if possible. // Share left / right vertices if possible.
double v_dot = unit_v_prev.dot(unit_v); double v_dot = unit_v_prev.dot(unit_v);
bool is_sharp = v_dot < 0.707; // sin(45 degrees)
bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0; bool is_right_turn = n_top_prev.dot(unit_v_prev.cross(unit_v)) > 0.0;
// To reduce gpu memory usage, we try to reuse vertices
// To reduce the visual artifacts, due to averaged normals, we allow to reuse vertices only when any of two adjacent edges
// is longer than a fixed threshold.
// The following value is arbitrary, it comes from tests made on a bunch of models showing the visual artifacts
double len_threshold = 2.5;
// Generate new vertices if the angle between adjacent edges is greater than 45 degrees or thresholds conditions are met
bool is_sharp = (v_dot < 0.707) || (len_prev > len_threshold) || (len > len_threshold);
if (is_sharp) if (is_sharp)
{ {
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn. // Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
@ -1371,54 +1182,8 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
volume.push_geometry(a[RIGHT], n_right); volume.push_geometry(a[RIGHT], n_right);
idx_a[LEFT] = idx_last++; idx_a[LEFT] = idx_last++;
volume.push_geometry(a[LEFT], n_left); volume.push_geometry(a[LEFT], n_left);
}
if (v_dot > 0.9) if (is_right_turn)
{
// The two successive segments are nearly collinear.
idx_a[LEFT] = idx_prev[LEFT];
idx_a[RIGHT] = idx_prev[RIGHT];
}
else if (!is_sharp)
{
// Create a sharp corner with an overshot and average the left / right normals.
// At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
// averages normals
Vec3d average_n_right = 0.5 * (n_right + n_right_prev).normalized();
Vec3d average_n_left = -average_n_right;
Vec3d average_rl_displacement = 0.5 * width * average_n_right;
// updates vertices around a
a[RIGHT] = l_a + average_rl_displacement;
a[LEFT] = l_a - average_rl_displacement;
// updates previous line normals
float* normal_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT] * 6;
normal_left_prev[0] = float(average_n_left(0));
normal_left_prev[1] = float(average_n_left(1));
normal_left_prev[2] = float(average_n_left(2));
float* normal_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6;
normal_right_prev[0] = float(average_n_right(0));
normal_right_prev[1] = float(average_n_right(1));
normal_right_prev[2] = float(average_n_right(2));
// updates previous line's vertices around b
float* b_left_prev = normal_left_prev + 3;
b_left_prev[0] = float(a[LEFT](0));
b_left_prev[1] = float(a[LEFT](1));
b_left_prev[2] = float(a[LEFT](2));
float* b_right_prev = normal_right_prev + 3;
b_right_prev[0] = float(a[RIGHT](0));
b_right_prev[1] = float(a[RIGHT](1));
b_right_prev[2] = float(a[RIGHT](2));
idx_a[LEFT] = idx_prev[LEFT];
idx_a[RIGHT] = idx_prev[RIGHT];
}
else if (is_right_turn)
{ {
// Right turn. Fill in the right turn wedge. // Right turn. Fill in the right turn wedge.
volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]); volume.push_triangle(idx_prev[RIGHT], idx_a[RIGHT], idx_prev[TOP]);
@ -1430,6 +1195,13 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]); volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a[LEFT]);
volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]); volume.push_triangle(idx_prev[LEFT], idx_a[LEFT], idx_prev[BOTTOM]);
} }
}
else
{
// The two successive segments are nearly collinear.
idx_a[LEFT] = idx_prev[LEFT];
idx_a[RIGHT] = idx_prev[RIGHT];
}
if (ii == lines.size()) if (ii == lines.size())
{ {
@ -1481,6 +1253,7 @@ static void thick_lines_to_indexed_vertex_array(const Lines3& lines,
n_right_prev = n_right; n_right_prev = n_right;
n_top_prev = n_top; n_top_prev = n_top;
unit_v_prev = unit_v; unit_v_prev = unit_v;
len_prev = len;
if (!closed) if (!closed)
{ {
@ -1694,8 +1467,7 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height
GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; GUI::GLCanvas3DManager _3DScene::s_canvas_mgr;
GLModel::GLModel() GLModel::GLModel()
: m_useVBOs(false) : m_filename("")
, m_filename("")
{ {
m_volume.shader_outside_printer_detection_enabled = false; m_volume.shader_outside_printer_detection_enabled = false;
} }
@ -1743,19 +1515,11 @@ void GLModel::set_scale(const Vec3d& scale)
void GLModel::reset() void GLModel::reset()
{ {
m_volume.release_geometry(); m_volume.indexed_vertex_array.release_geometry();
m_filename = ""; m_filename = "";
} }
void GLModel::render() const void GLModel::render() const
{
if (m_useVBOs)
render_VBOs();
else
render_legacy();
}
void GLModel::render_VBOs() const
{ {
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
@ -1768,7 +1532,8 @@ void GLModel::render_VBOs() const
glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id)); glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &current_program_id));
GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1; GLint color_id = (current_program_id > 0) ? ::glGetUniformLocation(current_program_id, "uniform_color") : -1;
glcheck(); glcheck();
m_volume.render_VBOs(color_id, -1, -1);
m_volume.render(color_id, -1, -1);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
@ -1779,26 +1544,7 @@ void GLModel::render_VBOs() const
glsafe(::glDisable(GL_BLEND)); glsafe(::glDisable(GL_BLEND));
} }
void GLModel::render_legacy() const bool GLArrow::on_init()
{
glsafe(::glEnable(GL_LIGHTING));
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
glsafe(::glCullFace(GL_BACK));
glsafe(::glEnableClientState(GL_VERTEX_ARRAY));
glsafe(::glEnableClientState(GL_NORMAL_ARRAY));
m_volume.render_legacy();
glsafe(::glDisableClientState(GL_VERTEX_ARRAY));
glsafe(::glDisableClientState(GL_NORMAL_ARRAY));
glsafe(::glDisable(GL_BLEND));
glsafe(::glDisable(GL_LIGHTING));
}
bool GLArrow::on_init(bool useVBOs)
{ {
Pointf3s vertices; Pointf3s vertices;
std::vector<Vec3crd> triangles; std::vector<Vec3crd> triangles;
@ -1851,9 +1597,7 @@ bool GLArrow::on_init(bool useVBOs)
triangles.emplace_back(6, 0, 7); triangles.emplace_back(6, 0, 7);
triangles.emplace_back(7, 13, 6); triangles.emplace_back(7, 13, 6);
m_useVBOs = useVBOs; m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles), useVBOs);
m_volume.finalize_geometry(m_useVBOs);
return true; return true;
} }
@ -1865,7 +1609,7 @@ GLCurvedArrow::GLCurvedArrow(unsigned int resolution)
m_resolution = 1; m_resolution = 1;
} }
bool GLCurvedArrow::on_init(bool useVBOs) bool GLCurvedArrow::on_init()
{ {
Pointf3s vertices; Pointf3s vertices;
std::vector<Vec3crd> triangles; std::vector<Vec3crd> triangles;
@ -1966,14 +1710,11 @@ bool GLCurvedArrow::on_init(bool useVBOs)
triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0); triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0);
triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1); triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1);
m_useVBOs = useVBOs; m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles));
m_volume.indexed_vertex_array.load_mesh(TriangleMesh(vertices, triangles), useVBOs);
m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
m_volume.finalize_geometry(m_useVBOs);
return true; return true;
} }
bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs) bool GLBed::on_init_from_file(const std::string& filename)
{ {
reset(); reset();
@ -1994,7 +1735,6 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
} }
m_filename = filename; m_filename = filename;
m_useVBOs = useVBOs;
ModelObject* model_object = model.objects.front(); ModelObject* model_object = model.objects.front();
model_object->center_around_origin(); model_object->center_around_origin();
@ -2002,14 +1742,11 @@ bool GLBed::on_init_from_file(const std::string& filename, bool useVBOs)
TriangleMesh mesh = model.mesh(); TriangleMesh mesh = model.mesh();
mesh.repair(); mesh.repair();
m_volume.indexed_vertex_array.load_mesh(mesh, useVBOs); m_volume.indexed_vertex_array.load_mesh(mesh);
float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f }; float color[4] = { 0.235f, 0.235f, 0.235f, 1.0f };
set_color(color, 4); set_color(color, 4);
m_volume.bounding_box = m_volume.indexed_vertex_array.bounding_box();
m_volume.finalize_geometry(m_useVBOs);
return true; return true;
} }

View file

@ -56,7 +56,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0), vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0), triangle_indices_VBO_id(0),
quad_indices_VBO_id(0) quad_indices_VBO_id(0)
{ this->setup_sizes(); } {}
GLIndexedVertexArray(const GLIndexedVertexArray &rhs) : GLIndexedVertexArray(const GLIndexedVertexArray &rhs) :
vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved), vertices_and_normals_interleaved(rhs.vertices_and_normals_interleaved),
triangle_indices(rhs.triangle_indices), triangle_indices(rhs.triangle_indices),
@ -64,7 +64,7 @@ public:
vertices_and_normals_interleaved_VBO_id(0), vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0), triangle_indices_VBO_id(0),
quad_indices_VBO_id(0) quad_indices_VBO_id(0)
{ this->setup_sizes(); } {}
GLIndexedVertexArray(GLIndexedVertexArray &&rhs) : GLIndexedVertexArray(GLIndexedVertexArray &&rhs) :
vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)), vertices_and_normals_interleaved(std::move(rhs.vertices_and_normals_interleaved)),
triangle_indices(std::move(rhs.triangle_indices)), triangle_indices(std::move(rhs.triangle_indices)),
@ -72,7 +72,9 @@ public:
vertices_and_normals_interleaved_VBO_id(0), vertices_and_normals_interleaved_VBO_id(0),
triangle_indices_VBO_id(0), triangle_indices_VBO_id(0),
quad_indices_VBO_id(0) quad_indices_VBO_id(0)
{ this->setup_sizes(); } {}
~GLIndexedVertexArray() { release_geometry(); }
GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs) GLIndexedVertexArray& operator=(const GLIndexedVertexArray &rhs)
{ {
@ -82,7 +84,10 @@ public:
this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved; this->vertices_and_normals_interleaved = rhs.vertices_and_normals_interleaved;
this->triangle_indices = rhs.triangle_indices; this->triangle_indices = rhs.triangle_indices;
this->quad_indices = rhs.quad_indices; this->quad_indices = rhs.quad_indices;
this->setup_sizes(); this->m_bounding_box = rhs.m_bounding_box;
vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
triangle_indices_size = rhs.triangle_indices_size;
quad_indices_size = rhs.quad_indices_size;
return *this; return *this;
} }
@ -94,30 +99,32 @@ public:
this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved); this->vertices_and_normals_interleaved = std::move(rhs.vertices_and_normals_interleaved);
this->triangle_indices = std::move(rhs.triangle_indices); this->triangle_indices = std::move(rhs.triangle_indices);
this->quad_indices = std::move(rhs.quad_indices); this->quad_indices = std::move(rhs.quad_indices);
this->setup_sizes(); this->m_bounding_box = std::move(rhs.m_bounding_box);
vertices_and_normals_interleaved_size = rhs.vertices_and_normals_interleaved_size;
triangle_indices_size = rhs.triangle_indices_size;
quad_indices_size = rhs.quad_indices_size;
return *this; return *this;
} }
// Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x) // Vertices and their normals, interleaved to be used by void glInterleavedArrays(GL_N3F_V3F, 0, x)
std::vector<float> vertices_and_normals_interleaved; mutable std::vector<float> vertices_and_normals_interleaved;
std::vector<int> triangle_indices; mutable std::vector<int> triangle_indices;
std::vector<int> quad_indices; mutable std::vector<int> quad_indices;
// When the geometry data is loaded into the graphics card as Vertex Buffer Objects, // When the geometry data is loaded into the graphics card as Vertex Buffer Objects,
// the above mentioned std::vectors are cleared and the following variables keep their original length. // the above mentioned std::vectors are cleared and the following variables keep their original length.
size_t vertices_and_normals_interleaved_size; size_t vertices_and_normals_interleaved_size{ 0 };
size_t triangle_indices_size; size_t triangle_indices_size{ 0 };
size_t quad_indices_size; size_t quad_indices_size{ 0 };
// IDs of the Vertex Array Objects, into which the geometry has been loaded. // IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not used. // Zero if the VBOs are not sent to GPU yet.
unsigned int vertices_and_normals_interleaved_VBO_id; mutable unsigned int vertices_and_normals_interleaved_VBO_id{ 0 };
unsigned int triangle_indices_VBO_id; mutable unsigned int triangle_indices_VBO_id{ 0 };
unsigned int quad_indices_VBO_id; mutable unsigned int quad_indices_VBO_id{ 0 };
void load_mesh_flat_shading(const TriangleMesh &mesh);
void load_mesh_full_shading(const TriangleMesh &mesh); void load_mesh_full_shading(const TriangleMesh &mesh);
void load_mesh(const TriangleMesh &mesh, bool use_VBOs) { use_VBOs ? this->load_mesh_full_shading(mesh) : this->load_mesh_flat_shading(mesh); } void load_mesh(const TriangleMesh& mesh) { this->load_mesh_full_shading(mesh); }
inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; } inline bool has_VBOs() const { return vertices_and_normals_interleaved_VBO_id != 0; }
@ -128,6 +135,10 @@ public:
} }
inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) { inline void push_geometry(float x, float y, float z, float nx, float ny, float nz) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity()) if (this->vertices_and_normals_interleaved.size() + 6 > this->vertices_and_normals_interleaved.capacity())
this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6)); this->vertices_and_normals_interleaved.reserve(next_highest_power_of_2(this->vertices_and_normals_interleaved.size() + 6));
this->vertices_and_normals_interleaved.push_back(nx); this->vertices_and_normals_interleaved.push_back(nx);
@ -136,6 +147,9 @@ public:
this->vertices_and_normals_interleaved.push_back(x); this->vertices_and_normals_interleaved.push_back(x);
this->vertices_and_normals_interleaved.push_back(y); this->vertices_and_normals_interleaved.push_back(y);
this->vertices_and_normals_interleaved.push_back(z); this->vertices_and_normals_interleaved.push_back(z);
this->vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
m_bounding_box.merge(Vec3f(x, y, z).cast<double>());
}; };
inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) { inline void push_geometry(double x, double y, double z, double nx, double ny, double nz) {
@ -147,80 +161,66 @@ public:
} }
inline void push_triangle(int idx1, int idx2, int idx3) { inline void push_triangle(int idx1, int idx2, int idx3) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity()) if (this->triangle_indices.size() + 3 > this->vertices_and_normals_interleaved.capacity())
this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3)); this->triangle_indices.reserve(next_highest_power_of_2(this->triangle_indices.size() + 3));
this->triangle_indices.push_back(idx1); this->triangle_indices.push_back(idx1);
this->triangle_indices.push_back(idx2); this->triangle_indices.push_back(idx2);
this->triangle_indices.push_back(idx3); this->triangle_indices.push_back(idx3);
this->triangle_indices_size = this->triangle_indices.size();
}; };
inline void push_quad(int idx1, int idx2, int idx3, int idx4) { inline void push_quad(int idx1, int idx2, int idx3, int idx4) {
assert(this->vertices_and_normals_interleaved_VBO_id == 0);
if (this->vertices_and_normals_interleaved_VBO_id != 0)
return;
if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity()) if (this->quad_indices.size() + 4 > this->vertices_and_normals_interleaved.capacity())
this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4)); this->quad_indices.reserve(next_highest_power_of_2(this->quad_indices.size() + 4));
this->quad_indices.push_back(idx1); this->quad_indices.push_back(idx1);
this->quad_indices.push_back(idx2); this->quad_indices.push_back(idx2);
this->quad_indices.push_back(idx3); this->quad_indices.push_back(idx3);
this->quad_indices.push_back(idx4); this->quad_indices.push_back(idx4);
this->quad_indices_size = this->quad_indices.size();
}; };
// Finalize the initialization of the geometry & indices, // Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects // upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
void finalize_geometry(bool use_VBOs); void finalize_geometry() const;
// Release the geometry data, release OpenGL VBOs. // Release the geometry data, release OpenGL VBOs.
void release_geometry(); void release_geometry();
// Render either using an immediate mode, or the VBOs.
void render() const; void render() const;
void render(const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& qverts_range) const; void render(const std::pair<size_t, size_t>& tverts_range, const std::pair<size_t, size_t>& qverts_range) const;
// Is there any geometry data stored? // Is there any geometry data stored?
bool empty() const { return vertices_and_normals_interleaved_size == 0; } bool empty() const { return vertices_and_normals_interleaved_size == 0; }
// Is this object indexed, or is it just a set of triangles?
bool indexed() const { return ! this->empty() && this->triangle_indices_size + this->quad_indices_size > 0; }
void clear() { void clear() {
this->vertices_and_normals_interleaved.clear(); this->vertices_and_normals_interleaved.clear();
this->triangle_indices.clear(); this->triangle_indices.clear();
this->quad_indices.clear(); this->quad_indices.clear();
this->setup_sizes(); this->m_bounding_box.reset();
vertices_and_normals_interleaved_size = 0;
triangle_indices_size = 0;
quad_indices_size = 0;
} }
// Shrink the internal storage to tighly fit the data stored. // Shrink the internal storage to tighly fit the data stored.
void shrink_to_fit() { void shrink_to_fit() const {
if (! this->has_VBOs())
this->setup_sizes();
this->vertices_and_normals_interleaved.shrink_to_fit(); this->vertices_and_normals_interleaved.shrink_to_fit();
this->triangle_indices.shrink_to_fit(); this->triangle_indices.shrink_to_fit();
this->quad_indices.shrink_to_fit(); this->quad_indices.shrink_to_fit();
} }
BoundingBoxf3 bounding_box() const { const BoundingBoxf3& bounding_box() const { return m_bounding_box; }
BoundingBoxf3 bbox;
if (! this->vertices_and_normals_interleaved.empty()) {
bbox.defined = true;
bbox.min(0) = bbox.max(0) = this->vertices_and_normals_interleaved[3];
bbox.min(1) = bbox.max(1) = this->vertices_and_normals_interleaved[4];
bbox.min(2) = bbox.max(2) = this->vertices_and_normals_interleaved[5];
for (size_t i = 9; i < this->vertices_and_normals_interleaved.size(); i += 6) {
const float *verts = this->vertices_and_normals_interleaved.data() + i;
bbox.min(0) = std::min<coordf_t>(bbox.min(0), verts[0]);
bbox.min(1) = std::min<coordf_t>(bbox.min(1), verts[1]);
bbox.min(2) = std::min<coordf_t>(bbox.min(2), verts[2]);
bbox.max(0) = std::max<coordf_t>(bbox.max(0), verts[0]);
bbox.max(1) = std::max<coordf_t>(bbox.max(1), verts[1]);
bbox.max(2) = std::max<coordf_t>(bbox.max(2), verts[2]);
}
}
return bbox;
}
private: private:
inline void setup_sizes() { BoundingBoxf3 m_bounding_box;
vertices_and_normals_interleaved_size = this->vertices_and_normals_interleaved.size();
triangle_indices_size = this->triangle_indices.size();
quad_indices_size = this->quad_indices.size();
}
}; };
class GLVolume { class GLVolume {
@ -263,8 +263,6 @@ private:
mutable bool m_transformed_convex_hull_bounding_box_dirty; mutable bool m_transformed_convex_hull_bounding_box_dirty;
public: public:
// Bounding box of this volume, in unscaled coordinates.
BoundingBoxf3 bounding_box;
// Color of the triangles / quads held by this volume. // Color of the triangles / quads held by this volume.
float color[4]; float color[4];
// Color used to render this volume. // Color used to render this volume.
@ -329,6 +327,9 @@ public:
// Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer. // Offset into qverts & tverts, or offsets into indices stored into an OpenGL name_index_buffer.
std::vector<size_t> offsets; std::vector<size_t> offsets;
// Bounding box of this volume, in unscaled coordinates.
const BoundingBoxf3& bounding_box() const { return this->indexed_vertex_array.bounding_box(); }
void set_render_color(float r, float g, float b, float a); void set_render_color(float r, float g, float b, float a);
void set_render_color(const float* rgba, unsigned int size); void set_render_color(const float* rgba, unsigned int size);
// Sets render color in dependence of current state // Sets render color in dependence of current state
@ -411,14 +412,13 @@ public:
const BoundingBoxf3& transformed_convex_hull_bounding_box() const; const BoundingBoxf3& transformed_convex_hull_bounding_box() const;
bool empty() const { return this->indexed_vertex_array.empty(); } bool empty() const { return this->indexed_vertex_array.empty(); }
bool indexed() const { return this->indexed_vertex_array.indexed(); }
void set_range(coordf_t low, coordf_t high); void set_range(coordf_t low, coordf_t high);
void render() const;
void render_VBOs(int color_id, int detection_id, int worldmatrix_id) const;
void render_legacy() const;
void finalize_geometry(bool use_VBOs) { this->indexed_vertex_array.finalize_geometry(use_VBOs); } void render() const;
void render(int color_id, int detection_id, int worldmatrix_id) const;
void finalize_geometry() { this->indexed_vertex_array.finalize_geometry(); }
void release_geometry() { this->indexed_vertex_array.release_geometry(); } void release_geometry() { this->indexed_vertex_array.release_geometry(); }
void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; }
@ -462,16 +462,14 @@ public:
const ModelObject* model_object, const ModelObject* model_object,
int obj_idx, int obj_idx,
const std::vector<int>& instance_idxs, const std::vector<int>& instance_idxs,
const std::string &color_by, const std::string& color_by);
bool use_VBOs);
int load_object_volume( int load_object_volume(
const ModelObject* model_object, const ModelObject* model_object,
int obj_idx, int obj_idx,
int volume_idx, int volume_idx,
int instance_idx, int instance_idx,
const std::string &color_by, const std::string& color_by);
bool use_VBOs);
// Load SLA auxiliary GLVolumes (for support trees or pad). // Load SLA auxiliary GLVolumes (for support trees or pad).
void load_object_auxiliary( void load_object_auxiliary(
@ -481,20 +479,18 @@ public:
const std::vector<std::pair<size_t, size_t>>& instances, const std::vector<std::pair<size_t, size_t>>& instances,
SLAPrintObjectStep milestone, SLAPrintObjectStep milestone,
// Timestamp of the last change of the milestone // Timestamp of the last change of the milestone
size_t timestamp, size_t timestamp);
bool use_VBOs);
int load_wipe_tower_preview( int load_wipe_tower_preview(
int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool use_VBOs, bool size_unknown, float brim_width); int obj_idx, float pos_x, float pos_y, float width, float depth, float height, float rotation_angle, bool size_unknown, float brim_width);
// Render the volumes by OpenGL. // Render the volumes by OpenGL.
void render_VBOs(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const; void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
void render_legacy(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, std::function<bool(const GLVolume&)> filter_func = std::function<bool(const GLVolume&)>()) const;
// Finalize the initialization of the geometry & indices, // Finalize the initialization of the geometry & indices,
// upload the geometry and indices to OpenGL VBO objects // upload the geometry and indices to OpenGL VBO objects
// and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs. // and shrink the allocated data, possibly relasing it if it has been loaded into the VBOs.
void finalize_geometry(bool use_VBOs) { for (auto *v : volumes) v->finalize_geometry(use_VBOs); } void finalize_geometry() { for (auto* v : volumes) v->finalize_geometry(); }
// Release the geometry data assigned to the volumes. // Release the geometry data assigned to the volumes.
// If OpenGL VBOs were allocated, an OpenGL context has to be active to release them. // If OpenGL VBOs were allocated, an OpenGL context has to be active to release them.
void release_geometry() { for (auto *v : volumes) v->release_geometry(); } void release_geometry() { for (auto *v : volumes) v->release_geometry(); }
@ -533,17 +529,16 @@ class GLModel
{ {
protected: protected:
GLVolume m_volume; GLVolume m_volume;
bool m_useVBOs;
std::string m_filename; std::string m_filename;
public: public:
GLModel(); GLModel();
virtual ~GLModel(); virtual ~GLModel();
bool init(bool useVBOs) { return on_init(useVBOs); } bool init() { return on_init(); }
bool init_from_file(const std::string& filename, bool useVBOs) { return on_init_from_file(filename, useVBOs); } bool init_from_file(const std::string& filename) { return on_init_from_file(filename); }
void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box.center()); } void center_around(const Vec3d& center) { m_volume.set_volume_offset(center - m_volume.bounding_box().center()); }
void set_color(const float* color, unsigned int size); void set_color(const float* color, unsigned int size);
const Vec3d& get_offset() const; const Vec3d& get_offset() const;
@ -554,7 +549,7 @@ public:
void set_scale(const Vec3d& scale); void set_scale(const Vec3d& scale);
const std::string& get_filename() const { return m_filename; } const std::string& get_filename() const { return m_filename; }
const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box; } const BoundingBoxf3& get_bounding_box() const { return m_volume.bounding_box(); }
const BoundingBoxf3& get_transformed_bounding_box() const { return m_volume.transformed_bounding_box(); } const BoundingBoxf3& get_transformed_bounding_box() const { return m_volume.transformed_bounding_box(); }
void reset(); void reset();
@ -562,18 +557,14 @@ public:
void render() const; void render() const;
protected: protected:
virtual bool on_init(bool useVBOs) { return false; } virtual bool on_init() { return false; }
virtual bool on_init_from_file(const std::string& filename, bool useVBOs) { return false; } virtual bool on_init_from_file(const std::string& filename) { return false; }
private:
void render_VBOs() const;
void render_legacy() const;
}; };
class GLArrow : public GLModel class GLArrow : public GLModel
{ {
protected: protected:
virtual bool on_init(bool useVBOs); virtual bool on_init();
}; };
class GLCurvedArrow : public GLModel class GLCurvedArrow : public GLModel
@ -584,13 +575,13 @@ public:
explicit GLCurvedArrow(unsigned int resolution); explicit GLCurvedArrow(unsigned int resolution);
protected: protected:
virtual bool on_init(bool useVBOs); virtual bool on_init();
}; };
class GLBed : public GLModel class GLBed : public GLModel
{ {
protected: protected:
virtual bool on_init_from_file(const std::string& filename, bool useVBOs); virtual bool on_init_from_file(const std::string& filename);
}; };
class _3DScene class _3DScene

View file

@ -53,10 +53,7 @@ void BedShapeDialog::on_dpi_changed(const wxRect &suggested_rect)
void BedShapePanel::build_panel(ConfigOptionPoints* default_pt) void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
{ {
// on_change(nullptr); auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
auto box = new wxStaticBox(this, wxID_ANY, _(L("Shape")));
auto sbsizer = new wxStaticBoxSizer(box, wxVERTICAL);
// shape options // shape options
m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition, m_shape_options_book = new wxChoicebook(this, wxID_ANY, wxDefaultPosition,
@ -92,12 +89,14 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
Line line{ "", "" }; Line line{ "", "" };
line.full_width = 1; line.full_width = 1;
line.widget = [this](wxWindow* parent) { line.widget = [this](wxWindow* parent) {
auto btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")), wxDefaultPosition, wxDefaultSize); auto shape_btn = new wxButton(parent, wxID_ANY, _(L("Load shape from STL...")));
wxSizer* shape_sizer = new wxBoxSizer(wxHORIZONTAL);
shape_sizer->Add(shape_btn, 1, wxEXPAND);
auto sizer = new wxBoxSizer(wxHORIZONTAL); wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(btn); sizer->Add(shape_sizer, 1, wxEXPAND);
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e) shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
{ {
load_stl(); load_stl();
})); }));
@ -106,7 +105,7 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
}; };
optgroup->append_line(line); optgroup->append_line(line);
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e) Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent& e)
{ {
update_shape(); update_shape();
})); }));
@ -117,7 +116,7 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// main sizer // main sizer
auto top_sizer = new wxBoxSizer(wxHORIZONTAL); auto top_sizer = new wxBoxSizer(wxHORIZONTAL);
top_sizer->Add(sbsizer, 0, wxEXPAND | wxLeft | wxTOP | wxBOTTOM, 10); top_sizer->Add(sbsizer, 0, wxEXPAND | wxLEFT | wxTOP | wxBOTTOM, 10);
if (m_canvas) if (m_canvas)
top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ; top_sizer->Add(m_canvas, 1, wxEXPAND | wxALL, 10) ;
@ -135,7 +134,6 @@ void BedShapePanel::build_panel(ConfigOptionPoints* default_pt)
// Create a panel for a rectangular / circular / custom bed shape. // Create a panel for a rectangular / circular / custom bed shape.
ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title) ConfigOptionsGroupShp BedShapePanel::init_shape_options_page(const wxString& title)
{ {
auto panel = new wxPanel(m_shape_options_book); auto panel = new wxPanel(m_shape_options_book);
ConfigOptionsGroupShp optgroup; ConfigOptionsGroupShp optgroup;
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings"))); optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Settings")));
@ -234,7 +232,7 @@ void BedShapePanel::set_shape(ConfigOptionPoints* points)
// This is a custom bed shape, use the polygon provided. // This is a custom bed shape, use the polygon provided.
m_shape_options_book->SetSelection(SHAPE_CUSTOM); m_shape_options_book->SetSelection(SHAPE_CUSTOM);
// Copy the polygon to the canvas, make a copy of the array. // Copy the polygon to the canvas, make a copy of the array.
m_canvas->m_bed_shape = points->values; m_loaded_bed_shape = points->values;
update_shape(); update_shape();
} }

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

@ -912,7 +912,8 @@ GLCanvas3D::LegendTexture::LegendTexture()
void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas, void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePreviewData& preview_data, const GLCanvas3D& canvas,
std::vector<std::pair<double, double>>& cp_legend_values) std::vector<std::pair<double, double>>& cp_legend_values)
{ {
if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint) if (preview_data.extrusion.view_type == GCodePreviewData::Extrusion::ColorPrint &&
wxGetApp().extruders_edited_cnt() == 1) // show color change legend only for single-material presets
{ {
auto& config = wxGetApp().preset_bundle->project_config; auto& config = wxGetApp().preset_bundle->project_config;
const std::vector<double>& color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values; const std::vector<double>& color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values;
@ -1211,7 +1212,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_model(nullptr) , m_model(nullptr)
, m_dirty(true) , m_dirty(true)
, m_initialized(false) , m_initialized(false)
, m_use_VBOs(false)
, m_apply_zoom_to_volumes_filter(false) , m_apply_zoom_to_volumes_filter(false)
, m_legend_texture_enabled(false) , m_legend_texture_enabled(false)
, m_picking_enabled(false) , m_picking_enabled(false)
@ -1252,7 +1252,7 @@ void GLCanvas3D::post_event(wxEvent &&event)
wxPostEvent(m_canvas, event); wxPostEvent(m_canvas, event);
} }
bool GLCanvas3D::init(bool useVBOs) bool GLCanvas3D::init()
{ {
if (m_initialized) if (m_initialized)
return true; return true;
@ -1303,30 +1303,30 @@ bool GLCanvas3D::init(bool useVBOs)
if (m_multisample_allowed) if (m_multisample_allowed)
glsafe(::glEnable(GL_MULTISAMPLE)); glsafe(::glEnable(GL_MULTISAMPLE));
if (useVBOs && !m_shader.init("gouraud.vs", "gouraud.fs")) if (!m_shader.init("gouraud.vs", "gouraud.fs"))
{
std::cout << "Unable to initialize gouraud shader: please, check that the files gouraud.vs and gouraud.fs are available" << std::endl;
return false; return false;
}
if (m_toolbar.is_enabled() && useVBOs && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs")) if (m_toolbar.is_enabled() && !m_layers_editing.init("variable_layer_height.vs", "variable_layer_height.fs"))
{
std::cout << "Unable to initialize variable_layer_height shader: please, check that the files variable_layer_height.vs and variable_layer_height.fs are available" << std::endl;
return false; return false;
}
m_use_VBOs = useVBOs; // // on linux the gl context is not valid until the canvas is not shown on screen
// // we defer the geometry finalization of volumes until the first call to render()
// if (!m_volumes.empty())
// m_volumes.finalize_geometry();
// on linux the gl context is not valid until the canvas is not shown on screen if (m_gizmos.is_enabled() && !m_gizmos.init(*this))
// we defer the geometry finalization of volumes until the first call to render()
if (!m_volumes.empty())
m_volumes.finalize_geometry(m_use_VBOs);
if (m_gizmos.is_enabled()) {
if (! m_gizmos.init(*this)) {
std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl;
return false;
}
}
if (!_init_toolbar()) if (!_init_toolbar())
return false; return false;
if (m_selection.is_enabled() && !m_selection.init(m_use_VBOs)) if (m_selection.is_enabled() && !m_selection.init())
return false; return false;
post_event(SimpleEvent(EVT_GLCANVAS_INIT)); post_event(SimpleEvent(EVT_GLCANVAS_INIT));
@ -1356,7 +1356,6 @@ void GLCanvas3D::reset_volumes()
if (!m_volumes.empty()) if (!m_volumes.empty())
{ {
m_selection.clear(); m_selection.clear();
m_volumes.release_geometry();
m_volumes.clear(); m_volumes.clear();
m_dirty = true; m_dirty = true;
} }
@ -1778,7 +1777,7 @@ std::vector<int> GLCanvas3D::load_object(const ModelObject& model_object, int ob
instance_idxs.push_back(i); instance_idxs.push_back(i);
} }
} }
return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by, m_use_VBOs && m_initialized); return m_volumes.load_object(&model_object, obj_idx, instance_idxs, m_color_by);
} }
std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx) std::vector<int> GLCanvas3D::load_object(const Model& model, int obj_idx)
@ -1918,7 +1917,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(volume_idx_wipe_tower_old == -1); assert(volume_idx_wipe_tower_old == -1);
volume_idx_wipe_tower_old = (int)volume_id; volume_idx_wipe_tower_old = (int)volume_id;
} }
volume->release_geometry();
if (! m_reload_delayed) if (! m_reload_delayed)
delete volume; delete volume;
} else { } else {
@ -1966,7 +1964,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) { if (it->new_geometry()) {
// New volume. // New volume.
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_use_VBOs && m_initialized); m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by);
m_volumes.volumes.back()->geometry_id = key.geometry_id; m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true; update_object_list = true;
} else { } else {
@ -2031,7 +2029,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
for (size_t istep = 0; istep < sla_steps.size(); ++istep) for (size_t istep = 0; istep < sla_steps.size(); ++istep)
if (!instances[istep].empty()) if (!instances[istep].empty())
m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_use_VBOs && m_initialized); m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp);
} }
// Shift-up all volumes of the object so that it has the right elevation with respect to the print bed // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed
@ -2070,7 +2068,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (!print->is_step_done(psWipeTower)) if (!print->is_step_done(psWipeTower))
depth = (900.f/w) * (float)(extruders_count - 1); depth = (900.f/w) * (float)(extruders_count - 1);
int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview( int volume_idx_wipe_tower_new = m_volumes.load_wipe_tower_preview(
1000, x, y, w, depth, (float)height, a, m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), 1000, x, y, w, depth, (float)height, a, !print->is_step_done(psWipeTower),
brim_spacing * 4.5f); brim_spacing * 4.5f);
if (volume_idx_wipe_tower_old != -1) if (volume_idx_wipe_tower_old != -1)
map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new;
@ -3323,6 +3321,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"));
@ -3356,7 +3360,7 @@ arr::WipeTowerInfo GLCanvas3D::get_wipe_tower_info() const
wti.pos = Vec2d(m_config->opt_float("wipe_tower_x"), wti.pos = Vec2d(m_config->opt_float("wipe_tower_x"),
m_config->opt_float("wipe_tower_y")); m_config->opt_float("wipe_tower_y"));
wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle"); wti.rotation = (M_PI/180.) * m_config->opt_float("wipe_tower_rotation_angle");
const BoundingBoxf3& bb = vol->bounding_box; const BoundingBoxf3& bb = vol->bounding_box();
wti.bb_size = Vec2d(bb.size()(0), bb.size()(1)); wti.bb_size = Vec2d(bb.size()(0), bb.size()(1));
break; break;
} }
@ -3821,7 +3825,11 @@ void GLCanvas3D::_render_bed(float theta) const
#if ENABLE_RETINA_GL #if ENABLE_RETINA_GL
scale_factor = m_retina_helper->get_scale_factor(); scale_factor = m_retina_helper->get_scale_factor();
#endif // ENABLE_RETINA_GL #endif // ENABLE_RETINA_GL
m_bed.render(const_cast<GLCanvas3D*>(this), theta, m_use_VBOs, scale_factor); #if ENABLE_TEXTURES_FROM_SVG
m_bed.render(const_cast<GLCanvas3D*>(this), theta, scale_factor);
#else
m_bed.render(theta, scale_factor);
#endif // ENABLE_TEXTURES_FROM_SVG
} }
void GLCanvas3D::_render_axes() const void GLCanvas3D::_render_axes() const
@ -3829,8 +3837,6 @@ void GLCanvas3D::_render_axes() const
m_bed.render_axes(); m_bed.render_axes();
} }
void GLCanvas3D::_render_objects() const void GLCanvas3D::_render_objects() const
{ {
if (m_volumes.empty()) if (m_volumes.empty())
@ -3841,8 +3847,6 @@ void GLCanvas3D::_render_objects() const
m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane(); m_camera_clipping_plane = m_gizmos.get_sla_clipping_plane();
if (m_use_VBOs)
{
if (m_picking_enabled) if (m_picking_enabled)
{ {
// Update the layer editing selection to the first object selected, update the current object maximum Z. // Update the layer editing selection to the first object selected, update the current object maximum Z.
@ -3866,7 +3870,7 @@ void GLCanvas3D::_render_objects() const
m_shader.start_using(); m_shader.start_using();
if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { if (m_picking_enabled && !m_gizmos.is_dragging() && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) {
int object_id = m_layers_editing.last_object_id; int object_id = m_layers_editing.last_object_id;
m_volumes.render_VBOs(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume &volume) { m_volumes.render(GLVolumeCollection::Opaque, false, m_camera.get_view_matrix(), [object_id](const GLVolume& volume) {
// Which volume to paint without the layer height profile shader? // Which volume to paint without the layer height profile shader?
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
}); });
@ -3874,41 +3878,12 @@ void GLCanvas3D::_render_objects() const
m_layers_editing.render_volumes(*this, this->m_volumes); m_layers_editing.render_volumes(*this, this->m_volumes);
} else { } else {
// do not cull backfaces to show broken geometry, if any // do not cull backfaces to show broken geometry, if any
m_volumes.render_VBOs(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) { m_volumes.render(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
}); });
} }
m_volumes.render_VBOs(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix()); m_volumes.render(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
m_shader.stop_using(); m_shader.stop_using();
}
else
{
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
::glEnable(GL_CLIP_PLANE0);
if (m_use_clipping_planes)
{
glsafe(::glClipPlane(GL_CLIP_PLANE1, (GLdouble*)m_clipping_planes[0].get_data()));
glsafe(::glEnable(GL_CLIP_PLANE1));
glsafe(::glClipPlane(GL_CLIP_PLANE2, (GLdouble*)m_clipping_planes[1].get_data()));
glsafe(::glEnable(GL_CLIP_PLANE2));
}
// do not cull backfaces to show broken geometry, if any
m_volumes.render_legacy(GLVolumeCollection::Opaque, m_picking_enabled, m_camera.get_view_matrix(), [this](const GLVolume& volume) {
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
});
m_volumes.render_legacy(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
::glDisable(GL_CLIP_PLANE0);
if (m_use_clipping_planes)
{
glsafe(::glDisable(GL_CLIP_PLANE1));
glsafe(::glDisable(GL_CLIP_PLANE2));
}
}
m_camera_clipping_plane = ClippingPlane::ClipsNothing(); m_camera_clipping_plane = ClippingPlane::ClipsNothing();
glsafe(::glDisable(GL_LIGHTING)); glsafe(::glDisable(GL_LIGHTING));
@ -4025,6 +4000,7 @@ void GLCanvas3D::_render_current_gizmo() const
void GLCanvas3D::_render_gizmos_overlay() const void GLCanvas3D::_render_gizmos_overlay() const
{ {
#if ENABLE_SVG_ICONS
#if ENABLE_RETINA_GL #if ENABLE_RETINA_GL
// m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor()); // m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor());
const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale(); const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale();
@ -4035,6 +4011,7 @@ void GLCanvas3D::_render_gizmos_overlay() const
const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale()); const float size = int(GLGizmosManager::Default_Icons_Size*wxGetApp().toolbar_icon_scale());
m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment
#endif /* __WXMSW__ */ #endif /* __WXMSW__ */
#endif // ENABLE_SVG_ICONS
m_gizmos.render_overlay(*this, m_selection); m_gizmos.render_overlay(*this, m_selection);
} }
@ -4288,16 +4265,9 @@ 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();
} }
void GLCanvas3D::_update_volumes_hover_state() const void GLCanvas3D::_update_volumes_hover_state() const
{ {
for (GLVolume* v : m_volumes.volumes) for (GLVolume* v : m_volumes.volumes)
@ -4506,8 +4476,6 @@ void GLCanvas3D::_load_print_toolpaths()
_3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume); _3DScene::extrusionentity_to_verts(print->skirt(), print_zs[i], Point(0, 0), volume);
} }
volume.bounding_box = volume.indexed_vertex_array.bounding_box();
volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
} }
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values) void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors, const std::vector<double>& color_print_values)
@ -4672,8 +4640,6 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume. // Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array; vol.indexed_vertex_array = vol_new.indexed_vertex_array;
// Finalize a bounding box of the old GLVolume.
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated. // Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear(); vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory. // Just make sure that clear did not clear the reserved memory.
@ -4681,10 +4647,6 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
} }
} }
} }
for (GLVolume *vol : vols) {
vol->bounding_box = vol->indexed_vertex_array.bounding_box();
vol->indexed_vertex_array.shrink_to_fit();
}
}); });
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results"; BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
@ -4693,8 +4655,6 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }), [](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end()); m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
} }
@ -4844,18 +4804,12 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array); vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume. // Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array; vol.indexed_vertex_array = vol_new.indexed_vertex_array;
// Finalize a bounding box of the old GLVolume.
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated. // Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear(); vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory. // Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve()); vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
} }
} }
for (GLVolume *vol : vols) {
vol->bounding_box = vol->indexed_vertex_array.bounding_box();
vol->indexed_vertex_array.shrink_to_fit();
}
}); });
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results"; BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
@ -4864,8 +4818,6 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(), std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }), [](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end()); m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end"; BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
} }
@ -5042,17 +4994,6 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
} }
} }
} }
// finalize volumes and sends geometry to gpu
if (m_volumes.volumes.size() > initial_volumes_count)
{
for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
{
GLVolume* volume = m_volumes.volumes[i];
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
}
} }
void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors) void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
@ -5097,17 +5038,6 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data,
return; return;
} }
// finalize volumes and sends geometry to gpu
if (m_volumes.volumes.size() > initial_volumes_count)
{
for (size_t i = initial_volumes_count; i < m_volumes.volumes.size(); ++i)
{
GLVolume* volume = m_volumes.volumes[i];
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
}
} }
bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data) bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
@ -5336,10 +5266,6 @@ void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data)
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume); _3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
} }
// finalize volumes and sends geometry to gpu
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
} }
} }
@ -5367,10 +5293,6 @@ void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data)
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume); _3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
} }
// finalize volumes and sends geometry to gpu
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
} }
} }
@ -5396,7 +5318,7 @@ void GLCanvas3D::_load_fff_shells()
instance_ids[i] = i; instance_ids[i] = i;
} }
m_volumes.load_object(model_obj, object_id, instance_ids, "object", m_use_VBOs && m_initialized); m_volumes.load_object(model_obj, object_id, instance_ids, "object");
++object_id; ++object_id;
} }
@ -5418,7 +5340,7 @@ void GLCanvas3D::_load_fff_shells()
if (!print->is_step_done(psWipeTower)) if (!print->is_step_done(psWipeTower))
depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1); depth = (900.f/config.wipe_tower_width) * (float)(extruders_count - 1);
m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle, m_volumes.load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, depth, max_z, config.wipe_tower_rotation_angle,
m_use_VBOs && m_initialized, !print->is_step_done(psWipeTower), brim_spacing * 4.5f); !print->is_step_done(psWipeTower), brim_spacing * 4.5f);
} }
} }
} }
@ -5436,7 +5358,7 @@ void GLCanvas3D::_load_sla_shells()
const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) { const TriangleMesh &mesh, const float color[4], bool outside_printer_detection_enabled) {
m_volumes.volumes.emplace_back(new GLVolume(color)); m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& v = *m_volumes.volumes.back(); GLVolume& v = *m_volumes.volumes.back();
v.indexed_vertex_array.load_mesh(mesh, m_use_VBOs); v.indexed_vertex_array.load_mesh(mesh);
v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled; v.shader_outside_printer_detection_enabled = outside_printer_detection_enabled;
v.composite_id.volume_id = volume_id; v.composite_id.volume_id = volume_id;
v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0)); v.set_instance_offset(unscale(instance.shift(0), instance.shift(1), 0));
@ -5462,9 +5384,6 @@ void GLCanvas3D::_load_sla_shells()
double shift_z = obj->get_current_elevation(); double shift_z = obj->get_current_elevation();
for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) { for (unsigned int i = initial_volumes_count; i < m_volumes.volumes.size(); ++ i) {
GLVolume& v = *m_volumes.volumes[i]; GLVolume& v = *m_volumes.volumes[i];
// finalize volumes and sends geometry to gpu
v.bounding_box = v.indexed_vertex_array.bounding_box();
v.indexed_vertex_array.finalize_geometry(m_use_VBOs);
// apply shift z // apply shift z
v.set_sla_shift_z(shift_z); v.set_sla_shift_z(shift_z);
} }
@ -5555,7 +5474,7 @@ void GLCanvas3D::_update_toolpath_volumes_outside_state()
for (GLVolume* volume : m_volumes.volumes) for (GLVolume* volume : m_volumes.volumes)
{ {
volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box) : false; volume->is_outside = ((print_volume.radius() > 0.0) && volume->is_extrusion_path) ? !print_volume.contains(volume->bounding_box()) : false;
} }
} }
@ -5640,7 +5559,7 @@ bool GLCanvas3D::_is_any_volume_outside() const
void GLCanvas3D::_resize_toolbars() const void GLCanvas3D::_resize_toolbars() const
{ {
Size cnv_size = get_canvas_size(); Size cnv_size = get_canvas_size();
float zoom = get_camera_zoom(); float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
#if ENABLE_RETINA_GL #if ENABLE_RETINA_GL
@ -5690,8 +5609,6 @@ void GLCanvas3D::_resize_toolbars() const
} }
} }
if (m_view_toolbar != nullptr)
{
#if ENABLE_RETINA_GL #if ENABLE_RETINA_GL
m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); m_view_toolbar.set_icons_scale(m_retina_helper->get_scale_factor());
#else #else
@ -5703,7 +5620,6 @@ void GLCanvas3D::_resize_toolbars() const
float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; float left = -0.5f * (float)cnv_size.get_width() * inv_zoom;
m_view_toolbar.set_position(top, left); m_view_toolbar.set_position(top, left);
} }
}
#endif // !ENABLE_SVG_ICONS #endif // !ENABLE_SVG_ICONS
void GLCanvas3D::_update_selection_from_hover() void GLCanvas3D::_update_selection_from_hover()

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>
@ -452,7 +453,6 @@ private:
// Screen is only refreshed from the OnIdle handler if it is dirty. // Screen is only refreshed from the OnIdle handler if it is dirty.
bool m_dirty; bool m_dirty;
bool m_initialized; bool m_initialized;
bool m_use_VBOs;
bool m_apply_zoom_to_volumes_filter; bool m_apply_zoom_to_volumes_filter;
mutable std::vector<int> m_hover_volume_idxs; mutable std::vector<int> m_hover_volume_idxs;
bool m_warning_texture_enabled; bool m_warning_texture_enabled;
@ -494,7 +494,7 @@ public:
wxGLCanvas* get_wxglcanvas() { return m_canvas; } wxGLCanvas* get_wxglcanvas() { return m_canvas; }
const wxGLCanvas* get_wxglcanvas() const { return m_canvas; } const wxGLCanvas* get_wxglcanvas() const { return m_canvas; }
bool init(bool useVBOs); bool init();
void post_event(wxEvent &&event); void post_event(wxEvent &&event);
void set_as_dirty(); void set_as_dirty();
@ -608,6 +608,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

@ -192,7 +192,6 @@ GLCanvas3DManager::GLInfo GLCanvas3DManager::s_gl_info;
GLCanvas3DManager::GLCanvas3DManager() GLCanvas3DManager::GLCanvas3DManager()
: m_context(nullptr) : m_context(nullptr)
, m_gl_initialized(false) , m_gl_initialized(false)
, m_use_VBOs(false)
{ {
} }
@ -266,8 +265,6 @@ void GLCanvas3DManager::init_gl()
if (!m_gl_initialized) if (!m_gl_initialized)
{ {
glewInit(); glewInit();
const AppConfig* config = GUI::get_app_config();
m_use_VBOs = s_gl_info.is_version_greater_or_equal_to(2, 0);
m_gl_initialized = true; m_gl_initialized = true;
if (GLEW_EXT_texture_compression_s3tc) if (GLEW_EXT_texture_compression_s3tc)
s_compressed_textures_supported = true; s_compressed_textures_supported = true;
@ -323,7 +320,7 @@ bool GLCanvas3DManager::init(GLCanvas3D& canvas)
if (!m_gl_initialized) if (!m_gl_initialized)
init_gl(); init_gl();
return canvas.init(m_use_VBOs); return canvas.init();
} }
void GLCanvas3DManager::detect_multisample(int* attribList) void GLCanvas3DManager::detect_multisample(int* attribList)

View file

@ -75,7 +75,6 @@ private:
wxGLContext* m_context; wxGLContext* m_context;
static GLInfo s_gl_info; static GLInfo s_gl_info;
bool m_gl_initialized; bool m_gl_initialized;
bool m_use_VBOs;
static EMultisampleState s_multisample; static EMultisampleState s_multisample;
static bool s_compressed_textures_supported; static bool s_compressed_textures_supported;

View file

@ -188,7 +188,7 @@ bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const Bac
return true; return true;
std::string path = resources_dir() + "/icons/"; std::string path = resources_dir() + "/icons/";
bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false); bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false, true);
if (res) if (res)
m_icons_texture.metadata = icons_texture; m_icons_texture.metadata = icons_texture;
#endif // ENABLE_SVG_ICONS #endif // ENABLE_SVG_ICONS

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,8 +148,8 @@ 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);
} }
@ -356,6 +357,7 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons
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,17 +443,24 @@ 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;
m_objects_model->GetValue(variant, item, 1); m_objects_model->GetValue(variant, item, 1);
@ -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)
{ {
@ -728,9 +820,9 @@ 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);
@ -2327,6 +2736,7 @@ 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 & itLayer ? smLayer :
type & itVolume ? smVolume : smInstance; type & itVolume ? smVolume : smInstance;
} }
@ -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,13 +124,18 @@ 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 };
ObjectDataViewModel *m_objects_model{ nullptr };
DynamicPrintConfig *m_config {nullptr};
std::vector<ModelObject*> *m_objects{ nullptr };
std::vector<wxBitmap*> m_bmp_vector; 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()
// happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler // happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler
@ -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

@ -441,7 +441,7 @@ void ObjectManipulation::update_settings_value(const Selection& selection)
m_new_position = volume->get_volume_offset(); m_new_position = volume->get_volume_offset();
m_new_rotation = volume->get_volume_rotation() * (180. / M_PI); m_new_rotation = volume->get_volume_rotation() * (180. / M_PI);
m_new_scale = volume->get_volume_scaling_factor() * 100.; m_new_scale = volume->get_volume_scaling_factor() * 100.;
m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box.size()); m_new_size = volume->get_volume_transformation().get_scaling_factor().cwiseProduct(volume->bounding_box().size());
m_new_enabled = true; m_new_enabled = true;
} }
else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot)) else if (obj_list->multiple_selection() || obj_list->is_selected(itInstanceRoot))
@ -721,7 +721,7 @@ void ObjectManipulation::change_size_value(int axis, double value)
Vec3d ref_size = m_cache.size; Vec3d ref_size = m_cache.size;
if (selection.is_single_volume() || selection.is_single_modifier()) if (selection.is_single_volume() || selection.is_single_modifier())
ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box.size(); ref_size = selection.get_volume(*selection.get_volume_idxs().begin())->bounding_box().size();
else if (selection.is_single_full_instance()) else if (selection.is_single_full_instance())
ref_size = m_world_coordinates ? ref_size = m_world_coordinates ?
selection.get_unscaled_instance_bounding_box().size() : selection.get_unscaled_instance_bounding_box().size() :

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

@ -541,6 +541,26 @@ void Preview::on_checkbox_shells(wxCommandEvent& evt)
refresh_print(); refresh_print();
} }
void Preview::update_view_type()
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
const wxString& choice = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() &&
wxGetApp().extruders_edited_cnt()==1 ?
_(L("Color Print")) :
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
_(L("Tool")) :
_(L("Feature type"));
int type = m_choice_view_type->FindString(choice);
if (m_choice_view_type->GetSelection() != type) {
m_choice_view_type->SetSelection(type);
if (0 <= type && type < (int)GCodePreviewData::Extrusion::Num_View_Types)
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
m_preferred_color_mode = "feature";
}
}
void Preview::create_double_slider() void Preview::create_double_slider()
{ {
m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100); m_slider = new DoubleSlider(this, wxID_ANY, 0, 0, 0, 100);
@ -553,21 +573,11 @@ void Preview::create_double_slider()
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) { Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
auto& config = wxGetApp().preset_bundle->project_config; wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights")->values = m_slider->GetTicksValues();
((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
m_schedule_background_process(); m_schedule_background_process();
const wxString& choise = !config.option<ConfigOptionFloats>("colorprint_heights")->values.empty() ? _(L("Color Print")) : update_view_type();
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
_(L("Tool")) : _(L("Feature type"));
int type = m_choice_view_type->FindString(choise);
if (m_choice_view_type->GetSelection() != type) {
m_choice_view_type->SetSelection(type);
if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types))
m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type;
m_preferred_color_mode = "feature";
}
reload_print(); reload_print();
}); });
} }
@ -787,9 +797,14 @@ void Preview::load_print_as_fff(bool keep_z_range)
// Load the real G-code preview. // Load the real G-code preview.
m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); m_canvas->load_gcode_preview(*m_gcode_preview_data, colors);
m_loaded = true; m_loaded = true;
} else } else {
// disable color change information for multi-material presets
if (wxGetApp().extruders_edited_cnt() > 1)
color_print_values.clear();
// Load the initial preview based on slices, not the final G-code. // Load the initial preview based on slices, not the final G-code.
m_canvas->load_preview(colors, color_print_values); m_canvas->load_preview(colors, color_print_values);
}
show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple"); show_hide_ui_elements(gcode_preview_data_valid ? "full" : "simple");
// recalculates zs and update sliders accordingly // recalculates zs and update sliders accordingly
std::vector<double> zs = m_canvas->get_current_print_zs(true); std::vector<double> zs = m_canvas->get_current_print_zs(true);

View file

@ -127,6 +127,8 @@ public:
void move_double_slider(wxKeyEvent& evt); void move_double_slider(wxKeyEvent& evt);
void edit_double_slider(wxKeyEvent& evt); void edit_double_slider(wxKeyEvent& evt);
void update_view_type();
private: private:
bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model); bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model);

View file

@ -136,7 +136,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
for (unsigned int idx : idxs) for (unsigned int idx : idxs)
{ {
const GLVolume* vol = selection.get_volume(idx); const GLVolume* vol = selection.get_volume(idx);
m_box.merge(vol->bounding_box.transformed(vol->get_volume_transformation().get_matrix())); m_box.merge(vol->bounding_box().transformed(vol->get_volume_transformation().get_matrix()));
} }
// gets transform from first selected volume // gets transform from first selected volume
@ -151,7 +151,7 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
else if (single_volume) else if (single_volume)
{ {
const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin());
m_box = v->bounding_box; m_box = v->bounding_box();
m_transform = v->world_matrix(); m_transform = v->world_matrix();
angles = Geometry::extract_euler_angles(m_transform); angles = Geometry::extract_euler_angles(m_transform);
// consider rotation+mirror only components of the transform for offsets // consider rotation+mirror only components of the transform for offsets

View file

@ -49,7 +49,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent)
if (!m_icons_texture.metadata.filename.empty()) if (!m_icons_texture.metadata.filename.empty())
{ {
if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false)) if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false, true))
{ {
reset(); reset();
return false; return false;
@ -1072,7 +1072,7 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio
#if ENABLE_SVG_ICONS #if ENABLE_SVG_ICONS
it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
#else #else
it->second->render_input_window(2.0f * m_overlay_border + icon_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); it->second->render_input_window(2.0f * m_overlay_border + scaled_icons_size * zoom, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection);
#endif // ENABLE_SVG_ICONS #endif // ENABLE_SVG_ICONS
} }
#if ENABLE_SVG_ICONS #if ENABLE_SVG_ICONS

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()
@ -750,6 +755,11 @@ Sidebar::Sidebar(Plater *parent)
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);
p->sliced_info = new SlicedInfo(p->scrolled); p->sliced_info = new SlicedInfo(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;
@ -2177,9 +2193,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
@ -2949,8 +2962,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)
@ -3295,6 +3314,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;
@ -3947,6 +3970,9 @@ void Plater::reslice()
} }
else if (!p->background_process.empty() && !p->background_process.idle()) else if (!p->background_process.empty() && !p->background_process.idle())
p->show_action_buttons(true); p->show_action_buttons(true);
// update type of preview
p->preview->update_view_type();
} }
void Plater::reslice_SLA_supports(const ModelObject &object) void Plater::reslice_SLA_supports(const ModelObject &object)
@ -4250,6 +4276,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

@ -829,11 +829,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>
@ -99,14 +100,14 @@ void Selection::set_volumes(GLVolumePtrs* volumes)
update_valid(); update_valid();
} }
bool Selection::init(bool useVBOs) bool Selection::init()
{ {
if (!m_arrow.init(useVBOs)) if (!m_arrow.init())
return false; return false;
m_arrow.set_scale(5.0 * Vec3d::Ones()); m_arrow.set_scale(5.0 * Vec3d::Ones());
if (!m_curved_arrow.init(useVBOs)) if (!m_curved_arrow.init())
return false; return false;
m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); m_curved_arrow.set_scale(5.0 * Vec3d::Ones());
@ -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,18 +1074,24 @@ 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;
if (!boost::starts_with(sidebar_field, "layer"))
{
shader.start_using();
glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glEnable(GL_LIGHTING)); glsafe(::glEnable(GL_LIGHTING));
}
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
if (!boost::starts_with(sidebar_field, "layer"))
{
const Vec3d& center = get_bounding_box().center(); const Vec3d& center = get_bounding_box().center();
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates()) if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates())
@ -1127,6 +1137,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
glsafe(::glMultMatrixd(orient_matrix.data())); glsafe(::glMultMatrixd(orient_matrix.data()));
} }
} }
}
if (boost::starts_with(sidebar_field, "position")) if (boost::starts_with(sidebar_field, "position"))
render_sidebar_position_hints(sidebar_field); render_sidebar_position_hints(sidebar_field);
@ -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());
if (!boost::starts_with(sidebar_field, "layer"))
{
glsafe(::glDisable(GL_LIGHTING)); 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:
@ -212,7 +212,7 @@ public:
#endif // ENABLE_RENDER_SELECTION_CENTER #endif // ENABLE_RENDER_SELECTION_CENTER
void set_volumes(GLVolumePtrs* volumes); void set_volumes(GLVolumePtrs* volumes);
bool init(bool useVBOs); bool init();
bool is_enabled() const { return m_enabled; } bool is_enabled() const { return m_enabled; }
void set_enabled(bool enable) { m_enabled = enable; } void set_enabled(bool enable) { m_enabled = enable; }
@ -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--;
@ -3593,11 +3581,9 @@ void TabSLAPrint::reload_config()
void TabSLAPrint::update() void TabSLAPrint::update()
{ {
if (m_preset_bundle->printers.get_selected_preset().printer_technology() if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptFFF)
== ptFFF) return;
return; // #ys_FIXME
// #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())
@ -2027,6 +2225,9 @@ void DoubleSlider::draw_thumbs(wxDC& dc, const wxCoord& lower_pos, const wxCoord
void DoubleSlider::draw_ticks(wxDC& dc) void DoubleSlider::draw_ticks(wxDC& dc)
{ {
if (!m_is_enabled_tick_manipulation)
return;
dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN ); dc.SetPen(m_is_enabled_tick_manipulation ? DARK_GREY_PEN : LIGHT_GREY_PEN );
int height, width; int height, width;
get_size(&width, &height); get_size(&width, &height);
@ -2044,6 +2245,9 @@ void DoubleSlider::draw_ticks(wxDC& dc)
void DoubleSlider::draw_colored_band(wxDC& dc) void DoubleSlider::draw_colored_band(wxDC& dc)
{ {
if (!m_is_enabled_tick_manipulation)
return;
int height, width; int height, width;
get_size(&width, &height); get_size(&width, &height);
@ -2113,7 +2317,7 @@ void DoubleSlider::draw_one_layer_icon(wxDC& dc)
void DoubleSlider::draw_revert_icon(wxDC& dc) void DoubleSlider::draw_revert_icon(wxDC& dc)
{ {
if (m_ticks.empty()) if (m_ticks.empty() || !m_is_enabled_tick_manipulation)
return; return;
int width, height; int width, height;
@ -2218,7 +2422,7 @@ void DoubleSlider::OnLeftDown(wxMouseEvent& event)
m_selection == ssLower ? correct_lower_value() : correct_higher_value(); m_selection == ssLower ? correct_lower_value() : correct_higher_value();
if (!m_selection) m_selection = ssHigher; if (!m_selection) m_selection = ssHigher;
} }
else if (is_point_in_rect(pos, m_rect_revert_icon)) { else if (is_point_in_rect(pos, m_rect_revert_icon) && m_is_enabled_tick_manipulation) {
// discard all color changes // discard all color changes
SetLowerValue(m_min_value); SetLowerValue(m_min_value);
SetHigherValue(m_max_value); SetHigherValue(m_max_value);
@ -2647,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__ */
@ -164,7 +167,9 @@ enum ItemType {
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)
@ -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

@ -402,15 +402,8 @@ Updates PresetUpdater::priv::get_config_updates() const
} }
} }
copy_file_fix(idx.path(), bundle_path_idx);
const auto ver_current = idx.find(vp.config_version); const auto ver_current = idx.find(vp.config_version);
const bool ver_current_found = ver_current != idx.end(); const bool ver_current_found = ver_current != idx.end();
if (! ver_current_found) {
auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
BOOST_LOG_TRIVIAL(error) << message;
GUI::show_error(nullptr, GUI::from_u8(message));
}
BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%%3%, version cached: %4%") BOOST_LOG_TRIVIAL(debug) << boost::format("Vendor: %1%, version installed: %2%%3%, version cached: %4%")
% vp.name % vp.name
@ -418,6 +411,13 @@ Updates PresetUpdater::priv::get_config_updates() const
% (ver_current_found ? "" : " (not found in index!)") % (ver_current_found ? "" : " (not found in index!)")
% recommended->config_version.to_string(); % recommended->config_version.to_string();
if (! ver_current_found) {
auto message = (boost::format("Preset bundle `%1%` version not found in index: %2%") % idx.vendor() % vp.config_version.to_string()).str();
BOOST_LOG_TRIVIAL(error) << message;
GUI::show_error(nullptr, GUI::from_u8(message));
continue;
}
if (ver_current_found && !ver_current->is_current_slic3r_supported()) { if (ver_current_found && !ver_current->is_current_slic3r_supported()) {
BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string(); BOOST_LOG_TRIVIAL(warning) << "Current Slic3r incompatible with installed bundle: " << bundle_path.string();
updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name); updates.incompats.emplace_back(std::move(bundle_path), *ver_current, vp.name);
@ -459,12 +459,18 @@ Updates PresetUpdater::priv::get_config_updates() const
found = true; found = true;
} }
} }
if (! found)
if (found) {
// 'Install' the index in the vendor directory. This is used to memoize
// offered updates and to not offer the same update again if it was cancelled by the user.
copy_file_fix(idx.path(), bundle_path_idx);
} else {
BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources") BOOST_LOG_TRIVIAL(warning) << boost::format("Index for vendor %1% indicates update (%2%) but the new bundle was found neither in cache nor resources")
% idx.vendor() % idx.vendor()
% recommended->config_version.to_string(); % recommended->config_version.to_string();
} }
} }
}
return updates; return updates;
} }

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,24 +34,29 @@ 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%{