mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-21 21:58:03 -06:00
Merge branch 'master' into tm_builtin_pad
This commit is contained in:
commit
af89bcee53
65 changed files with 2903 additions and 1706 deletions
2
deps/CMakeLists.txt
vendored
2
deps/CMakeLists.txt
vendored
|
@ -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()
|
||||||
|
|
|
@ -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";
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
|
@ -146,60 +146,66 @@ sub mesh {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub model {
|
sub model {
|
||||||
my ($model_name, %params) = @_;
|
my ($model_names, %params) = @_;
|
||||||
|
$model_names = [ $model_names ] if ! ref($model_names);
|
||||||
|
|
||||||
my $input_file = "${model_name}.stl";
|
|
||||||
my $mesh = mesh($model_name, %params);
|
|
||||||
# $mesh->write_ascii("out/$input_file");
|
|
||||||
|
|
||||||
my $model = Slic3r::Model->new;
|
my $model = Slic3r::Model->new;
|
||||||
my $object = $model->add_object(input_file => $input_file);
|
|
||||||
$model->set_material($model_name);
|
for my $model_name (@$model_names) {
|
||||||
$object->add_volume(mesh => $mesh, material_id => $model_name);
|
my $input_file = "${model_name}.stl";
|
||||||
$object->add_instance(
|
my $mesh = mesh($model_name, %params);
|
||||||
offset => Slic3r::Pointf->new(0,0),
|
# $mesh->write_ascii("out/$input_file");
|
||||||
# 3D full transform
|
|
||||||
rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
|
my $object = $model->add_object(input_file => $input_file);
|
||||||
scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
|
$model->set_material($model_name);
|
||||||
# old transform
|
$object->add_volume(mesh => $mesh, material_id => $model_name);
|
||||||
# rotation => $params{rotation} // 0,
|
$object->add_instance(
|
||||||
# scaling_factor => $params{scale} // 1,
|
offset => Slic3r::Pointf->new(0,0),
|
||||||
);
|
# 3D full transform
|
||||||
|
rotation => Slic3r::Pointf3->new(0, 0, $params{rotation} // 0),
|
||||||
|
scaling_factor => Slic3r::Pointf3->new($params{scale} // 1, $params{scale} // 1, $params{scale} // 1),
|
||||||
|
# old transform
|
||||||
|
# rotation => $params{rotation} // 0,
|
||||||
|
# scaling_factor => $params{scale} // 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
return $model;
|
return $model;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub init_print {
|
sub init_print {
|
||||||
my ($models, %params) = @_;
|
my ($models, %params) = @_;
|
||||||
|
my $model;
|
||||||
|
if (ref($models) eq 'ARRAY') {
|
||||||
|
$model = model($models, %params);
|
||||||
|
} elsif (ref($models)) {
|
||||||
|
$model = $models;
|
||||||
|
} else {
|
||||||
|
$model = model([$models], %params);
|
||||||
|
}
|
||||||
|
|
||||||
my $config = Slic3r::Config->new;
|
my $config = Slic3r::Config->new;
|
||||||
$config->apply($params{config}) if $params{config};
|
$config->apply($params{config}) if $params{config};
|
||||||
$config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE};
|
$config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE};
|
||||||
|
|
||||||
my $print = Slic3r::Print->new;
|
my $print = Slic3r::Print->new;
|
||||||
$print->apply_config_perl_tests_only($config);
|
die "Unknown model in test" if !defined $model;
|
||||||
|
if (defined $params{duplicate} && $params{duplicate} > 1) {
|
||||||
$models = [$models] if ref($models) ne 'ARRAY';
|
$model->duplicate($params{duplicate} // 1, $config->min_object_distance);
|
||||||
$models = [ map { ref($_) ? $_ : model($_, %params) } @$models ];
|
|
||||||
for my $model (@$models) {
|
|
||||||
die "Unknown model in test" if !defined $model;
|
|
||||||
if (defined $params{duplicate} && $params{duplicate} > 1) {
|
|
||||||
$model->duplicate($params{duplicate} // 1, $print->config->min_object_distance);
|
|
||||||
}
|
|
||||||
$model->arrange_objects($print->config->min_object_distance);
|
|
||||||
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
|
|
||||||
foreach my $model_object (@{$model->objects}) {
|
|
||||||
$print->auto_assign_extruders($model_object);
|
|
||||||
$print->add_model_object($model_object);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
# Call apply_config_perl_tests_only one more time, so that the layer height profiles are updated over all PrintObjects.
|
$model->arrange_objects($config->min_object_distance);
|
||||||
$print->apply_config_perl_tests_only($config);
|
$model->center_instances_around_point($params{print_center} ? Slic3r::Pointf->new(@{$params{print_center}}) : Slic3r::Pointf->new(100,100));
|
||||||
|
foreach my $model_object (@{$model->objects}) {
|
||||||
|
$model_object->ensure_on_bed;
|
||||||
|
$print->auto_assign_extruders($model_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
$print->apply($model, $config);
|
||||||
$print->validate;
|
$print->validate;
|
||||||
|
|
||||||
# We return a proxy object in order to keep $models alive as required by the Print API.
|
# We return a proxy object in order to keep $models alive as required by the Print API.
|
||||||
return Slic3r::Test::Print->new(
|
return Slic3r::Test::Print->new(
|
||||||
print => $print,
|
print => $print,
|
||||||
models => $models,
|
model => $model,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +256,7 @@ sub add_facet {
|
||||||
package Slic3r::Test::Print;
|
package Slic3r::Test::Print;
|
||||||
use Moo;
|
use Moo;
|
||||||
|
|
||||||
has 'print' => (is => 'ro', required => 1, handles => [qw(process apply_config_perl_tests_only)]);
|
has 'print' => (is => 'ro', required => 1, handles => [qw(process apply)]);
|
||||||
has 'models' => (is => 'ro', required => 1);
|
has 'model' => (is => 'ro', required => 1);
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
BIN
resources/icons/row.png
Normal file
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
BIN
resources/icons/table.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 465 B |
|
@ -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.
|
||||||
|
|
|
@ -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 = "";
|
||||||
|
|
|
@ -106,6 +106,9 @@ struct AMFParserContext
|
||||||
// amf/material/metadata
|
// amf/material/metadata
|
||||||
NODE_TYPE_OBJECT, // amf/object
|
NODE_TYPE_OBJECT, // amf/object
|
||||||
// amf/object/metadata
|
// amf/object/metadata
|
||||||
|
NODE_TYPE_LAYER_CONFIG, // amf/object/layer_config_ranges
|
||||||
|
NODE_TYPE_RANGE, // amf/object/layer_config_ranges/range
|
||||||
|
// amf/object/layer_config_ranges/range/metadata
|
||||||
NODE_TYPE_MESH, // amf/object/mesh
|
NODE_TYPE_MESH, // amf/object/mesh
|
||||||
NODE_TYPE_VERTICES, // amf/object/mesh/vertices
|
NODE_TYPE_VERTICES, // amf/object/mesh/vertices
|
||||||
NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex
|
NODE_TYPE_VERTEX, // amf/object/mesh/vertices/vertex
|
||||||
|
@ -189,7 +192,7 @@ struct AMFParserContext
|
||||||
};
|
};
|
||||||
|
|
||||||
// Version of the amf file
|
// Version of the amf file
|
||||||
unsigned int m_version;
|
unsigned int m_version;
|
||||||
// Current Expat XML parser instance.
|
// Current Expat XML parser instance.
|
||||||
XML_Parser m_parser;
|
XML_Parser m_parser;
|
||||||
// Model to receive objects extracted from an AMF file.
|
// Model to receive objects extracted from an AMF file.
|
||||||
|
@ -260,7 +263,9 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
||||||
m_value[0] = get_attribute(atts, "type");
|
m_value[0] = get_attribute(atts, "type");
|
||||||
node_type_new = NODE_TYPE_METADATA;
|
node_type_new = NODE_TYPE_METADATA;
|
||||||
}
|
}
|
||||||
} else if (strcmp(name, "mesh") == 0) {
|
} else if (strcmp(name, "layer_config_ranges") == 0 && m_path[1] == NODE_TYPE_OBJECT)
|
||||||
|
node_type_new = NODE_TYPE_LAYER_CONFIG;
|
||||||
|
else if (strcmp(name, "mesh") == 0) {
|
||||||
if (m_path[1] == NODE_TYPE_OBJECT)
|
if (m_path[1] == NODE_TYPE_OBJECT)
|
||||||
node_type_new = NODE_TYPE_MESH;
|
node_type_new = NODE_TYPE_MESH;
|
||||||
} else if (strcmp(name, "instance") == 0) {
|
} else if (strcmp(name, "instance") == 0) {
|
||||||
|
@ -317,6 +322,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
||||||
else if (strcmp(name, "mirrorz") == 0)
|
else if (strcmp(name, "mirrorz") == 0)
|
||||||
node_type_new = NODE_TYPE_MIRRORZ;
|
node_type_new = NODE_TYPE_MIRRORZ;
|
||||||
}
|
}
|
||||||
|
else if (m_path[2] == NODE_TYPE_LAYER_CONFIG && strcmp(name, "range") == 0) {
|
||||||
|
assert(m_object);
|
||||||
|
node_type_new = NODE_TYPE_RANGE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if (m_path[3] == NODE_TYPE_VERTICES) {
|
if (m_path[3] == NODE_TYPE_VERTICES) {
|
||||||
|
@ -334,6 +343,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
||||||
} else if (strcmp(name, "triangle") == 0)
|
} else if (strcmp(name, "triangle") == 0)
|
||||||
node_type_new = NODE_TYPE_TRIANGLE;
|
node_type_new = NODE_TYPE_TRIANGLE;
|
||||||
}
|
}
|
||||||
|
else if (m_path[3] == NODE_TYPE_RANGE && strcmp(name, "metadata") == 0) {
|
||||||
|
m_value[0] = get_attribute(atts, "type");
|
||||||
|
node_type_new = NODE_TYPE_METADATA;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (strcmp(name, "coordinates") == 0) {
|
if (strcmp(name, "coordinates") == 0) {
|
||||||
|
@ -571,8 +584,13 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||||
config = &m_material->config;
|
config = &m_material->config;
|
||||||
else if (m_path[1] == NODE_TYPE_OBJECT && m_object)
|
else if (m_path[1] == NODE_TYPE_OBJECT && m_object)
|
||||||
config = &m_object->config;
|
config = &m_object->config;
|
||||||
} else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
|
}
|
||||||
|
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume)
|
||||||
config = &m_volume->config;
|
config = &m_volume->config;
|
||||||
|
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_RANGE && m_object && !m_object->layer_config_ranges.empty()) {
|
||||||
|
auto it = --m_object->layer_config_ranges.end();
|
||||||
|
config = &it->second;
|
||||||
|
}
|
||||||
if (config)
|
if (config)
|
||||||
config->set_deserialize(opt_key, m_value[1]);
|
config->set_deserialize(opt_key, m_value[1]);
|
||||||
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
|
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
|
||||||
|
@ -598,7 +616,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||||
if (end != nullptr)
|
if (end != nullptr)
|
||||||
*end = 0;
|
*end = 0;
|
||||||
|
|
||||||
point(coord_idx) = atof(p);
|
point(coord_idx) = float(atof(p));
|
||||||
if (++coord_idx == 5) {
|
if (++coord_idx == 5) {
|
||||||
m_object->sla_support_points.push_back(sla::SupportPoint(point));
|
m_object->sla_support_points.push_back(sla::SupportPoint(point));
|
||||||
coord_idx = 0;
|
coord_idx = 0;
|
||||||
|
@ -609,6 +627,16 @@ void AMFParserContext::endElement(const char * /* name */)
|
||||||
}
|
}
|
||||||
m_object->sla_points_status = sla::PointsStatus::UserModified;
|
m_object->sla_points_status = sla::PointsStatus::UserModified;
|
||||||
}
|
}
|
||||||
|
else if (m_path.size() == 5 && m_path[1] == NODE_TYPE_OBJECT && m_path[3] == NODE_TYPE_RANGE &&
|
||||||
|
m_object && strcmp(opt_key, "layer_height_range") == 0) {
|
||||||
|
// Parse object's layer_height_range, a semicolon separated doubles.
|
||||||
|
char* p = const_cast<char*>(m_value[1].c_str());
|
||||||
|
char* end = strchr(p, ';');
|
||||||
|
*end = 0;
|
||||||
|
|
||||||
|
const t_layer_height_range range = {double(atof(p)), double(atof(end + 1))};
|
||||||
|
m_object->layer_config_ranges[range];
|
||||||
|
}
|
||||||
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
|
else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
|
||||||
if (strcmp(opt_key, "modifier") == 0) {
|
if (strcmp(opt_key, "modifier") == 0) {
|
||||||
// Is this volume a modifier volume?
|
// Is this volume a modifier volume?
|
||||||
|
@ -907,6 +935,31 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
||||||
}
|
}
|
||||||
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
|
//FIXME Store the layer height ranges (ModelObject::layer_height_ranges)
|
||||||
|
|
||||||
|
|
||||||
|
// #ys_FIXME_experiment : Try to export layer config range
|
||||||
|
const t_layer_config_ranges& config_ranges = object->layer_config_ranges;
|
||||||
|
if (!config_ranges.empty())
|
||||||
|
{
|
||||||
|
// Store the layer config range as a single semicolon separated list.
|
||||||
|
stream << " <layer_config_ranges>\n";
|
||||||
|
size_t layer_counter = 0;
|
||||||
|
for (auto range : config_ranges) {
|
||||||
|
stream << " <range id=\"" << layer_counter << "\">\n";
|
||||||
|
|
||||||
|
stream << " <metadata type=\"slic3r.layer_height_range\">";
|
||||||
|
stream << range.first.first << ";" << range.first.second << "</metadata>\n";
|
||||||
|
|
||||||
|
for (const std::string& key : range.second.keys())
|
||||||
|
stream << " <metadata type=\"slic3r." << key << "\">" << range.second.serialize(key) << "</metadata>\n";
|
||||||
|
|
||||||
|
stream << " </range>\n";
|
||||||
|
layer_counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream << " </layer_config_ranges>\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
|
const std::vector<sla::SupportPoint>& sla_support_points = object->sla_support_points;
|
||||||
if (!sla_support_points.empty()) {
|
if (!sla_support_points.empty()) {
|
||||||
// Store the SLA supports as a single semicolon separated list.
|
// Store the SLA supports as a single semicolon separated list.
|
||||||
|
@ -941,7 +994,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
||||||
stream << " </coordinates>\n";
|
stream << " </coordinates>\n";
|
||||||
stream << " </vertex>\n";
|
stream << " </vertex>\n";
|
||||||
}
|
}
|
||||||
num_vertices += its.vertices.size();
|
num_vertices += (int)its.vertices.size();
|
||||||
}
|
}
|
||||||
stream << " </vertices>\n";
|
stream << " </vertices>\n";
|
||||||
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
|
for (size_t i_volume = 0; i_volume < object->volumes.size(); ++i_volume) {
|
||||||
|
|
|
@ -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";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -629,7 +629,7 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
||||||
this->config = rhs.config;
|
this->config = rhs.config;
|
||||||
this->sla_support_points = rhs.sla_support_points;
|
this->sla_support_points = rhs.sla_support_points;
|
||||||
this->sla_points_status = rhs.sla_points_status;
|
this->sla_points_status = rhs.sla_points_status;
|
||||||
this->layer_height_ranges = rhs.layer_height_ranges;
|
this->layer_config_ranges = rhs.layer_config_ranges; // #ys_FIXME_experiment
|
||||||
this->layer_height_profile = rhs.layer_height_profile;
|
this->layer_height_profile = rhs.layer_height_profile;
|
||||||
this->origin_translation = rhs.origin_translation;
|
this->origin_translation = rhs.origin_translation;
|
||||||
m_bounding_box = rhs.m_bounding_box;
|
m_bounding_box = rhs.m_bounding_box;
|
||||||
|
@ -665,7 +665,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
||||||
this->config = std::move(rhs.config);
|
this->config = std::move(rhs.config);
|
||||||
this->sla_support_points = std::move(rhs.sla_support_points);
|
this->sla_support_points = std::move(rhs.sla_support_points);
|
||||||
this->sla_points_status = std::move(rhs.sla_points_status);
|
this->sla_points_status = std::move(rhs.sla_points_status);
|
||||||
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
|
this->layer_config_ranges = std::move(rhs.layer_config_ranges); // #ys_FIXME_experiment
|
||||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||||
this->origin_translation = std::move(rhs.origin_translation);
|
this->origin_translation = std::move(rhs.origin_translation);
|
||||||
m_bounding_box = std::move(rhs.m_bounding_box);
|
m_bounding_box = std::move(rhs.m_bounding_box);
|
||||||
|
@ -1845,7 +1845,7 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO
|
||||||
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
|
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
++i_old;
|
++ i_old;
|
||||||
++ i_new;
|
++ i_new;
|
||||||
}
|
}
|
||||||
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
|
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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 ®ion = *m_regions[region_id];
|
|
||||||
PrintRegionConfig this_region_config;
|
|
||||||
bool this_region_config_set = false;
|
|
||||||
for (PrintObject *object : m_objects) {
|
|
||||||
if (region_id < object->region_volumes.size()) {
|
|
||||||
for (int volume_id : object->region_volumes[region_id]) {
|
|
||||||
const ModelVolume &volume = *object->model_object()->volumes[volume_id];
|
|
||||||
if (this_region_config_set) {
|
|
||||||
// If the new config for this volume differs from the other
|
|
||||||
// volume configs currently associated to this region, it means
|
|
||||||
// the region subdivision does not make sense anymore.
|
|
||||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
|
|
||||||
rearrange_regions = true;
|
|
||||||
goto exit_for_rearrange_regions;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
|
|
||||||
this_region_config_set = true;
|
|
||||||
}
|
|
||||||
for (const PrintRegionConfig &cfg : other_region_configs) {
|
|
||||||
// If the new config for this volume equals any of the other
|
|
||||||
// volume configs that are not currently associated to this
|
|
||||||
// region, it means the region subdivision does not make
|
|
||||||
// sense anymore.
|
|
||||||
if (cfg.equals(this_region_config)) {
|
|
||||||
rearrange_regions = true;
|
|
||||||
goto exit_for_rearrange_regions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this_region_config_set) {
|
|
||||||
t_config_option_keys diff = region.config().diff(this_region_config);
|
|
||||||
if (! diff.empty()) {
|
|
||||||
region.config_apply_only(this_region_config, diff, false);
|
|
||||||
for (PrintObject *object : m_objects)
|
|
||||||
if (region_id < object->region_volumes.size() && ! object->region_volumes[region_id].empty())
|
|
||||||
invalidated |= object->invalidate_state_by_config_options(diff);
|
|
||||||
}
|
|
||||||
other_region_configs.emplace_back(std::move(this_region_config));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_for_rearrange_regions:
|
|
||||||
|
|
||||||
if (rearrange_regions) {
|
|
||||||
// The current subdivision of regions does not make sense anymore.
|
|
||||||
// We need to remove all objects and re-add them.
|
|
||||||
ModelObjectPtrs model_objects;
|
|
||||||
model_objects.reserve(m_objects.size());
|
|
||||||
for (PrintObject *object : m_objects)
|
|
||||||
model_objects.push_back(object->model_object());
|
|
||||||
this->clear();
|
|
||||||
for (ModelObject *mo : model_objects)
|
|
||||||
this->add_model_object(mo);
|
|
||||||
invalidated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (PrintObject *object : m_objects)
|
|
||||||
object->update_slicing_parameters();
|
|
||||||
|
|
||||||
return invalidated;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
// Add or remove support modifier ModelVolumes from model_object_dst to match the ModelVolumes of model_object_new
|
||||||
// in the exact order and with the same IDs.
|
// in the exact order and with the same IDs.
|
||||||
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
// It is expected, that the model_object_dst already contains the non-support volumes of model_object_new in the correct order.
|
||||||
|
@ -620,6 +400,20 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_dst, const t_layer_config_ranges &lr_src)
|
||||||
|
{
|
||||||
|
assert(lr_dst.size() == lr_src.size());
|
||||||
|
auto it_src = lr_src.cbegin();
|
||||||
|
for (auto &kvp_dst : lr_dst) {
|
||||||
|
const auto &kvp_src = *it_src ++;
|
||||||
|
assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON);
|
||||||
|
assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON);
|
||||||
|
// Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile.
|
||||||
|
// assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON);
|
||||||
|
kvp_dst.second = kvp_src.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
|
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
|
||||||
{
|
{
|
||||||
typedef Transform3d::Scalar T;
|
typedef Transform3d::Scalar T;
|
||||||
|
@ -674,6 +468,23 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
|
||||||
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
|
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare just the layer ranges and their layer heights, not the associated configs.
|
||||||
|
// Ignore the layer heights if check_layer_heights is false.
|
||||||
|
bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_layer_config_ranges &lr2, bool check_layer_height)
|
||||||
|
{
|
||||||
|
if (lr1.size() != lr2.size())
|
||||||
|
return false;
|
||||||
|
auto it2 = lr2.begin();
|
||||||
|
for (const auto &kvp1 : lr1) {
|
||||||
|
const auto &kvp2 = *it2 ++;
|
||||||
|
if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON ||
|
||||||
|
std::abs(kvp1.first.second - kvp2.first.second) > EPSILON ||
|
||||||
|
(check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
|
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
|
||||||
{
|
{
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
|
@ -724,6 +535,50 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
// Handle changes to regions config defaults
|
// Handle changes to regions config defaults
|
||||||
m_default_region_config.apply_only(config, region_diff, true);
|
m_default_region_config.apply_only(config, region_diff, true);
|
||||||
|
|
||||||
|
class LayerRanges
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LayerRanges() {}
|
||||||
|
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs.
|
||||||
|
void assign(const t_layer_config_ranges &in) {
|
||||||
|
m_ranges.clear();
|
||||||
|
m_ranges.reserve(in.size());
|
||||||
|
// Input ranges are sorted lexicographically. First range trims the other ranges.
|
||||||
|
coordf_t last_z = 0;
|
||||||
|
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in) {
|
||||||
|
// for (auto &range : in) {
|
||||||
|
if (range.first.second > last_z) {
|
||||||
|
coordf_t min_z = std::max(range.first.first, 0.);
|
||||||
|
if (min_z > last_z + EPSILON) {
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
|
||||||
|
last_z = min_z;
|
||||||
|
}
|
||||||
|
if (range.first.second > last_z + EPSILON) {
|
||||||
|
const DynamicPrintConfig* cfg = &range.second;
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
|
||||||
|
last_z = range.first.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (m_ranges.empty())
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
|
||||||
|
else if (m_ranges.back().second == nullptr)
|
||||||
|
m_ranges.back().first.second = DBL_MAX;
|
||||||
|
else
|
||||||
|
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
|
||||||
|
}
|
||||||
|
const DynamicPrintConfig* config(const t_layer_height_range &range) const {
|
||||||
|
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
|
||||||
|
assert(it != m_ranges.end());
|
||||||
|
assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON);
|
||||||
|
assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON);
|
||||||
|
return (it == m_ranges.end()) ? nullptr : it->second;
|
||||||
|
}
|
||||||
|
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator begin() const { return m_ranges.cbegin(); }
|
||||||
|
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>>::const_iterator end() const { return m_ranges.cend(); }
|
||||||
|
private:
|
||||||
|
std::vector<std::pair<t_layer_height_range, const DynamicPrintConfig*>> m_ranges;
|
||||||
|
};
|
||||||
struct ModelObjectStatus {
|
struct ModelObjectStatus {
|
||||||
enum Status {
|
enum Status {
|
||||||
Unknown,
|
Unknown,
|
||||||
|
@ -733,8 +588,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
Deleted,
|
Deleted,
|
||||||
};
|
};
|
||||||
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
|
ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {}
|
||||||
ModelID id;
|
ModelID id;
|
||||||
Status status;
|
Status status;
|
||||||
|
LayerRanges layer_ranges;
|
||||||
// Search by id.
|
// Search by id.
|
||||||
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; }
|
||||||
};
|
};
|
||||||
|
@ -861,21 +717,23 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||||
assert(it_status != model_object_status.end());
|
assert(it_status != model_object_status.end());
|
||||||
assert(it_status->status != ModelObjectStatus::Deleted);
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
const ModelObject& model_object_new = *model.objects[idx_model_object];
|
||||||
|
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
|
||||||
if (it_status->status == ModelObjectStatus::New)
|
if (it_status->status == ModelObjectStatus::New)
|
||||||
// PrintObject instances will be added in the next loop.
|
// PrintObject instances will be added in the next loop.
|
||||||
continue;
|
continue;
|
||||||
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
// Update the ModelObject instance, possibly invalidate the linked PrintObjects.
|
||||||
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved);
|
||||||
const ModelObject &model_object_new = *model.objects[idx_model_object];
|
|
||||||
// Check whether a model part volume was added or removed, their transformations or order changed.
|
// Check whether a model part volume was added or removed, their transformations or order changed.
|
||||||
|
// Only volume IDs, volume types and their order are checked, configuration and other parameters are NOT checked.
|
||||||
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART);
|
||||||
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
|
bool modifiers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::PARAMETER_MODIFIER);
|
||||||
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
|
bool support_blockers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER);
|
||||||
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
bool support_enforcers_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER);
|
||||||
if (model_parts_differ || modifiers_differ ||
|
if (model_parts_differ || modifiers_differ ||
|
||||||
model_object.origin_translation != model_object_new.origin_translation ||
|
model_object.origin_translation != model_object_new.origin_translation ||
|
||||||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
|
model_object.layer_height_profile != model_object_new.layer_height_profile ||
|
||||||
model_object.layer_height_profile != model_object_new.layer_height_profile) {
|
! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty())) {
|
||||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||||
for (auto it = range.first; it != range.second; ++ it) {
|
for (auto it = range.first; it != range.second; ++ it) {
|
||||||
|
@ -915,7 +773,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
//FIXME What to do with m_material_id?
|
//FIXME What to do with m_material_id?
|
||||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
|
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::MODEL_PART);
|
||||||
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
|
model_volume_list_copy_configs(model_object /* dst */, model_object_new /* src */, ModelVolumeType::PARAMETER_MODIFIER);
|
||||||
// Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step.
|
layer_height_ranges_copy_configs(model_object.layer_config_ranges /* dst */, model_object_new.layer_config_ranges /* src */);
|
||||||
|
// Copy the ModelObject name, input_file and instances. The instances will be compared against PrintObject instances in the next step.
|
||||||
model_object.name = model_object_new.name;
|
model_object.name = model_object_new.name;
|
||||||
model_object.input_file = model_object_new.input_file;
|
model_object.input_file = model_object_new.input_file;
|
||||||
model_object.clear_instances();
|
model_object.clear_instances();
|
||||||
|
@ -1027,19 +886,27 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
PrintRegionConfig this_region_config;
|
PrintRegionConfig this_region_config;
|
||||||
bool this_region_config_set = false;
|
bool this_region_config_set = false;
|
||||||
for (PrintObject *print_object : m_objects) {
|
for (PrintObject *print_object : m_objects) {
|
||||||
|
const LayerRanges *layer_ranges;
|
||||||
|
{
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
layer_ranges = &it_status->layer_ranges;
|
||||||
|
}
|
||||||
if (region_id < print_object->region_volumes.size()) {
|
if (region_id < print_object->region_volumes.size()) {
|
||||||
for (int volume_id : print_object->region_volumes[region_id]) {
|
for (const std::pair<t_layer_height_range, int> &volume_and_range : print_object->region_volumes[region_id]) {
|
||||||
const ModelVolume &volume = *print_object->model_object()->volumes[volume_id];
|
const ModelVolume &volume = *print_object->model_object()->volumes[volume_and_range.second];
|
||||||
|
const DynamicPrintConfig *layer_range_config = layer_ranges->config(volume_and_range.first);
|
||||||
if (this_region_config_set) {
|
if (this_region_config_set) {
|
||||||
// If the new config for this volume differs from the other
|
// If the new config for this volume differs from the other
|
||||||
// volume configs currently associated to this region, it means
|
// volume configs currently associated to this region, it means
|
||||||
// the region subdivision does not make sense anymore.
|
// the region subdivision does not make sense anymore.
|
||||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
|
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders)))
|
||||||
// Regions were split. Reset this print_object.
|
// Regions were split. Reset this print_object.
|
||||||
goto print_object_end;
|
goto print_object_end;
|
||||||
} else {
|
} else {
|
||||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, layer_range_config, volume, num_extruders);
|
||||||
for (size_t i = 0; i < region_id; ++i) {
|
for (size_t i = 0; i < region_id; ++ i) {
|
||||||
const PrintRegion ®ion_other = *m_regions[i];
|
const PrintRegion ®ion_other = *m_regions[i];
|
||||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
||||||
// Regions were merged. Reset this print_object.
|
// Regions were merged. Reset this print_object.
|
||||||
|
@ -1054,7 +921,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
update_apply_status(print_object->invalidate_all_steps());
|
update_apply_status(print_object->invalidate_all_steps());
|
||||||
// Decrease the references to regions from this volume.
|
// Decrease the references to regions from this volume.
|
||||||
int ireg = 0;
|
int ireg = 0;
|
||||||
for (const std::vector<int> &volumes : print_object->region_volumes) {
|
for (const std::vector<std::pair<t_layer_height_range, int>> &volumes : print_object->region_volumes) {
|
||||||
if (! volumes.empty())
|
if (! volumes.empty())
|
||||||
-- m_regions[ireg]->m_refcnt;
|
-- m_regions[ireg]->m_refcnt;
|
||||||
++ ireg;
|
++ ireg;
|
||||||
|
@ -1076,52 +943,65 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
||||||
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
|
for (size_t idx_print_object = 0; idx_print_object < m_objects.size(); ++ idx_print_object) {
|
||||||
PrintObject &print_object0 = *m_objects[idx_print_object];
|
PrintObject &print_object0 = *m_objects[idx_print_object];
|
||||||
const ModelObject &model_object = *print_object0.model_object();
|
const ModelObject &model_object = *print_object0.model_object();
|
||||||
std::vector<int> map_volume_to_region(model_object.volumes.size(), -1);
|
const LayerRanges *layer_ranges;
|
||||||
|
{
|
||||||
|
auto it_status = model_object_status.find(ModelObjectStatus(model_object.id()));
|
||||||
|
assert(it_status != model_object_status.end());
|
||||||
|
assert(it_status->status != ModelObjectStatus::Deleted);
|
||||||
|
layer_ranges = &it_status->layer_ranges;
|
||||||
|
}
|
||||||
|
std::vector<int> regions_in_object;
|
||||||
|
regions_in_object.reserve(64);
|
||||||
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
|
for (size_t i = idx_print_object; i < m_objects.size() && m_objects[i]->model_object() == &model_object; ++ i) {
|
||||||
PrintObject &print_object = *m_objects[i];
|
PrintObject &print_object = *m_objects[i];
|
||||||
bool fresh = print_object.region_volumes.empty();
|
bool fresh = print_object.region_volumes.empty();
|
||||||
unsigned int volume_id = 0;
|
unsigned int volume_id = 0;
|
||||||
|
unsigned int idx_region_in_object = 0;
|
||||||
for (const ModelVolume *volume : model_object.volumes) {
|
for (const ModelVolume *volume : model_object.volumes) {
|
||||||
if (! volume->is_model_part() && ! volume->is_modifier()) {
|
if (! volume->is_model_part() && ! volume->is_modifier()) {
|
||||||
++ volume_id;
|
++ volume_id;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int region_id = -1;
|
// Filter the layer ranges, so they do not overlap and they contain at least a single layer.
|
||||||
if (&print_object == &print_object0) {
|
// Now insert a volume with a layer range to its own region.
|
||||||
// Get the config applied to this volume.
|
for (auto it_range = layer_ranges->begin(); it_range != layer_ranges->end(); ++ it_range) {
|
||||||
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
|
int region_id = -1;
|
||||||
// Find an existing print region with the same config.
|
if (&print_object == &print_object0) {
|
||||||
int idx_empty_slot = -1;
|
// Get the config applied to this volume.
|
||||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, it_range->second, *volume, num_extruders);
|
||||||
if (m_regions[i]->m_refcnt == 0) {
|
// Find an existing print region with the same config.
|
||||||
if (idx_empty_slot == -1)
|
int idx_empty_slot = -1;
|
||||||
idx_empty_slot = i;
|
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
||||||
} else if (config.equals(m_regions[i]->config())) {
|
if (m_regions[i]->m_refcnt == 0) {
|
||||||
region_id = i;
|
if (idx_empty_slot == -1)
|
||||||
break;
|
idx_empty_slot = i;
|
||||||
|
} else if (config.equals(m_regions[i]->config())) {
|
||||||
|
region_id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If no region exists with the same config, create a new one.
|
||||||
|
if (region_id == -1) {
|
||||||
|
if (idx_empty_slot == -1) {
|
||||||
|
region_id = (int)m_regions.size();
|
||||||
|
this->add_region(config);
|
||||||
|
} else {
|
||||||
|
region_id = idx_empty_slot;
|
||||||
|
m_regions[region_id]->set_config(std::move(config));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
regions_in_object.emplace_back(region_id);
|
||||||
// If no region exists with the same config, create a new one.
|
} else
|
||||||
if (region_id == -1) {
|
region_id = regions_in_object[idx_region_in_object ++];
|
||||||
if (idx_empty_slot == -1) {
|
// Assign volume to a region.
|
||||||
region_id = (int)m_regions.size();
|
if (fresh) {
|
||||||
this->add_region(config);
|
if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
|
||||||
} else {
|
++ m_regions[region_id]->m_refcnt;
|
||||||
region_id = idx_empty_slot;
|
print_object.add_region_volume(region_id, volume_id, it_range->first);
|
||||||
m_regions[region_id]->set_config(std::move(config));
|
}
|
||||||
}
|
}
|
||||||
}
|
++ volume_id;
|
||||||
map_volume_to_region[volume_id] = region_id;
|
}
|
||||||
} else
|
|
||||||
region_id = map_volume_to_region[volume_id];
|
|
||||||
// Assign volume to a region.
|
|
||||||
if (fresh) {
|
|
||||||
if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty())
|
|
||||||
++ m_regions[region_id]->m_refcnt;
|
|
||||||
print_object.add_region_volume(region_id, volume_id);
|
|
||||||
}
|
|
||||||
++ volume_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1175,9 +1055,9 @@ std::string Print::validate() const
|
||||||
Polygon convex_hull0 = offset(
|
Polygon convex_hull0 = offset(
|
||||||
print_object->model_object()->convex_hull_2d(
|
print_object->model_object()->convex_hull_2d(
|
||||||
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
Geometry::assemble_transform(Vec3d::Zero(), rotation, model_instance0->get_scaling_factor(), model_instance0->get_mirror())),
|
||||||
scale_(m_config.extruder_clearance_radius.value) / 2., jtRound, scale_(0.1)).front();
|
float(scale_(0.5 * m_config.extruder_clearance_radius.value)), jtRound, float(scale_(0.1))).front();
|
||||||
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
|
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
|
||||||
for (const Point © : print_object->m_copies) {
|
for (const Point © : print_object->copies()) {
|
||||||
Polygon convex_hull = convex_hull0;
|
Polygon convex_hull = convex_hull0;
|
||||||
convex_hull.translate(copy);
|
convex_hull.translate(copy);
|
||||||
if (! intersection(convex_hulls_other, convex_hull).empty())
|
if (! intersection(convex_hulls_other, convex_hull).empty())
|
||||||
|
@ -1227,7 +1107,7 @@ std::string Print::validate() const
|
||||||
bool has_custom_layering = false;
|
bool has_custom_layering = false;
|
||||||
std::vector<std::vector<coordf_t>> layer_height_profiles;
|
std::vector<std::vector<coordf_t>> layer_height_profiles;
|
||||||
for (const PrintObject *object : m_objects) {
|
for (const PrintObject *object : m_objects) {
|
||||||
has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
|
has_custom_layering = ! object->model_object()->layer_config_ranges.empty() || ! object->model_object()->layer_height_profile.empty(); // #ys_FIXME_experiment
|
||||||
if (has_custom_layering) {
|
if (has_custom_layering) {
|
||||||
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
|
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
|
||||||
break;
|
break;
|
||||||
|
@ -1435,9 +1315,9 @@ Flow Print::brim_flow() const
|
||||||
generation as well. */
|
generation as well. */
|
||||||
return Flow::new_from_config_width(
|
return Flow::new_from_config_width(
|
||||||
frPerimeter,
|
frPerimeter,
|
||||||
width,
|
width,
|
||||||
m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
|
(float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
|
||||||
this->skirt_first_layer_height(),
|
(float)this->skirt_first_layer_height(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1457,9 +1337,9 @@ Flow Print::skirt_flow() const
|
||||||
generation as well. */
|
generation as well. */
|
||||||
return Flow::new_from_config_width(
|
return Flow::new_from_config_width(
|
||||||
frPerimeter,
|
frPerimeter,
|
||||||
width,
|
width,
|
||||||
m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
|
(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
|
||||||
this->skirt_first_layer_height(),
|
(float)this->skirt_first_layer_height(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1634,20 +1514,20 @@ void Print::_make_skirt()
|
||||||
|
|
||||||
// Initial offset of the brim inner edge from the object (possible with a support & raft).
|
// Initial offset of the brim inner edge from the object (possible with a support & raft).
|
||||||
// The skirt will touch the brim if the brim is extruded.
|
// The skirt will touch the brim if the brim is extruded.
|
||||||
Flow brim_flow = this->brim_flow();
|
Flow brim_flow = this->brim_flow();
|
||||||
double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
|
double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
|
||||||
coord_t distance = scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.);
|
auto distance = float(scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.));
|
||||||
// Draw outlines from outside to inside.
|
// Draw outlines from outside to inside.
|
||||||
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
||||||
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
||||||
for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
|
for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) {
|
||||||
this->throw_if_canceled();
|
this->throw_if_canceled();
|
||||||
// Offset the skirt outside.
|
// Offset the skirt outside.
|
||||||
distance += coord_t(scale_(spacing));
|
distance += float(scale_(spacing));
|
||||||
// Generate the skirt centerline.
|
// Generate the skirt centerline.
|
||||||
Polygon loop;
|
Polygon loop;
|
||||||
{
|
{
|
||||||
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1));
|
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||||
if (loops.empty())
|
if (loops.empty())
|
||||||
break;
|
break;
|
||||||
|
@ -1658,9 +1538,9 @@ void Print::_make_skirt()
|
||||||
eloop.paths.emplace_back(ExtrusionPath(
|
eloop.paths.emplace_back(ExtrusionPath(
|
||||||
ExtrusionPath(
|
ExtrusionPath(
|
||||||
erSkirt,
|
erSkirt,
|
||||||
mm3_per_mm, // this will be overridden at G-code export time
|
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||||
flow.width,
|
flow.width,
|
||||||
first_layer_height // this will be overridden at G-code export time
|
(float)first_layer_height // this will be overridden at G-code export time
|
||||||
)));
|
)));
|
||||||
eloop.paths.back().polyline = loop.split_at_first_point();
|
eloop.paths.back().polyline = loop.split_at_first_point();
|
||||||
m_skirt.append(eloop);
|
m_skirt.append(eloop);
|
||||||
|
@ -1786,7 +1666,7 @@ void Print::_make_wipe_tower()
|
||||||
// Insert the new support layer.
|
// Insert the new support layer.
|
||||||
double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z;
|
double height = lt.print_z - m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z;
|
||||||
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
|
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
|
||||||
it_layer = m_objects.front()->insert_support_layer(it_layer, size_t(-1), height, lt.print_z, lt.print_z - 0.5 * height);
|
it_layer = m_objects.front()->insert_support_layer(it_layer, -1, height, lt.print_z, lt.print_z - 0.5 * height);
|
||||||
++ it_layer;
|
++ it_layer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1813,19 +1693,19 @@ void Print::_make_wipe_tower()
|
||||||
WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
|
WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
|
||||||
m_config.temperature.get_at(i),
|
m_config.temperature.get_at(i),
|
||||||
m_config.first_layer_temperature.get_at(i),
|
m_config.first_layer_temperature.get_at(i),
|
||||||
m_config.filament_loading_speed.get_at(i),
|
(float)m_config.filament_loading_speed.get_at(i),
|
||||||
m_config.filament_loading_speed_start.get_at(i),
|
(float)m_config.filament_loading_speed_start.get_at(i),
|
||||||
m_config.filament_unloading_speed.get_at(i),
|
(float)m_config.filament_unloading_speed.get_at(i),
|
||||||
m_config.filament_unloading_speed_start.get_at(i),
|
(float)m_config.filament_unloading_speed_start.get_at(i),
|
||||||
m_config.filament_toolchange_delay.get_at(i),
|
(float)m_config.filament_toolchange_delay.get_at(i),
|
||||||
m_config.filament_cooling_moves.get_at(i),
|
m_config.filament_cooling_moves.get_at(i),
|
||||||
m_config.filament_cooling_initial_speed.get_at(i),
|
(float)m_config.filament_cooling_initial_speed.get_at(i),
|
||||||
m_config.filament_cooling_final_speed.get_at(i),
|
(float)m_config.filament_cooling_final_speed.get_at(i),
|
||||||
m_config.filament_ramming_parameters.get_at(i),
|
m_config.filament_ramming_parameters.get_at(i),
|
||||||
m_config.nozzle_diameter.get_at(i));
|
(float)m_config.nozzle_diameter.get_at(i));
|
||||||
|
|
||||||
m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
|
||||||
wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
|
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
|
||||||
|
|
||||||
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
||||||
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
|
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
|
||||||
|
@ -1834,21 +1714,21 @@ void Print::_make_wipe_tower()
|
||||||
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
|
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
|
||||||
if (!layer_tools.has_wipe_tower) continue;
|
if (!layer_tools.has_wipe_tower) continue;
|
||||||
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
|
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
|
||||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
|
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id, false);
|
||||||
for (const auto extruder_id : layer_tools.extruders) {
|
for (const auto extruder_id : layer_tools.extruders) {
|
||||||
if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
|
||||||
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||||||
// Not all of that can be used for infill purging:
|
// Not all of that can be used for infill purging:
|
||||||
volume_to_wipe -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
volume_to_wipe -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||||
|
|
||||||
// try to assign some infills/objects for the wiping:
|
// try to assign some infills/objects for the wiping:
|
||||||
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
|
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
|
||||||
|
|
||||||
// add back the minimal amount toforce on the wipe tower:
|
// add back the minimal amount toforce on the wipe tower:
|
||||||
volume_to_wipe += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
volume_to_wipe += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||||
|
|
||||||
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
||||||
wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
|
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
|
||||||
first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
|
first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||||
current_extruder_id = extruder_id;
|
current_extruder_id = extruder_id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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).
|
||||||
|
|
|
@ -49,7 +49,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
||||||
{
|
{
|
||||||
// Translate meshes so that our toolpath generation algorithms work with smaller
|
// Translate meshes so that our toolpath generation algorithms work with smaller
|
||||||
// XY coordinates; this translation is an optimization and not strictly required.
|
// XY coordinates; this translation is an optimization and not strictly required.
|
||||||
// A cloned mesh will be aligned to 0 before slicing in _slice_region() since we
|
// A cloned mesh will be aligned to 0 before slicing in slice_region() since we
|
||||||
// don't assume it's already aligned and we don't alter the original position in model.
|
// don't assume it's already aligned and we don't alter the original position in model.
|
||||||
// We store the XY translation so that we can place copies correctly in the output G-code
|
// We store the XY translation so that we can place copies correctly in the output G-code
|
||||||
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
|
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
|
||||||
|
@ -590,7 +590,12 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
||||||
|
|
||||||
bool PrintObject::invalidate_all_steps()
|
bool PrintObject::invalidate_all_steps()
|
||||||
{
|
{
|
||||||
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
// First call the "invalidate" functions, which may cancel background processing.
|
||||||
|
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||||
|
// Then reset some of the depending values.
|
||||||
|
this->m_slicing_params.valid = false;
|
||||||
|
this->region_volumes.clear();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrintObject::has_support_material() const
|
bool PrintObject::has_support_material() const
|
||||||
|
@ -1354,10 +1359,12 @@ PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObject
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
|
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
|
||||||
{
|
{
|
||||||
PrintRegionConfig config = default_region_config;
|
PrintRegionConfig config = default_region_config;
|
||||||
normalize_and_apply_config(config, volume.get_object()->config);
|
normalize_and_apply_config(config, volume.get_object()->config);
|
||||||
|
if (layer_range_config != nullptr)
|
||||||
|
normalize_and_apply_config(config, *layer_range_config);
|
||||||
normalize_and_apply_config(config, volume.config);
|
normalize_and_apply_config(config, volume.config);
|
||||||
if (! volume.material_id().empty())
|
if (! volume.material_id().empty())
|
||||||
normalize_and_apply_config(config, volume.material()->config);
|
normalize_and_apply_config(config, volume.material()->config);
|
||||||
|
@ -1375,28 +1382,37 @@ void PrintObject::update_slicing_parameters()
|
||||||
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
|
this->print()->config(), m_config, unscale<double>(this->size(2)), this->object_extruders());
|
||||||
}
|
}
|
||||||
|
|
||||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z)
|
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full_config, const ModelObject& model_object, float object_max_z)
|
||||||
{
|
{
|
||||||
PrintConfig print_config;
|
PrintConfig print_config;
|
||||||
PrintObjectConfig object_config;
|
PrintObjectConfig object_config;
|
||||||
PrintRegionConfig default_region_config;
|
PrintRegionConfig default_region_config;
|
||||||
print_config .apply(full_config, true);
|
print_config.apply(full_config, true);
|
||||||
object_config.apply(full_config, true);
|
object_config.apply(full_config, true);
|
||||||
default_region_config.apply(full_config, true);
|
default_region_config.apply(full_config, true);
|
||||||
size_t num_extruders = print_config.nozzle_diameter.size();
|
size_t num_extruders = print_config.nozzle_diameter.size();
|
||||||
object_config = object_config_from_model_object(object_config, model_object, num_extruders);
|
object_config = object_config_from_model_object(object_config, model_object, num_extruders);
|
||||||
|
|
||||||
std::vector<unsigned int> object_extruders;
|
std::vector<unsigned int> object_extruders;
|
||||||
for (const ModelVolume *model_volume : model_object.volumes)
|
for (const ModelVolume* model_volume : model_object.volumes)
|
||||||
if (model_volume->is_model_part())
|
if (model_volume->is_model_part()) {
|
||||||
PrintRegion::collect_object_printing_extruders(
|
PrintRegion::collect_object_printing_extruders(
|
||||||
print_config,
|
print_config,
|
||||||
region_config_from_model_volume(default_region_config, *model_volume, num_extruders),
|
region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders),
|
||||||
object_extruders);
|
object_extruders);
|
||||||
|
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range_and_config : model_object.layer_config_ranges)
|
||||||
|
if (range_and_config.second.has("perimeter_extruder") ||
|
||||||
|
range_and_config.second.has("infill_extruder") ||
|
||||||
|
range_and_config.second.has("solid_infill_extruder"))
|
||||||
|
PrintRegion::collect_object_printing_extruders(
|
||||||
|
print_config,
|
||||||
|
region_config_from_model_volume(default_region_config, &range_and_config.second, *model_volume, num_extruders),
|
||||||
|
object_extruders);
|
||||||
|
}
|
||||||
sort_remove_duplicates(object_extruders);
|
sort_remove_duplicates(object_extruders);
|
||||||
|
|
||||||
if (object_max_z <= 0.f)
|
if (object_max_z <= 0.f)
|
||||||
object_max_z = model_object.raw_bounding_box().size().z();
|
object_max_z = (float)model_object.raw_bounding_box().size().z();
|
||||||
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
|
return SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,12 +1446,12 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
||||||
layer_height_profile.clear();
|
layer_height_profile.clear();
|
||||||
|
|
||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
if (0)
|
if (0)
|
||||||
// if (this->layer_height_profile.empty())
|
// if (this->layer_height_profile.empty())
|
||||||
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes);
|
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
||||||
else
|
else
|
||||||
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges);
|
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); // #ys_FIXME_experiment
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
@ -1489,22 +1505,28 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count model parts and modifier meshes, check whether the model parts are of the same region.
|
// Count model parts and modifier meshes, check whether the model parts are of the same region.
|
||||||
int single_volume_region = -2; // not set yet
|
int all_volumes_single_region = -2; // not set yet
|
||||||
|
bool has_z_ranges = false;
|
||||||
size_t num_volumes = 0;
|
size_t num_volumes = 0;
|
||||||
size_t num_modifiers = 0;
|
size_t num_modifiers = 0;
|
||||||
std::vector<int> map_volume_to_region(this->model_object()->volumes.size());
|
|
||||||
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
|
for (int region_id = 0; region_id < (int)this->region_volumes.size(); ++ region_id) {
|
||||||
for (int volume_id : this->region_volumes[region_id]) {
|
int last_volume_id = -1;
|
||||||
|
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
|
||||||
|
const int volume_id = volume_and_range.second;
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
if (model_volume->is_model_part()) {
|
if (model_volume->is_model_part()) {
|
||||||
map_volume_to_region[volume_id] = region_id;
|
if (last_volume_id == volume_id) {
|
||||||
if (single_volume_region == -2)
|
has_z_ranges = true;
|
||||||
// first model volume met
|
} else {
|
||||||
single_volume_region = region_id;
|
last_volume_id = volume_id;
|
||||||
else if (single_volume_region != region_id)
|
if (all_volumes_single_region == -2)
|
||||||
// multiple volumes met and they are not equal
|
// first model volume met
|
||||||
single_volume_region = -1;
|
all_volumes_single_region = region_id;
|
||||||
++ num_volumes;
|
else if (all_volumes_single_region != region_id)
|
||||||
|
// multiple volumes met and they are not equal
|
||||||
|
all_volumes_single_region = -1;
|
||||||
|
++ num_volumes;
|
||||||
|
}
|
||||||
} else if (model_volume->is_modifier())
|
} else if (model_volume->is_modifier())
|
||||||
++ num_modifiers;
|
++ num_modifiers;
|
||||||
}
|
}
|
||||||
|
@ -1514,13 +1536,13 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
// Slice all non-modifier volumes.
|
// Slice all non-modifier volumes.
|
||||||
bool clipped = false;
|
bool clipped = false;
|
||||||
bool upscaled = false;
|
bool upscaled = false;
|
||||||
if (! m_config.clip_multipart_objects.value || single_volume_region >= 0) {
|
if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
|
||||||
// Cheap path: Slice regions without mutual clipping.
|
// Cheap path: Slice regions without mutual clipping.
|
||||||
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
|
// The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region.
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
|
std::vector<ExPolygons> expolygons_by_layer = this->slice_region(region_id, slice_zs);
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start";
|
||||||
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
|
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id)
|
||||||
|
@ -1541,15 +1563,29 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
};
|
};
|
||||||
std::vector<SlicedVolume> sliced_volumes;
|
std::vector<SlicedVolume> sliced_volumes;
|
||||||
sliced_volumes.reserve(num_volumes);
|
sliced_volumes.reserve(num_volumes);
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||||
for (int volume_id : this->region_volumes[region_id]) {
|
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
||||||
|
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
||||||
|
int volume_id = volumes_and_ranges[i].second;
|
||||||
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
if (model_volume->is_model_part()) {
|
if (model_volume->is_model_part()) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
|
||||||
|
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
||||||
|
std::vector<t_layer_height_range> ranges;
|
||||||
|
ranges.emplace_back(volumes_and_ranges[i].first);
|
||||||
|
size_t j = i + 1;
|
||||||
|
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
|
||||||
|
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
|
||||||
|
ranges.back().second = volumes_and_ranges[j].first.second;
|
||||||
|
else
|
||||||
|
ranges.emplace_back(volumes_and_ranges[j].first);
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
sliced_volumes.emplace_back(volume_id, map_volume_to_region[volume_id], this->_slice_volume(slice_zs, *model_volume));
|
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume));
|
||||||
}
|
i = j;
|
||||||
|
} else
|
||||||
|
++ i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Second clip the volumes in the order they are presented at the user interface.
|
// Second clip the volumes in the order they are presented at the user interface.
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
|
||||||
tbb::parallel_for(
|
tbb::parallel_for(
|
||||||
|
@ -1603,7 +1639,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
|
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
|
||||||
// slicing in parallel
|
// slicing in parallel
|
||||||
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
|
std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
|
||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
if (expolygons_by_layer.empty())
|
if (expolygons_by_layer.empty())
|
||||||
continue;
|
continue;
|
||||||
|
@ -1619,7 +1655,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||||
Layer *layer = m_layers[layer_id];
|
Layer *layer = m_layers[layer_id];
|
||||||
LayerRegion *layerm = layer->m_regions[region_id];
|
LayerRegion *layerm = layer->m_regions[region_id];
|
||||||
LayerRegion *other_layerm = layer->m_regions[other_region_id];
|
LayerRegion *other_layerm = layer->m_regions[other_region_id];
|
||||||
if (layerm == nullptr || other_layerm == nullptr)
|
if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
|
||||||
continue;
|
continue;
|
||||||
Polygons other_slices = to_polygons(other_layerm->slices);
|
Polygons other_slices = to_polygons(other_layerm->slices);
|
||||||
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
|
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
|
||||||
|
@ -1752,46 +1788,127 @@ end:
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
|
// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region.
|
||||||
|
std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z) const
|
||||||
{
|
{
|
||||||
std::vector<const ModelVolume*> volumes;
|
std::vector<const ModelVolume*> volumes;
|
||||||
if (region_id < this->region_volumes.size()) {
|
if (region_id < this->region_volumes.size()) {
|
||||||
for (int volume_id : this->region_volumes[region_id]) {
|
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
|
||||||
const ModelVolume *volume = this->model_object()->volumes[volume_id];
|
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
|
||||||
if (modifier ? volume->is_modifier() : volume->is_model_part())
|
if (volume->is_model_part())
|
||||||
volumes.emplace_back(volume);
|
volumes.emplace_back(volume);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this->_slice_volumes(z, volumes);
|
return this->slice_volumes(z, volumes);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
|
// Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once.
|
||||||
|
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
|
||||||
|
{
|
||||||
|
std::vector<ExPolygons> out;
|
||||||
|
if (region_id < this->region_volumes.size())
|
||||||
|
{
|
||||||
|
std::vector<std::vector<t_layer_height_range>> volume_ranges;
|
||||||
|
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
||||||
|
volume_ranges.reserve(volumes_and_ranges.size());
|
||||||
|
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
||||||
|
int volume_id = volumes_and_ranges[i].second;
|
||||||
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
|
if (model_volume->is_modifier()) {
|
||||||
|
std::vector<t_layer_height_range> ranges;
|
||||||
|
ranges.emplace_back(volumes_and_ranges[i].first);
|
||||||
|
size_t j = i + 1;
|
||||||
|
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) {
|
||||||
|
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges[j].first.first) < EPSILON)
|
||||||
|
ranges.back().second = volumes_and_ranges[j].first.second;
|
||||||
|
else
|
||||||
|
ranges.emplace_back(volumes_and_ranges[j].first);
|
||||||
|
}
|
||||||
|
volume_ranges.emplace_back(std::move(ranges));
|
||||||
|
i = j;
|
||||||
|
} else
|
||||||
|
++ i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! volume_ranges.empty())
|
||||||
|
{
|
||||||
|
bool equal_ranges = true;
|
||||||
|
for (size_t i = 1; i < volume_ranges.size(); ++ i) {
|
||||||
|
assert(! volume_ranges[i].empty());
|
||||||
|
if (volume_ranges.front() != volume_ranges[i]) {
|
||||||
|
equal_ranges = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
|
||||||
|
// No modifier in this region was split to layer spans.
|
||||||
|
std::vector<const ModelVolume*> volumes;
|
||||||
|
for (const std::pair<t_layer_height_range, int> &volume_and_range : this->region_volumes[region_id]) {
|
||||||
|
const ModelVolume *volume = this->model_object()->volumes[volume_and_range.second];
|
||||||
|
if (volume->is_modifier())
|
||||||
|
volumes.emplace_back(volume);
|
||||||
|
}
|
||||||
|
out = this->slice_volumes(slice_zs, volumes);
|
||||||
|
} else {
|
||||||
|
// Some modifier in this region was split to layer spans.
|
||||||
|
std::vector<char> merge;
|
||||||
|
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||||
|
const std::vector<std::pair<t_layer_height_range, int>> &volumes_and_ranges = this->region_volumes[region_id];
|
||||||
|
for (size_t i = 0; i < volumes_and_ranges.size(); ) {
|
||||||
|
int volume_id = volumes_and_ranges[i].second;
|
||||||
|
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
|
||||||
|
if (model_volume->is_modifier()) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
|
||||||
|
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
|
||||||
|
std::vector<t_layer_height_range> ranges;
|
||||||
|
ranges.emplace_back(volumes_and_ranges[i].first);
|
||||||
|
size_t j = i + 1;
|
||||||
|
for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j)
|
||||||
|
ranges.emplace_back(volumes_and_ranges[j].first);
|
||||||
|
// slicing in parallel
|
||||||
|
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, *model_volume);
|
||||||
|
if (out.empty()) {
|
||||||
|
out = std::move(this_slices);
|
||||||
|
merge.assign(out.size(), false);
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < out.size(); ++ i)
|
||||||
|
if (! this_slices[i].empty())
|
||||||
|
if (! out[i].empty()) {
|
||||||
|
append(out[i], this_slices[i]);
|
||||||
|
merge[i] = true;
|
||||||
|
} else
|
||||||
|
out[i] = std::move(this_slices[i]);
|
||||||
|
}
|
||||||
|
i = j;
|
||||||
|
} else
|
||||||
|
++ i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < merge.size(); ++ i)
|
||||||
|
if (merge[i])
|
||||||
|
out[i] = union_ex(out[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType &model_volume_type) const
|
||||||
{
|
{
|
||||||
std::vector<const ModelVolume*> volumes;
|
std::vector<const ModelVolume*> volumes;
|
||||||
for (const ModelVolume *volume : this->model_object()->volumes)
|
for (const ModelVolume *volume : this->model_object()->volumes)
|
||||||
if (volume->is_support_enforcer())
|
if (volume->type() == model_volume_type)
|
||||||
volumes.emplace_back(volume);
|
volumes.emplace_back(volume);
|
||||||
std::vector<float> zs;
|
std::vector<float> zs;
|
||||||
zs.reserve(this->layers().size());
|
zs.reserve(this->layers().size());
|
||||||
for (const Layer *l : this->layers())
|
for (const Layer *l : this->layers())
|
||||||
zs.emplace_back((float)l->slice_z);
|
zs.emplace_back((float)l->slice_z);
|
||||||
return this->_slice_volumes(zs, volumes);
|
return this->slice_volumes(zs, volumes);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::slice_support_blockers() const
|
std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
|
||||||
{
|
|
||||||
std::vector<const ModelVolume*> volumes;
|
|
||||||
for (const ModelVolume *volume : this->model_object()->volumes)
|
|
||||||
if (volume->is_support_blocker())
|
|
||||||
volumes.emplace_back(volume);
|
|
||||||
std::vector<float> zs;
|
|
||||||
zs.reserve(this->layers().size());
|
|
||||||
for (const Layer *l : this->layers())
|
|
||||||
zs.emplace_back((float)l->slice_z);
|
|
||||||
return this->_slice_volumes(zs, volumes);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
|
|
||||||
{
|
{
|
||||||
std::vector<ExPolygons> layers;
|
std::vector<ExPolygons> layers;
|
||||||
if (! volumes.empty()) {
|
if (! volumes.empty()) {
|
||||||
|
@ -1828,34 +1945,71 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
|
||||||
return layers;
|
return layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::_slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
|
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const ModelVolume &volume) const
|
||||||
{
|
{
|
||||||
std::vector<ExPolygons> layers;
|
std::vector<ExPolygons> layers;
|
||||||
// Compose mesh.
|
if (! z.empty()) {
|
||||||
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
|
// Compose mesh.
|
||||||
TriangleMesh mesh(volume.mesh());
|
//FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
|
||||||
mesh.transform(volume.get_matrix(), true);
|
TriangleMesh mesh(volume.mesh());
|
||||||
if (mesh.repaired) {
|
mesh.transform(volume.get_matrix(), true);
|
||||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
if (mesh.repaired) {
|
||||||
stl_check_facets_exact(&mesh.stl);
|
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||||
|
stl_check_facets_exact(&mesh.stl);
|
||||||
|
}
|
||||||
|
if (mesh.stl.stats.number_of_facets > 0) {
|
||||||
|
mesh.transform(m_trafo, true);
|
||||||
|
// apply XY shift
|
||||||
|
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
|
||||||
|
// perform actual slicing
|
||||||
|
TriangleMeshSlicer mslicer;
|
||||||
|
const Print *print = this->print();
|
||||||
|
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
||||||
|
// TriangleMeshSlicer needs the shared vertices.
|
||||||
|
mesh.require_shared_vertices();
|
||||||
|
mslicer.init(&mesh, callback);
|
||||||
|
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
|
||||||
|
m_print->throw_if_canceled();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mesh.stl.stats.number_of_facets > 0) {
|
|
||||||
mesh.transform(m_trafo, true);
|
|
||||||
// apply XY shift
|
|
||||||
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
|
|
||||||
// perform actual slicing
|
|
||||||
TriangleMeshSlicer mslicer;
|
|
||||||
const Print *print = this->print();
|
|
||||||
auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
|
|
||||||
// TriangleMeshSlicer needs the shared vertices.
|
|
||||||
mesh.require_shared_vertices();
|
|
||||||
mslicer.init(&mesh, callback);
|
|
||||||
mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback);
|
|
||||||
m_print->throw_if_canceled();
|
|
||||||
}
|
|
||||||
return layers;
|
return layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter the zs not inside the ranges. The ranges are closed at the botton and open at the top, they are sorted lexicographically and non overlapping.
|
||||||
|
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, const ModelVolume &volume) const
|
||||||
|
{
|
||||||
|
std::vector<ExPolygons> out;
|
||||||
|
if (! z.empty() && ! ranges.empty()) {
|
||||||
|
if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) {
|
||||||
|
// All layers fit into a single range.
|
||||||
|
out = this->slice_volume(z, volume);
|
||||||
|
} else {
|
||||||
|
std::vector<float> z_filtered;
|
||||||
|
std::vector<std::pair<size_t, size_t>> n_filtered;
|
||||||
|
z_filtered.reserve(z.size());
|
||||||
|
n_filtered.reserve(2 * ranges.size());
|
||||||
|
size_t i = 0;
|
||||||
|
for (const t_layer_height_range &range : ranges) {
|
||||||
|
for (; i < z.size() && z[i] < range.first; ++ i) ;
|
||||||
|
size_t first = i;
|
||||||
|
for (; i < z.size() && z[i] < range.second; ++ i)
|
||||||
|
z_filtered.emplace_back(z[i]);
|
||||||
|
if (i > first)
|
||||||
|
n_filtered.emplace_back(std::make_pair(first, i));
|
||||||
|
}
|
||||||
|
if (! n_filtered.empty()) {
|
||||||
|
std::vector<ExPolygons> layers = this->slice_volume(z_filtered, volume);
|
||||||
|
out.assign(z.size(), ExPolygons());
|
||||||
|
i = 0;
|
||||||
|
for (const std::pair<size_t, size_t> &span : n_filtered)
|
||||||
|
for (size_t j = span.first; j < span.second; ++ j)
|
||||||
|
out[j] = std::move(layers[i ++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
std::string PrintObject::_fix_slicing_errors()
|
std::string PrintObject::_fix_slicing_errors()
|
||||||
{
|
{
|
||||||
// Collect layers with slicing errors.
|
// Collect layers with slicing errors.
|
||||||
|
@ -2119,7 +2273,7 @@ void PrintObject::clip_fill_surfaces()
|
||||||
//Should the pw not be half of the current value?
|
//Should the pw not be half of the current value?
|
||||||
float pw = FLT_MAX;
|
float pw = FLT_MAX;
|
||||||
for (const LayerRegion *layerm : layer->m_regions)
|
for (const LayerRegion *layerm : layer->m_regions)
|
||||||
pw = std::min<float>(pw, layerm->flow(frPerimeter).scaled_width());
|
pw = std::min(pw, (float)layerm->flow(frPerimeter).scaled_width());
|
||||||
// Append such thick perimeters to the areas that need support
|
// Append such thick perimeters to the areas that need support
|
||||||
polygons_append(overhangs, offset2(perimeters, -pw, +pw));
|
polygons_append(overhangs, offset2(perimeters, -pw, +pw));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -787,18 +787,27 @@ 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;
|
|
||||||
po.m_supportdata.reset(
|
|
||||||
new SLAPrintObject::SupportData(po.transformed_mesh()) );
|
|
||||||
|
|
||||||
// If supports are disabled, we can skip the model scan.
|
// If supports are disabled, we can skip the model scan.
|
||||||
if(!po.m_config.supports_enable.getBool()) return;
|
if(!po.m_config.supports_enable.getBool()) return;
|
||||||
|
|
||||||
|
if (!po.m_supportdata)
|
||||||
|
po.m_supportdata.reset(
|
||||||
|
new SLAPrintObject::SupportData(po.transformed_mesh()));
|
||||||
|
|
||||||
|
const ModelObject& mo = *po.m_model_object;
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Support point count "
|
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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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; }
|
||||||
|
@ -459,42 +459,38 @@ public:
|
||||||
~GLVolumeCollection() { clear(); };
|
~GLVolumeCollection() { clear(); };
|
||||||
|
|
||||||
std::vector<int> load_object(
|
std::vector<int> 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);
|
|
||||||
|
|
||||||
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(
|
||||||
const SLAPrintObject *print_object,
|
const SLAPrintObject* print_object,
|
||||||
int obj_idx,
|
int obj_idx,
|
||||||
// pairs of <instance_idx, print_instance_idx>
|
// pairs of <instance_idx, print_instance_idx>
|
||||||
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
|
||||||
|
|
|
@ -53,15 +53,12 @@ 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,
|
||||||
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
|
wxSize(25*wxGetApp().em_unit(), -1), wxCHB_TOP);
|
||||||
sbsizer->Add(m_shape_options_book);
|
sbsizer->Add(m_shape_options_book);
|
||||||
|
|
||||||
auto optgroup = init_shape_options_page(_(L("Rectangular")));
|
auto optgroup = init_shape_options_page(_(L("Rectangular")));
|
||||||
ConfigOptionDef def;
|
ConfigOptionDef def;
|
||||||
|
@ -92,13 +89,15 @@ 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);
|
||||||
auto sizer = new wxBoxSizer(wxHORIZONTAL);
|
shape_sizer->Add(shape_btn, 1, wxEXPAND);
|
||||||
sizer->Add(btn);
|
|
||||||
|
|
||||||
btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent e)
|
wxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
{
|
sizer->Add(shape_sizer, 1, wxEXPAND);
|
||||||
|
|
||||||
|
shape_btn->Bind(wxEVT_BUTTON, ([this](wxCommandEvent& e)
|
||||||
|
{
|
||||||
load_stl();
|
load_stl();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -106,8 +105,8 @@ 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,8 +116,8 @@ 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) ;
|
||||||
|
|
||||||
SetSizerAndFit(top_sizer);
|
SetSizerAndFit(top_sizer);
|
||||||
|
@ -135,8 +134,7 @@ 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,8 +232,8 @@ 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BedShapePanel::update_preview()
|
void BedShapePanel::update_preview()
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -524,7 +524,7 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
||||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
glsafe(::glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, half_w, half_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0));
|
||||||
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
|
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data()));
|
||||||
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
|
glsafe(::glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, half_w, half_h, GL_RGBA, GL_UNSIGNED_BYTE, m_layers_texture.data.data() + m_layers_texture.width * m_layers_texture.height * 4));
|
||||||
for (const GLVolume *glvolume : volumes.volumes) {
|
for (const GLVolume* glvolume : volumes.volumes) {
|
||||||
// Render the object using the layer editing shader and texture.
|
// Render the object using the layer editing shader and texture.
|
||||||
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
if (! glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
||||||
continue;
|
continue;
|
||||||
|
@ -543,8 +543,8 @@ void GLCanvas3D::LayersEditing::render_volumes(const GLCanvas3D& canvas, const G
|
||||||
{
|
{
|
||||||
// Something went wrong. Just render the object.
|
// Something went wrong. Just render the object.
|
||||||
assert(false);
|
assert(false);
|
||||||
for (const GLVolume *glvolume : volumes.volumes) {
|
for (const GLVolume* glvolume : volumes.volumes) {
|
||||||
// Render the object using the layer editing shader and texture.
|
// Render the object using the layer editing shader and texture.
|
||||||
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
if (!glvolume->is_active || glvolume->composite_id.object_id != this->last_object_id || glvolume->is_modifier)
|
||||||
continue;
|
continue;
|
||||||
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
|
glsafe(::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)glvolume->world_matrix().cast<float>().data()));
|
||||||
|
@ -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"))
|
|
||||||
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(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;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 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();
|
||||||
|
|
||||||
|
if (m_gizmos.is_enabled() && !m_gizmos.init(*this))
|
||||||
|
std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl;
|
||||||
|
|
||||||
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,8 +1964,8 @@ 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 {
|
||||||
// Recycling an old GLVolume.
|
// Recycling an old GLVolume.
|
||||||
|
@ -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
|
||||||
|
@ -2068,9 +2066,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||||
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
|
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
|
||||||
|
|
||||||
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,75 +3847,44 @@ 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.
|
||||||
|
const_cast<LayersEditing&>(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
|
||||||
|
|
||||||
|
if (m_config != nullptr)
|
||||||
{
|
{
|
||||||
// Update the layer editing selection to the first object selected, update the current object maximum Z.
|
const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false);
|
||||||
const_cast<LayersEditing&>(m_layers_editing).select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1);
|
m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
|
||||||
|
m_volumes.check_outside_state(m_config, nullptr);
|
||||||
if (m_config != nullptr)
|
|
||||||
{
|
|
||||||
const BoundingBoxf3& bed_bb = m_bed.get_bounding_box(false);
|
|
||||||
m_volumes.set_print_box((float)bed_bb.min(0), (float)bed_bb.min(1), 0.0f, (float)bed_bb.max(0), (float)bed_bb.max(1), (float)m_config->opt_float("max_print_height"));
|
|
||||||
m_volumes.check_outside_state(m_config, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_use_clipping_planes)
|
|
||||||
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
|
|
||||||
else
|
|
||||||
m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
|
||||||
|
|
||||||
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
|
|
||||||
|
|
||||||
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)) {
|
|
||||||
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) {
|
|
||||||
// Which volume to paint without the layer height profile shader?
|
|
||||||
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
|
|
||||||
});
|
|
||||||
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
|
|
||||||
m_layers_editing.render_volumes(*this, this->m_volumes);
|
|
||||||
} else {
|
|
||||||
// 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) {
|
|
||||||
return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
m_volumes.render_VBOs(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
|
|
||||||
m_shader.stop_using();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_use_clipping_planes)
|
||||||
|
m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]);
|
||||||
else
|
else
|
||||||
{
|
m_volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
||||||
::glClipPlane(GL_CLIP_PLANE0, (GLdouble*)m_camera_clipping_plane.get_data());
|
|
||||||
::glEnable(GL_CLIP_PLANE0);
|
|
||||||
|
|
||||||
if (m_use_clipping_planes)
|
m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data());
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
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)) {
|
||||||
|
int object_id = m_layers_editing.last_object_id;
|
||||||
|
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?
|
||||||
|
return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id);
|
||||||
|
});
|
||||||
|
// Let LayersEditing handle rendering of the active object using the layer height profile shader.
|
||||||
|
m_layers_editing.render_volumes(*this, this->m_volumes);
|
||||||
|
} else {
|
||||||
// do not cull backfaces to show broken geometry, if any
|
// 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) {
|
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_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_volumes.render(GLVolumeCollection::Transparent, false, m_camera.get_view_matrix());
|
||||||
|
m_shader.stop_using();
|
||||||
|
|
||||||
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,19 +4640,13 @@ 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.
|
// Clear the buffers, but keep them pre-allocated.
|
||||||
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
|
|
||||||
// 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 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;
|
||||||
}
|
}
|
||||||
|
@ -5416,9 +5338,9 @@ void GLCanvas3D::_load_fff_shells()
|
||||||
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
|
float brim_spacing = print->config().nozzle_diameter.values[0] * 1.25f - first_layer_height * (1. - M_PI_4);
|
||||||
|
|
||||||
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,8 +5358,8 @@ 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));
|
||||||
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
|
v.set_instance_rotation(Vec3d(0.0, 0.0, (double)instance.rotation));
|
||||||
|
@ -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,19 +5609,16 @@ 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
|
||||||
m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
|
m_view_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor());
|
||||||
#endif /* __WXMSW__ */
|
#endif /* __WXMSW__ */
|
||||||
|
|
||||||
// places the toolbar on the bottom-left corner of the 3d scene
|
// places the toolbar on the bottom-left corner of the 3d scene
|
||||||
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
|
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
|
||||||
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
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
341
src/slic3r/GUI/GUI_ObjectLayers.cpp
Normal file
341
src/slic3r/GUI/GUI_ObjectLayers.cpp
Normal 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
|
88
src/slic3r/GUI/GUI_ObjectLayers.hpp
Normal file
88
src/slic3r/GUI/GUI_ObjectLayers.hpp
Normal 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_
|
|
@ -1,6 +1,7 @@
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "GUI_ObjectManipulation.hpp"
|
#include "GUI_ObjectManipulation.hpp"
|
||||||
|
#include "GUI_ObjectLayers.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
@ -147,10 +148,10 @@ ObjectList::ObjectList(wxWindow* parent) :
|
||||||
wxAcceleratorTable accel(6, entries);
|
wxAcceleratorTable accel(6, entries);
|
||||||
SetAcceleratorTable(accel);
|
SetAcceleratorTable(accel);
|
||||||
|
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY)); }, wxID_COPY);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY);
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); }, wxID_PASTE);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->paste(); }, wxID_PASTE);
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL);
|
||||||
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
|
this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE);
|
||||||
}
|
}
|
||||||
#else __WXOSX__
|
#else __WXOSX__
|
||||||
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX
|
||||||
|
@ -350,12 +351,13 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons
|
||||||
const ItemType type = m_objects_model->GetItemType(item);
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
|
|
||||||
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
|
const int obj_idx = type & itObject ? m_objects_model->GetIdByItem(item) :
|
||||||
m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
||||||
|
|
||||||
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
|
const int vol_idx = type & itVolume ? m_objects_model->GetVolumeIdByItem(item) : -1;
|
||||||
|
|
||||||
assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0));
|
assert(obj_idx >= 0 || ((type & itVolume) && vol_idx >=0));
|
||||||
return type & itVolume ?(*m_objects)[obj_idx]->volumes[vol_idx]->config :
|
return type & itVolume ?(*m_objects)[obj_idx]->volumes[vol_idx]->config :
|
||||||
|
type & itLayer ?(*m_objects)[obj_idx]->layer_config_ranges[m_objects_model->GetLayerRangeByItem(item)] :
|
||||||
(*m_objects)[obj_idx]->config;
|
(*m_objects)[obj_idx]->config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,16 +443,23 @@ void ObjectList::update_extruder_in_config(const wxDataViewItem& item)
|
||||||
{
|
{
|
||||||
if (m_prevent_update_extruder_in_config)
|
if (m_prevent_update_extruder_in_config)
|
||||||
return;
|
return;
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
|
|
||||||
|
const ItemType item_type = m_objects_model->GetItemType(item);
|
||||||
|
if (item_type & itObject) {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(item);
|
const int obj_idx = m_objects_model->GetIdByItem(item);
|
||||||
m_config = &(*m_objects)[obj_idx]->config;
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
|
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
||||||
|
if (item_type & itVolume)
|
||||||
|
{
|
||||||
const int volume_id = m_objects_model->GetVolumeIdByItem(item);
|
const int volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
if (obj_idx < 0 || volume_id < 0)
|
if (obj_idx < 0 || volume_id < 0)
|
||||||
return;
|
return;
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
|
}
|
||||||
|
else if (item_type & itLayer)
|
||||||
|
m_config = &get_item_config(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxVariant variant;
|
wxVariant variant;
|
||||||
|
@ -569,9 +578,75 @@ void ObjectList::selection_changed()
|
||||||
wxPostEvent(this, event);
|
wxPostEvent(this, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const wxDataViewItem item = GetSelection())
|
||||||
|
{
|
||||||
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
|
// to correct visual hints for layers editing on the Scene
|
||||||
|
if (type & (itLayer|itLayerRoot)) {
|
||||||
|
wxGetApp().obj_layers()->reset_selection();
|
||||||
|
|
||||||
|
if (type & itLayerRoot)
|
||||||
|
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
|
||||||
|
else {
|
||||||
|
wxGetApp().obj_layers()->set_selectable_range(m_objects_model->GetLayerRangeByItem(item));
|
||||||
|
wxGetApp().obj_layers()->update_scene_from_editor_selection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
part_selection_changed();
|
part_selection_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::fill_layer_config_ranges_cache()
|
||||||
|
{
|
||||||
|
wxDataViewItemArray sel_layers;
|
||||||
|
GetSelections(sel_layers);
|
||||||
|
|
||||||
|
const int obj_idx = m_objects_model->GetObjectIdByItem(sel_layers[0]);
|
||||||
|
if (obj_idx < 0 || (int)m_objects->size() <= obj_idx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||||
|
m_layer_config_ranges_cache.clear();
|
||||||
|
|
||||||
|
for (const auto layer_item : sel_layers)
|
||||||
|
if (m_objects_model->GetItemType(layer_item) & itLayer) {
|
||||||
|
auto range = m_objects_model->GetLayerRangeByItem(layer_item);
|
||||||
|
auto it = ranges.find(range);
|
||||||
|
if (it != ranges.end())
|
||||||
|
m_layer_config_ranges_cache[it->first] = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::paste_layers_into_list()
|
||||||
|
{
|
||||||
|
const int obj_idx = m_objects_model->GetObjectIdByItem(GetSelection());
|
||||||
|
|
||||||
|
if (obj_idx < 0 || (int)m_objects->size() <= obj_idx ||
|
||||||
|
m_layer_config_ranges_cache.empty() || printer_technology() == ptSLA)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx);
|
||||||
|
wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item);
|
||||||
|
if (layers_item)
|
||||||
|
m_objects_model->Delete(layers_item);
|
||||||
|
|
||||||
|
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||||
|
|
||||||
|
// and create Layer item(s) according to the layer_config_ranges
|
||||||
|
for (const auto range : m_layer_config_ranges_cache)
|
||||||
|
ranges.emplace(range);
|
||||||
|
|
||||||
|
layers_item = add_layer_root_item(object_item);
|
||||||
|
|
||||||
|
changed_object(obj_idx);
|
||||||
|
|
||||||
|
select_item(layers_item);
|
||||||
|
#ifndef __WXOSX__
|
||||||
|
selection_changed();
|
||||||
|
#endif //no __WXOSX__
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
|
void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes)
|
||||||
{
|
{
|
||||||
if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
|
if ((obj_idx < 0) || ((int)m_objects->size() <= obj_idx))
|
||||||
|
@ -653,7 +728,7 @@ void ObjectList::OnContextMenu(wxDataViewEvent&)
|
||||||
const wxPoint pt = get_mouse_position_in_control();
|
const wxPoint pt = get_mouse_position_in_control();
|
||||||
HitTest(pt, item, col);
|
HitTest(pt, item, col);
|
||||||
if (!item)
|
if (!item)
|
||||||
#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX
|
#ifdef __WXOSX__ // temporary workaround for OSX
|
||||||
// after Yosemite OS X version, HitTest return undefined item
|
// after Yosemite OS X version, HitTest return undefined item
|
||||||
item = GetSelection();
|
item = GetSelection();
|
||||||
if (item)
|
if (item)
|
||||||
|
@ -699,10 +774,11 @@ void ObjectList::show_context_menu()
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
const ItemType type = m_objects_model->GetItemType(item);
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
if (!(type & (itObject | itVolume | itInstance)))
|
if (!(type & (itObject | itVolume | itLayer | itInstance)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wxMenu* menu = type & itInstance ? &m_menu_instance :
|
wxMenu* menu = type & itInstance ? &m_menu_instance :
|
||||||
|
type & itLayer ? &m_menu_layer :
|
||||||
m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
|
m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part :
|
||||||
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
|
printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object;
|
||||||
|
|
||||||
|
@ -713,6 +789,22 @@ void ObjectList::show_context_menu()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::copy()
|
||||||
|
{
|
||||||
|
if (m_selection_mode & smLayer)
|
||||||
|
fill_layer_config_ranges_cache();
|
||||||
|
else
|
||||||
|
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::paste()
|
||||||
|
{
|
||||||
|
if (!m_layer_config_ranges_cache.empty())
|
||||||
|
paste_layers_into_list();
|
||||||
|
else
|
||||||
|
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef __WXOSX__
|
#ifndef __WXOSX__
|
||||||
void ObjectList::key_event(wxKeyEvent& event)
|
void ObjectList::key_event(wxKeyEvent& event)
|
||||||
{
|
{
|
||||||
|
@ -727,10 +819,10 @@ void ObjectList::key_event(wxKeyEvent& event)
|
||||||
}
|
}
|
||||||
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
|
else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/))
|
||||||
select_item_all_children();
|
select_item_all_children();
|
||||||
else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))
|
else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL))
|
||||||
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_COPY));
|
copy();
|
||||||
else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL))
|
else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL))
|
||||||
wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE));
|
paste();
|
||||||
else
|
else
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
@ -1033,7 +1125,17 @@ void ObjectList::get_settings_choice(const wxString& category_name)
|
||||||
|
|
||||||
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
|
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
|
||||||
{
|
{
|
||||||
const std::vector<std::string>& options = get_options_for_bundle(bundle_name);
|
std::vector<std::string> options = get_options_for_bundle(bundle_name);
|
||||||
|
|
||||||
|
/* Because of we couldn't edited layer_height for ItVolume from settings list,
|
||||||
|
* correct options according to the selected item type :
|
||||||
|
* remove "layer_height" option
|
||||||
|
*/
|
||||||
|
if ((m_objects_model->GetItemType(GetSelection()) & itVolume) && bundle_name == _("Layers and Perimeters")) {
|
||||||
|
const auto layer_height_it = std::find(options.begin(), options.end(), "layer_height");
|
||||||
|
if (layer_height_it != options.end())
|
||||||
|
options.erase(layer_height_it);
|
||||||
|
}
|
||||||
|
|
||||||
assert(m_config);
|
assert(m_config);
|
||||||
auto opt_keys = m_config->keys();
|
auto opt_keys = m_config->keys();
|
||||||
|
@ -1137,6 +1239,12 @@ wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu)
|
||||||
[this]() { return is_splittable(); }, wxGetApp().plater());
|
[this]() { return is_splittable(); }, wxGetApp().plater());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxMenuItem* ObjectList::append_menu_item_layers_editing(wxMenu* menu)
|
||||||
|
{
|
||||||
|
return append_menu_item(menu, wxID_ANY, _(L("Edit Layers")), "",
|
||||||
|
[this](wxCommandEvent&) { layers_editing(); }, "layers", menu);
|
||||||
|
}
|
||||||
|
|
||||||
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
|
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
|
||||||
{
|
{
|
||||||
MenuWithSeparators* menu = dynamic_cast<MenuWithSeparators*>(menu_);
|
MenuWithSeparators* menu = dynamic_cast<MenuWithSeparators*>(menu_);
|
||||||
|
@ -1301,7 +1409,11 @@ void ObjectList::create_object_popupmenu(wxMenu *menu)
|
||||||
append_menu_item_scale_selection_to_fit_print_volume(menu);
|
append_menu_item_scale_selection_to_fit_print_volume(menu);
|
||||||
|
|
||||||
// Split object to parts
|
// Split object to parts
|
||||||
m_menu_item_split = append_menu_item_split(menu);
|
append_menu_item_split(menu);
|
||||||
|
menu->AppendSeparator();
|
||||||
|
|
||||||
|
// Layers Editing for object
|
||||||
|
append_menu_item_layers_editing(menu);
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
|
|
||||||
// rest of a object_menu will be added later in:
|
// rest of a object_menu will be added later in:
|
||||||
|
@ -1330,7 +1442,7 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
|
||||||
append_menu_item_fix_through_netfabb(menu);
|
append_menu_item_fix_through_netfabb(menu);
|
||||||
append_menu_item_export_stl(menu);
|
append_menu_item_export_stl(menu);
|
||||||
|
|
||||||
m_menu_item_split_part = append_menu_item_split(menu);
|
append_menu_item_split(menu);
|
||||||
|
|
||||||
// Append change part type
|
// Append change part type
|
||||||
menu->AppendSeparator();
|
menu->AppendSeparator();
|
||||||
|
@ -1576,38 +1688,52 @@ void ObjectList::del_subobject_item(wxDataViewItem& item)
|
||||||
ItemType type;
|
ItemType type;
|
||||||
|
|
||||||
m_objects_model->GetItemInfo(item, type, obj_idx, idx);
|
m_objects_model->GetItemInfo(item, type, obj_idx, idx);
|
||||||
if (type == itUndef)
|
if (type & itUndef)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (type == itSettings)
|
if (type & itSettings)
|
||||||
del_settings_from_config();
|
del_settings_from_config(m_objects_model->GetParent(item));
|
||||||
else if (type == itInstanceRoot && obj_idx != -1)
|
else if (type & itInstanceRoot && obj_idx != -1)
|
||||||
del_instances_from_object(obj_idx);
|
del_instances_from_object(obj_idx);
|
||||||
|
else if (type & itLayerRoot && obj_idx != -1)
|
||||||
|
del_layers_from_object(obj_idx);
|
||||||
|
else if (type & itLayer && obj_idx != -1)
|
||||||
|
del_layer_from_object(obj_idx, m_objects_model->GetLayerRangeByItem(item));
|
||||||
else if (idx == -1)
|
else if (idx == -1)
|
||||||
return;
|
return;
|
||||||
else if (!del_subobject_from_object(obj_idx, idx, type))
|
else if (!del_subobject_from_object(obj_idx, idx, type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If last volume item with warning was deleted, unmark object item
|
// If last volume item with warning was deleted, unmark object item
|
||||||
if (type == itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
|
if (type & itVolume && (*m_objects)[obj_idx]->get_mesh_errors_count() == 0)
|
||||||
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
|
m_objects_model->DeleteWarningIcon(m_objects_model->GetParent(item));
|
||||||
|
|
||||||
m_objects_model->Delete(item);
|
m_objects_model->Delete(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::del_settings_from_config()
|
void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
|
||||||
{
|
{
|
||||||
auto opt_keys = m_config->keys();
|
const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer;
|
||||||
if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
|
|
||||||
|
const int opt_cnt = m_config->keys().size();
|
||||||
|
if (opt_cnt == 1 && m_config->has("extruder") ||
|
||||||
|
is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int extruder = -1;
|
int extruder = -1;
|
||||||
if (m_config->has("extruder"))
|
if (m_config->has("extruder"))
|
||||||
extruder = m_config->option<ConfigOptionInt>("extruder")->value;
|
extruder = m_config->option<ConfigOptionInt>("extruder")->value;
|
||||||
|
|
||||||
|
coordf_t layer_height = 0.0;
|
||||||
|
if (is_layer_settings)
|
||||||
|
layer_height = m_config->opt_float("layer_height");
|
||||||
|
|
||||||
m_config->clear();
|
m_config->clear();
|
||||||
|
|
||||||
if (extruder >= 0)
|
if (extruder >= 0)
|
||||||
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
|
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
|
||||||
|
if (is_layer_settings)
|
||||||
|
m_config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::del_instances_from_object(const int obj_idx)
|
void ObjectList::del_instances_from_object(const int obj_idx)
|
||||||
|
@ -1624,6 +1750,24 @@ void ObjectList::del_instances_from_object(const int obj_idx)
|
||||||
changed_object(obj_idx);
|
changed_object(obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range)
|
||||||
|
{
|
||||||
|
const auto del_range = object(obj_idx)->layer_config_ranges.find(layer_range);
|
||||||
|
if (del_range == object(obj_idx)->layer_config_ranges.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
object(obj_idx)->layer_config_ranges.erase(del_range);
|
||||||
|
|
||||||
|
changed_object(obj_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::del_layers_from_object(const int obj_idx)
|
||||||
|
{
|
||||||
|
object(obj_idx)->layer_config_ranges.clear();
|
||||||
|
|
||||||
|
changed_object(obj_idx);
|
||||||
|
}
|
||||||
|
|
||||||
bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type)
|
bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type)
|
||||||
{
|
{
|
||||||
if (obj_idx == 1000)
|
if (obj_idx == 1000)
|
||||||
|
@ -1719,6 +1863,70 @@ void ObjectList::split()
|
||||||
changed_object(obj_idx);
|
changed_object(obj_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ObjectList::layers_editing()
|
||||||
|
{
|
||||||
|
const auto item = GetSelection();
|
||||||
|
const int obj_idx = get_selected_obj_idx();
|
||||||
|
if (!item || obj_idx < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const wxDataViewItem obj_item = m_objects_model->GetTopParent(item);
|
||||||
|
wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(obj_item);
|
||||||
|
|
||||||
|
// if it doesn't exist now
|
||||||
|
if (!layers_item.IsOk())
|
||||||
|
{
|
||||||
|
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||||
|
|
||||||
|
// set some default value
|
||||||
|
if (ranges.empty())
|
||||||
|
ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx);
|
||||||
|
|
||||||
|
// create layer root item
|
||||||
|
layers_item = add_layer_root_item(obj_item);
|
||||||
|
}
|
||||||
|
if (!layers_item.IsOk())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// to correct visual hints for layers editing on the Scene, reset previous selection
|
||||||
|
wxGetApp().obj_layers()->reset_selection();
|
||||||
|
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
|
||||||
|
|
||||||
|
// select LayerRoor item and expand
|
||||||
|
select_item(layers_item);
|
||||||
|
Expand(layers_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item)
|
||||||
|
{
|
||||||
|
const int obj_idx = m_objects_model->GetIdByItem(obj_item);
|
||||||
|
if (obj_idx < 0 ||
|
||||||
|
object(obj_idx)->layer_config_ranges.empty() ||
|
||||||
|
printer_technology() == ptSLA)
|
||||||
|
return wxDataViewItem(0);
|
||||||
|
|
||||||
|
// create LayerRoot item
|
||||||
|
wxDataViewItem layers_item = m_objects_model->AddLayersRoot(obj_item);
|
||||||
|
|
||||||
|
// and create Layer item(s) according to the layer_config_ranges
|
||||||
|
for (const auto range : object(obj_idx)->layer_config_ranges)
|
||||||
|
add_layer_item(range.first, layers_item);
|
||||||
|
|
||||||
|
return layers_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicPrintConfig ObjectList::get_default_layer_config(const int obj_idx)
|
||||||
|
{
|
||||||
|
DynamicPrintConfig config;
|
||||||
|
coordf_t layer_height = object(obj_idx)->config.has("layer_height") ?
|
||||||
|
object(obj_idx)->config.opt_float("layer_height") :
|
||||||
|
wxGetApp().preset_bundle->prints.get_edited_preset().config.opt_float("layer_height");
|
||||||
|
config.set_key_value("layer_height",new ConfigOptionFloat(layer_height));
|
||||||
|
config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume)
|
bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume)
|
||||||
{
|
{
|
||||||
auto obj_idx = get_selected_obj_idx();
|
auto obj_idx = get_selected_obj_idx();
|
||||||
|
@ -1788,6 +1996,7 @@ void ObjectList::part_selection_changed()
|
||||||
|
|
||||||
bool update_and_show_manipulations = false;
|
bool update_and_show_manipulations = false;
|
||||||
bool update_and_show_settings = false;
|
bool update_and_show_settings = false;
|
||||||
|
bool update_and_show_layers = false;
|
||||||
|
|
||||||
const auto item = GetSelection();
|
const auto item = GetSelection();
|
||||||
|
|
||||||
|
@ -1810,36 +2019,47 @@ void ObjectList::part_selection_changed()
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto parent = m_objects_model->GetParent(item);
|
obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||||
// Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
|
|
||||||
obj_idx = m_objects_model->GetIdByItem(parent);
|
const ItemType type = m_objects_model->GetItemType(item);
|
||||||
if (m_objects_model->GetItemType(item) == itSettings) {
|
if (type & itSettings) {
|
||||||
if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) {
|
const auto parent = m_objects_model->GetParent(item);
|
||||||
|
const ItemType parent_type = m_objects_model->GetItemType(parent);
|
||||||
|
|
||||||
|
if (parent_type & itObject) {
|
||||||
og_name = _(L("Object Settings to modify"));
|
og_name = _(L("Object Settings to modify"));
|
||||||
m_config = &(*m_objects)[obj_idx]->config;
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
}
|
}
|
||||||
else {
|
else if (parent_type & itVolume) {
|
||||||
og_name = _(L("Part Settings to modify"));
|
og_name = _(L("Part Settings to modify"));
|
||||||
auto main_parent = m_objects_model->GetParent(parent);
|
volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
||||||
obj_idx = m_objects_model->GetIdByItem(main_parent);
|
|
||||||
const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
|
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
}
|
}
|
||||||
|
else if (parent_type & itLayer) {
|
||||||
|
og_name = _(L("Layer range Settings to modify"));
|
||||||
|
m_config = &get_item_config(parent);
|
||||||
|
}
|
||||||
update_and_show_settings = true;
|
update_and_show_settings = true;
|
||||||
}
|
}
|
||||||
else if (m_objects_model->GetItemType(item) == itVolume) {
|
else if (type & itVolume) {
|
||||||
og_name = _(L("Part manipulation"));
|
og_name = _(L("Part manipulation"));
|
||||||
volume_id = m_objects_model->GetVolumeIdByItem(item);
|
volume_id = m_objects_model->GetVolumeIdByItem(item);
|
||||||
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
m_config = &(*m_objects)[obj_idx]->volumes[volume_id]->config;
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
}
|
}
|
||||||
else if (m_objects_model->GetItemType(item) == itInstance) {
|
else if (type & itInstance) {
|
||||||
og_name = _(L("Instance manipulation"));
|
og_name = _(L("Instance manipulation"));
|
||||||
update_and_show_manipulations = true;
|
update_and_show_manipulations = true;
|
||||||
|
|
||||||
// fill m_config by object's values
|
// fill m_config by object's values
|
||||||
const int obj_idx_ = m_objects_model->GetObjectIdByItem(item);
|
m_config = &(*m_objects)[obj_idx]->config;
|
||||||
m_config = &(*m_objects)[obj_idx_]->config;
|
}
|
||||||
|
else if (type & (itLayerRoot|itLayer)) {
|
||||||
|
og_name = type & itLayerRoot ? _(L("Layers Editing")) : _(L("Layer Editing"));
|
||||||
|
update_and_show_layers = true;
|
||||||
|
|
||||||
|
if (type & itLayer)
|
||||||
|
m_config = &get_item_config(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1859,11 +2079,18 @@ void ObjectList::part_selection_changed()
|
||||||
if (update_and_show_settings)
|
if (update_and_show_settings)
|
||||||
wxGetApp().obj_settings()->get_og()->set_name(" " + og_name + " ");
|
wxGetApp().obj_settings()->get_og()->set_name(" " + og_name + " ");
|
||||||
|
|
||||||
|
if (printer_technology() == ptSLA)
|
||||||
|
update_and_show_layers = false;
|
||||||
|
else if (update_and_show_layers)
|
||||||
|
wxGetApp().obj_layers()->get_og()->set_name(" " + og_name + " ");
|
||||||
|
|
||||||
Sidebar& panel = wxGetApp().sidebar();
|
Sidebar& panel = wxGetApp().sidebar();
|
||||||
panel.Freeze();
|
panel.Freeze();
|
||||||
|
|
||||||
|
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
|
||||||
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
|
wxGetApp().obj_manipul() ->UpdateAndShow(update_and_show_manipulations);
|
||||||
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
|
wxGetApp().obj_settings()->UpdateAndShow(update_and_show_settings);
|
||||||
|
wxGetApp().obj_layers() ->UpdateAndShow(update_and_show_layers);
|
||||||
wxGetApp().sidebar().show_info_sizer();
|
wxGetApp().sidebar().show_info_sizer();
|
||||||
|
|
||||||
panel.Layout();
|
panel.Layout();
|
||||||
|
@ -1909,6 +2136,9 @@ void ObjectList::add_object_to_list(size_t obj_idx)
|
||||||
Expand(item);
|
Expand(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add layers if it has
|
||||||
|
add_layer_root_item(item);
|
||||||
|
|
||||||
#ifndef __WXOSX__
|
#ifndef __WXOSX__
|
||||||
selection_changed();
|
selection_changed();
|
||||||
#endif //__WXMSW__
|
#endif //__WXMSW__
|
||||||
|
@ -2053,16 +2283,196 @@ void ObjectList::remove()
|
||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
GetSelections(sels);
|
GetSelections(sels);
|
||||||
|
|
||||||
|
wxDataViewItem parent = wxDataViewItem(0);
|
||||||
|
|
||||||
for (auto& item : sels)
|
for (auto& item : sels)
|
||||||
{
|
{
|
||||||
if (m_objects_model->GetParent(item) == wxDataViewItem(0))
|
if (m_objects_model->GetParent(item) == wxDataViewItem(0))
|
||||||
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
|
delete_from_model_and_list(itObject, m_objects_model->GetIdByItem(item), -1);
|
||||||
else {
|
else {
|
||||||
if (sels.size() == 1)
|
if (m_objects_model->GetItemType(item) & itLayer) {
|
||||||
|
parent = m_objects_model->GetParent(item);
|
||||||
|
wxDataViewItemArray children;
|
||||||
|
if (m_objects_model->GetChildren(parent, children) == 1)
|
||||||
|
parent = m_objects_model->GetTopParent(item);
|
||||||
|
}
|
||||||
|
else if (sels.size() == 1)
|
||||||
select_item(m_objects_model->GetParent(item));
|
select_item(m_objects_model->GetParent(item));
|
||||||
|
|
||||||
del_subobject_item(item);
|
del_subobject_item(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
select_item(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::del_layer_range(const t_layer_height_range& range)
|
||||||
|
{
|
||||||
|
const int obj_idx = get_selected_obj_idx();
|
||||||
|
if (obj_idx < 0) return;
|
||||||
|
|
||||||
|
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||||
|
|
||||||
|
wxDataViewItem selectable_item = GetSelection();
|
||||||
|
|
||||||
|
if (ranges.size() == 1)
|
||||||
|
selectable_item = m_objects_model->GetParent(selectable_item);
|
||||||
|
|
||||||
|
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, range);
|
||||||
|
del_subobject_item(layer_item);
|
||||||
|
|
||||||
|
select_item(selectable_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_min_layer_height(const int extruder_idx)
|
||||||
|
{
|
||||||
|
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
|
return config.opt_float("min_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_max_layer_height(const int extruder_idx)
|
||||||
|
{
|
||||||
|
const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
|
return config.opt_float("max_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::add_layer_range_after_current(const t_layer_height_range& current_range)
|
||||||
|
{
|
||||||
|
const int obj_idx = get_selected_obj_idx();
|
||||||
|
if (obj_idx < 0) return;
|
||||||
|
|
||||||
|
const wxDataViewItem layers_item = GetSelection();
|
||||||
|
|
||||||
|
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||||
|
|
||||||
|
const t_layer_height_range& last_range = (--ranges.end())->first;
|
||||||
|
|
||||||
|
if (current_range == last_range)
|
||||||
|
{
|
||||||
|
const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f };
|
||||||
|
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||||
|
add_layer_item(new_range, layers_item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const t_layer_height_range& next_range = (++ranges.find(current_range))->first;
|
||||||
|
|
||||||
|
if (current_range.second > next_range.first)
|
||||||
|
return; // range division has no sense
|
||||||
|
|
||||||
|
const int layer_idx = m_objects_model->GetItemIdByLayerRange(obj_idx, next_range);
|
||||||
|
if (layer_idx < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (current_range.second == next_range.first)
|
||||||
|
{
|
||||||
|
const auto old_config = ranges.at(next_range);
|
||||||
|
|
||||||
|
const coordf_t delta = (next_range.second - next_range.first);
|
||||||
|
if (delta < get_min_layer_height(old_config.opt_int("extruder"))/*0.05f*/) // next range division has no sense
|
||||||
|
return;
|
||||||
|
|
||||||
|
const coordf_t midl_layer = next_range.first + 0.5f * delta;
|
||||||
|
|
||||||
|
t_layer_height_range new_range = { midl_layer, next_range.second };
|
||||||
|
|
||||||
|
// delete old layer
|
||||||
|
|
||||||
|
wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range);
|
||||||
|
del_subobject_item(layer_item);
|
||||||
|
|
||||||
|
// create new 2 layers instead of deleted one
|
||||||
|
|
||||||
|
ranges[new_range] = old_config;
|
||||||
|
add_layer_item(new_range, layers_item, layer_idx);
|
||||||
|
|
||||||
|
new_range = { current_range.second, midl_layer };
|
||||||
|
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||||
|
add_layer_item(new_range, layers_item, layer_idx);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const t_layer_height_range new_range = { current_range.second, next_range.first };
|
||||||
|
ranges[new_range] = get_default_layer_config(obj_idx);
|
||||||
|
add_layer_item(new_range, layers_item, layer_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changed_object(obj_idx);
|
||||||
|
|
||||||
|
// select item to update layers sizer
|
||||||
|
select_item(layers_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::add_layer_item(const t_layer_height_range& range,
|
||||||
|
const wxDataViewItem layers_item,
|
||||||
|
const int layer_idx /* = -1*/)
|
||||||
|
{
|
||||||
|
const int obj_idx = m_objects_model->GetObjectIdByItem(layers_item);
|
||||||
|
if (obj_idx < 0) return;
|
||||||
|
|
||||||
|
const DynamicPrintConfig& config = object(obj_idx)->layer_config_ranges[range];
|
||||||
|
if (!config.has("extruder"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto layer_item = m_objects_model->AddLayersChild(layers_item,
|
||||||
|
range,
|
||||||
|
config.opt_int("extruder"),
|
||||||
|
layer_idx);
|
||||||
|
|
||||||
|
if (config.keys().size() > 2)
|
||||||
|
select_item(m_objects_model->AddSettingsChild(layer_item));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height)
|
||||||
|
{
|
||||||
|
const int obj_idx = get_selected_obj_idx();
|
||||||
|
if (obj_idx < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DynamicPrintConfig* config = &object(obj_idx)->layer_config_ranges[range];
|
||||||
|
if (fabs(layer_height - config->opt_float("layer_height")) < EPSILON)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int extruder_idx = config->opt_int("extruder");
|
||||||
|
|
||||||
|
if (layer_height >= get_min_layer_height(extruder_idx) &&
|
||||||
|
layer_height <= get_max_layer_height(extruder_idx))
|
||||||
|
{
|
||||||
|
config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_layer_height_range& new_range)
|
||||||
|
{
|
||||||
|
const int obj_idx = get_selected_obj_idx();
|
||||||
|
if (obj_idx < 0) return false;
|
||||||
|
|
||||||
|
const ItemType sel_type = m_objects_model->GetItemType(GetSelection());
|
||||||
|
|
||||||
|
t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges;
|
||||||
|
|
||||||
|
const DynamicPrintConfig config = ranges[range];
|
||||||
|
|
||||||
|
ranges.erase(range);
|
||||||
|
ranges[new_range] = config;
|
||||||
|
|
||||||
|
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
|
||||||
|
m_objects_model->DeleteChildren(root_item);
|
||||||
|
|
||||||
|
if (root_item.IsOk())
|
||||||
|
// create Layer item(s) according to the layer_config_ranges
|
||||||
|
for (const auto r : ranges)
|
||||||
|
add_layer_item(r.first, root_item);
|
||||||
|
|
||||||
|
select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item);
|
||||||
|
Expand(root_item);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::init_objects()
|
void ObjectList::init_objects()
|
||||||
|
@ -2092,11 +2502,12 @@ void ObjectList::update_selections()
|
||||||
m_selection_mode = smInstance;
|
m_selection_mode = smInstance;
|
||||||
|
|
||||||
// We doesn't update selection if SettingsItem for the current object/part is selected
|
// We doesn't update selection if SettingsItem for the current object/part is selected
|
||||||
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
// if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings )
|
||||||
|
if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) & (itSettings | itLayerRoot | itLayer))
|
||||||
{
|
{
|
||||||
const auto item = GetSelection();
|
const auto item = GetSelection();
|
||||||
if (selection.is_single_full_object()) {
|
if (selection.is_single_full_object()) {
|
||||||
if ( m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx())
|
if (m_objects_model->GetObjectIdByItem(item) == selection.get_object_idx())
|
||||||
return;
|
return;
|
||||||
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
sels.Add(m_objects_model->GetItemById(selection.get_object_idx()));
|
||||||
}
|
}
|
||||||
|
@ -2217,22 +2628,18 @@ void ObjectList::update_selections_on_canvas()
|
||||||
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
|
auto add_to_selection = [this](const wxDataViewItem& item, Selection& selection, int instance_idx, bool as_single_selection)
|
||||||
{
|
{
|
||||||
const ItemType& type = m_objects_model->GetItemType(item);
|
const ItemType& type = m_objects_model->GetItemType(item);
|
||||||
if ( type == itInstanceRoot || m_objects_model->GetParent(item) == wxDataViewItem(0) ) {
|
const int obj_idx = m_objects_model->GetObjectIdByItem(item);
|
||||||
wxDataViewItem obj_item = type == itInstanceRoot ? m_objects_model->GetParent(item) : item;
|
|
||||||
selection.add_object(m_objects_model->GetIdByItem(obj_item), as_single_selection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == itVolume) {
|
if (type == itVolume) {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
|
|
||||||
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
|
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
|
||||||
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
|
selection.add_volume(obj_idx, vol_idx, std::max(instance_idx, 0), as_single_selection);
|
||||||
}
|
}
|
||||||
else if (type == itInstance) {
|
else if (type == itInstance) {
|
||||||
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
|
|
||||||
const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
|
const int inst_idx = m_objects_model->GetInstanceIdByItem(item);
|
||||||
selection.add_instance(obj_idx, inst_idx, as_single_selection);
|
selection.add_instance(obj_idx, inst_idx, as_single_selection);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
selection.add_object(obj_idx, as_single_selection);
|
||||||
};
|
};
|
||||||
|
|
||||||
// stores current instance idx before to clear the selection
|
// stores current instance idx before to clear the selection
|
||||||
|
@ -2240,7 +2647,7 @@ void ObjectList::update_selections_on_canvas()
|
||||||
|
|
||||||
if (sel_cnt == 1) {
|
if (sel_cnt == 1) {
|
||||||
wxDataViewItem item = GetSelection();
|
wxDataViewItem item = GetSelection();
|
||||||
if (m_objects_model->GetItemType(item) & (itSettings|itInstanceRoot))
|
if (m_objects_model->GetItemType(item) & (itSettings | itInstanceRoot | itLayerRoot | itLayer))
|
||||||
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true);
|
add_to_selection(m_objects_model->GetParent(item), selection, instance_idx, true);
|
||||||
else
|
else
|
||||||
add_to_selection(item, selection, instance_idx, true);
|
add_to_selection(item, selection, instance_idx, true);
|
||||||
|
@ -2303,11 +2710,13 @@ void ObjectList::select_item_all_children()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const auto item = GetSelection();
|
const auto item = GetSelection();
|
||||||
// Some volume(instance) is selected => select all volumes(instances) inside the current object
|
const ItemType item_type = m_objects_model->GetItemType(item);
|
||||||
if (m_objects_model->GetItemType(item) & (itVolume | itInstance))
|
// Some volume/layer/instance is selected => select all volumes/layers/instances inside the current object
|
||||||
|
if (item_type & (itVolume | itInstance | itLayer))
|
||||||
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
|
m_objects_model->GetChildren(m_objects_model->GetParent(item), sels);
|
||||||
|
|
||||||
m_selection_mode = m_objects_model->GetItemType(item)&itVolume ? smVolume : smInstance;
|
m_selection_mode = item_type&itVolume ? smVolume :
|
||||||
|
item_type&itLayer ? smLayer : smInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetSelections(sels);
|
SetSelections(sels);
|
||||||
|
@ -2326,8 +2735,9 @@ void ObjectList::update_selection_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
const ItemType type = m_objects_model->GetItemType(GetSelection());
|
const ItemType type = m_objects_model->GetItemType(GetSelection());
|
||||||
m_selection_mode = type&itSettings ? smUndef :
|
m_selection_mode = type & itSettings ? smUndef :
|
||||||
type&itVolume ? smVolume : smInstance;
|
type & itLayer ? smLayer :
|
||||||
|
type & itVolume ? smVolume : smInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check last selected item. If is it possible to select it
|
// check last selected item. If is it possible to select it
|
||||||
|
@ -2338,33 +2748,37 @@ bool ObjectList::check_last_selection(wxString& msg_str)
|
||||||
|
|
||||||
const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT);
|
const bool is_shift_pressed = wxGetKeyState(WXK_SHIFT);
|
||||||
|
|
||||||
/* We can't mix Parts and Objects/Instances.
|
/* We can't mix Volumes, Layers and Objects/Instances.
|
||||||
* So, show information about it
|
* So, show information about it
|
||||||
*/
|
*/
|
||||||
const ItemType type = m_objects_model->GetItemType(m_last_selected_item);
|
const ItemType type = m_objects_model->GetItemType(m_last_selected_item);
|
||||||
|
|
||||||
// check a case of a selection of the Parts from different Objects
|
// check a case of a selection of the same type items from different Objects
|
||||||
bool impossible_multipart_selection = false;
|
auto impossible_multi_selection = [type, this](const ItemType item_type, const SELECTION_MODE selection_mode) {
|
||||||
if (type & itVolume && m_selection_mode == smVolume)
|
if (!(type & item_type && m_selection_mode & selection_mode))
|
||||||
{
|
return false;
|
||||||
|
|
||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
GetSelections(sels);
|
GetSelections(sels);
|
||||||
for (const auto& sel: sels)
|
for (const auto& sel : sels)
|
||||||
if (sel != m_last_selected_item &&
|
if (sel != m_last_selected_item &&
|
||||||
m_objects_model->GetParent(sel) != m_objects_model->GetParent(m_last_selected_item))
|
m_objects_model->GetTopParent(sel) != m_objects_model->GetTopParent(m_last_selected_item))
|
||||||
{
|
return true;
|
||||||
impossible_multipart_selection = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (impossible_multipart_selection ||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (impossible_multi_selection(itVolume, smVolume) ||
|
||||||
|
impossible_multi_selection(itLayer, smLayer ) ||
|
||||||
type & itSettings ||
|
type & itSettings ||
|
||||||
type & itVolume && m_selection_mode == smInstance ||
|
type & itVolume && !(m_selection_mode & smVolume ) ||
|
||||||
!(type & itVolume) && m_selection_mode == smVolume)
|
type & itLayer && !(m_selection_mode & smLayer ) ||
|
||||||
|
type & itInstance && !(m_selection_mode & smInstance)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
// Inform user why selection isn't complited
|
// Inform user why selection isn't complited
|
||||||
const wxString item_type = m_selection_mode == smInstance ? _(L("Object or Instance")) : _(L("Part"));
|
const wxString item_type = m_selection_mode & smInstance ? _(L("Object or Instance")) :
|
||||||
|
m_selection_mode & smVolume ? _(L("Part")) : _(L("Layer"));
|
||||||
|
|
||||||
msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" +
|
msg_str = wxString::Format( _(L("Unsupported selection")) + "\n\n" +
|
||||||
_(L("You started your selection with %s Item.")) + "\n" +
|
_(L("You started your selection with %s Item.")) + "\n" +
|
||||||
|
@ -2401,7 +2815,7 @@ void ObjectList::fix_multiselection_conflicts()
|
||||||
wxDataViewItemArray sels;
|
wxDataViewItemArray sels;
|
||||||
GetSelections(sels);
|
GetSelections(sels);
|
||||||
|
|
||||||
if (m_selection_mode == smVolume)
|
if (m_selection_mode & (smVolume|smLayer))
|
||||||
{
|
{
|
||||||
// identify correct parent of the initial selected item
|
// identify correct parent of the initial selected item
|
||||||
const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front());
|
const wxDataViewItem& parent = m_objects_model->GetParent(m_last_selected_item == sels.front() ? sels.back() : sels.front());
|
||||||
|
@ -2410,8 +2824,10 @@ void ObjectList::fix_multiselection_conflicts()
|
||||||
wxDataViewItemArray children; // selected volumes from current parent
|
wxDataViewItemArray children; // selected volumes from current parent
|
||||||
m_objects_model->GetChildren(parent, children);
|
m_objects_model->GetChildren(parent, children);
|
||||||
|
|
||||||
|
const ItemType item_type = m_selection_mode & smVolume ? itVolume : itLayer;
|
||||||
|
|
||||||
for (const auto child : children)
|
for (const auto child : children)
|
||||||
if (IsSelected(child) && m_objects_model->GetItemType(child)&itVolume)
|
if (IsSelected(child) && m_objects_model->GetItemType(child) & item_type)
|
||||||
sels.Add(child);
|
sels.Add(child);
|
||||||
|
|
||||||
// If some part is selected, unselect all items except of selected parts of the current object
|
// If some part is selected, unselect all items except of selected parts of the current object
|
||||||
|
@ -2576,6 +2992,87 @@ void ObjectList::update_settings_items()
|
||||||
m_prevent_canvas_selection_update = false;
|
m_prevent_canvas_selection_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update settings item for item had it
|
||||||
|
void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections)
|
||||||
|
{
|
||||||
|
const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item);
|
||||||
|
select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item));
|
||||||
|
|
||||||
|
// If settings item was deleted from the list,
|
||||||
|
// it's need to be deleted from selection array, if it was there
|
||||||
|
if (settings_item != m_objects_model->GetSettingsItem(item) &&
|
||||||
|
selections.Index(settings_item) != wxNOT_FOUND) {
|
||||||
|
selections.Remove(settings_item);
|
||||||
|
|
||||||
|
// Select item, if settings_item doesn't exist for item anymore, but was selected
|
||||||
|
if (selections.Index(item) == wxNOT_FOUND)
|
||||||
|
selections.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ObjectList::update_object_list_by_printer_technology()
|
||||||
|
{
|
||||||
|
m_prevent_canvas_selection_update = true;
|
||||||
|
wxDataViewItemArray sel;
|
||||||
|
GetSelections(sel); // stash selection
|
||||||
|
|
||||||
|
wxDataViewItemArray object_items;
|
||||||
|
m_objects_model->GetChildren(wxDataViewItem(0), object_items);
|
||||||
|
|
||||||
|
for (auto& object_item : object_items) {
|
||||||
|
// Update Settings Item for object
|
||||||
|
update_settings_item_and_selection(object_item, sel);
|
||||||
|
|
||||||
|
// Update settings for Volumes
|
||||||
|
wxDataViewItemArray all_object_subitems;
|
||||||
|
m_objects_model->GetChildren(object_item, all_object_subitems);
|
||||||
|
for (auto item : all_object_subitems)
|
||||||
|
if (m_objects_model->GetItemType(item) & itVolume)
|
||||||
|
// update settings for volume
|
||||||
|
update_settings_item_and_selection(item, sel);
|
||||||
|
|
||||||
|
// Update Layers Items
|
||||||
|
wxDataViewItem layers_item = m_objects_model->GetLayerRootItem(object_item);
|
||||||
|
if (!layers_item)
|
||||||
|
layers_item = add_layer_root_item(object_item);
|
||||||
|
else if (printer_technology() == ptSLA) {
|
||||||
|
// If layers root item will be deleted from the list, so
|
||||||
|
// it's need to be deleted from selection array, if it was there
|
||||||
|
wxDataViewItemArray del_items;
|
||||||
|
bool some_layers_was_selected = false;
|
||||||
|
m_objects_model->GetAllChildren(layers_item, del_items);
|
||||||
|
for (auto& del_item:del_items)
|
||||||
|
if (sel.Index(del_item) != wxNOT_FOUND) {
|
||||||
|
some_layers_was_selected = true;
|
||||||
|
sel.Remove(del_item);
|
||||||
|
}
|
||||||
|
if (sel.Index(layers_item) != wxNOT_FOUND) {
|
||||||
|
some_layers_was_selected = true;
|
||||||
|
sel.Remove(layers_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete all "layers" items
|
||||||
|
m_objects_model->Delete(layers_item);
|
||||||
|
|
||||||
|
// Select object_item, if layers_item doesn't exist for item anymore, but was some of layer items was/were selected
|
||||||
|
if (some_layers_was_selected)
|
||||||
|
sel.Add(object_item);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wxDataViewItemArray all_obj_layers;
|
||||||
|
m_objects_model->GetChildren(layers_item, all_obj_layers);
|
||||||
|
|
||||||
|
for (auto item : all_obj_layers)
|
||||||
|
// update settings for layer
|
||||||
|
update_settings_item_and_selection(item, sel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore selection:
|
||||||
|
SetSelections(sel);
|
||||||
|
m_prevent_canvas_selection_update = false;
|
||||||
|
}
|
||||||
|
|
||||||
void ObjectList::update_object_menu()
|
void ObjectList::update_object_menu()
|
||||||
{
|
{
|
||||||
append_menu_items_add_volume(&m_menu_object);
|
append_menu_items_add_volume(&m_menu_object);
|
||||||
|
@ -2749,7 +3246,8 @@ void ObjectList::msw_rescale()
|
||||||
for (MenuWithSeparators* menu : { &m_menu_object,
|
for (MenuWithSeparators* menu : { &m_menu_object,
|
||||||
&m_menu_part,
|
&m_menu_part,
|
||||||
&m_menu_sla_object,
|
&m_menu_sla_object,
|
||||||
&m_menu_instance })
|
&m_menu_instance,
|
||||||
|
&m_menu_layer })
|
||||||
msw_rescale_menu(menu);
|
msw_rescale_menu(menu);
|
||||||
|
|
||||||
Layout();
|
Layout();
|
||||||
|
@ -2862,5 +3360,13 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const
|
||||||
wxGetApp().plater()->update();
|
wxGetApp().plater()->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModelObject* ObjectList::object(const int obj_idx) const
|
||||||
|
{
|
||||||
|
if (obj_idx < 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return (*m_objects)[obj_idx];
|
||||||
|
}
|
||||||
|
|
||||||
} //namespace GUI
|
} //namespace GUI
|
||||||
} //namespace Slic3r
|
} //namespace Slic3r
|
|
@ -33,6 +33,10 @@ typedef std::map< std::string, std::vector< std::pair<std::string, std::string>
|
||||||
|
|
||||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||||
|
|
||||||
|
typedef double coordf_t;
|
||||||
|
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||||
|
typedef std::map<t_layer_height_range, DynamicPrintConfig> t_layer_config_ranges;
|
||||||
|
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||||
|
@ -64,9 +68,10 @@ class ObjectList : public wxDataViewCtrl
|
||||||
{
|
{
|
||||||
enum SELECTION_MODE
|
enum SELECTION_MODE
|
||||||
{
|
{
|
||||||
smUndef,
|
smUndef = 0,
|
||||||
smVolume,
|
smVolume = 1,
|
||||||
smInstance
|
smInstance = 2,
|
||||||
|
smLayer = 4
|
||||||
} m_selection_mode {smUndef};
|
} m_selection_mode {smUndef};
|
||||||
|
|
||||||
struct dragged_item_data
|
struct dragged_item_data
|
||||||
|
@ -119,12 +124,17 @@ class ObjectList : public wxDataViewCtrl
|
||||||
MenuWithSeparators m_menu_part;
|
MenuWithSeparators m_menu_part;
|
||||||
MenuWithSeparators m_menu_sla_object;
|
MenuWithSeparators m_menu_sla_object;
|
||||||
MenuWithSeparators m_menu_instance;
|
MenuWithSeparators m_menu_instance;
|
||||||
wxMenuItem* m_menu_item_split { nullptr };
|
MenuWithSeparators m_menu_layer;
|
||||||
wxMenuItem* m_menu_item_split_part { nullptr };
|
|
||||||
wxMenuItem* m_menu_item_settings { nullptr };
|
wxMenuItem* m_menu_item_settings { nullptr };
|
||||||
wxMenuItem* m_menu_item_split_instances { nullptr };
|
wxMenuItem* m_menu_item_split_instances { nullptr };
|
||||||
|
|
||||||
std::vector<wxBitmap*> m_bmp_vector;
|
ObjectDataViewModel *m_objects_model{ nullptr };
|
||||||
|
DynamicPrintConfig *m_config {nullptr};
|
||||||
|
std::vector<ModelObject*> *m_objects{ nullptr };
|
||||||
|
|
||||||
|
std::vector<wxBitmap*> m_bmp_vector;
|
||||||
|
|
||||||
|
t_layer_config_ranges m_layer_config_ranges_cache;
|
||||||
|
|
||||||
int m_selected_object_id = -1;
|
int m_selected_object_id = -1;
|
||||||
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
|
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
|
||||||
|
@ -153,11 +163,11 @@ public:
|
||||||
|
|
||||||
std::map<std::string, wxBitmap> CATEGORY_ICON;
|
std::map<std::string, wxBitmap> CATEGORY_ICON;
|
||||||
|
|
||||||
ObjectDataViewModel *m_objects_model{ nullptr };
|
ObjectDataViewModel* GetModel() const { return m_objects_model; }
|
||||||
DynamicPrintConfig *m_config {nullptr};
|
DynamicPrintConfig* config() const { return m_config; }
|
||||||
|
std::vector<ModelObject*>* objects() const { return m_objects; }
|
||||||
std::vector<ModelObject*> *m_objects{ nullptr };
|
|
||||||
|
|
||||||
|
ModelObject* object(const int obj_idx) const ;
|
||||||
|
|
||||||
void create_objects_ctrl();
|
void create_objects_ctrl();
|
||||||
void create_popup_menus();
|
void create_popup_menus();
|
||||||
|
@ -192,6 +202,9 @@ public:
|
||||||
void key_event(wxKeyEvent& event);
|
void key_event(wxKeyEvent& event);
|
||||||
#endif /* __WXOSX__ */
|
#endif /* __WXOSX__ */
|
||||||
|
|
||||||
|
void copy();
|
||||||
|
void paste();
|
||||||
|
|
||||||
void get_settings_choice(const wxString& category_name);
|
void get_settings_choice(const wxString& category_name);
|
||||||
void get_freq_settings_choice(const wxString& bundle_name);
|
void get_freq_settings_choice(const wxString& bundle_name);
|
||||||
void update_settings_item();
|
void update_settings_item();
|
||||||
|
@ -199,6 +212,7 @@ public:
|
||||||
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
|
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
|
||||||
void append_menu_items_add_volume(wxMenu* menu);
|
void append_menu_items_add_volume(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_split(wxMenu* menu);
|
wxMenuItem* append_menu_item_split(wxMenu* menu);
|
||||||
|
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
||||||
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
|
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
|
||||||
|
@ -222,10 +236,17 @@ public:
|
||||||
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
||||||
void del_object(const int obj_idx);
|
void del_object(const int obj_idx);
|
||||||
void del_subobject_item(wxDataViewItem& item);
|
void del_subobject_item(wxDataViewItem& item);
|
||||||
void del_settings_from_config();
|
void del_settings_from_config(const wxDataViewItem& parent_item);
|
||||||
void del_instances_from_object(const int obj_idx);
|
void del_instances_from_object(const int obj_idx);
|
||||||
|
void del_layer_from_object(const int obj_idx, const t_layer_height_range& layer_range);
|
||||||
|
void del_layers_from_object(const int obj_idx);
|
||||||
bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
|
bool del_subobject_from_object(const int obj_idx, const int idx, const int type);
|
||||||
void split();
|
void split();
|
||||||
|
void layers_editing();
|
||||||
|
|
||||||
|
wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item);
|
||||||
|
|
||||||
|
DynamicPrintConfig get_default_layer_config(const int obj_idx);
|
||||||
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
|
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
|
||||||
bool is_splittable();
|
bool is_splittable();
|
||||||
bool selected_instances_of_same_object();
|
bool selected_instances_of_same_object();
|
||||||
|
@ -265,6 +286,14 @@ public:
|
||||||
|
|
||||||
// Remove objects/sub-object from the list
|
// Remove objects/sub-object from the list
|
||||||
void remove();
|
void remove();
|
||||||
|
void del_layer_range(const t_layer_height_range& range);
|
||||||
|
void add_layer_range_after_current(const t_layer_height_range& current_range);
|
||||||
|
void add_layer_item (const t_layer_height_range& range,
|
||||||
|
const wxDataViewItem layers_item,
|
||||||
|
const int layer_idx = -1);
|
||||||
|
bool edit_layer_range(const t_layer_height_range& range, coordf_t layer_height);
|
||||||
|
bool edit_layer_range(const t_layer_height_range& range,
|
||||||
|
const t_layer_height_range& new_range);
|
||||||
|
|
||||||
void init_objects();
|
void init_objects();
|
||||||
bool multiple_selection() const ;
|
bool multiple_selection() const ;
|
||||||
|
@ -286,6 +315,8 @@ public:
|
||||||
void last_volume_is_deleted(const int obj_idx);
|
void last_volume_is_deleted(const int obj_idx);
|
||||||
bool has_multi_part_objects();
|
bool has_multi_part_objects();
|
||||||
void update_settings_items();
|
void update_settings_items();
|
||||||
|
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
|
||||||
|
void update_object_list_by_printer_technology();
|
||||||
void update_object_menu();
|
void update_object_menu();
|
||||||
|
|
||||||
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
||||||
|
@ -295,6 +326,8 @@ public:
|
||||||
void fix_through_netfabb();
|
void fix_through_netfabb();
|
||||||
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
void update_item_error_icon(const int obj_idx, int vol_idx) const ;
|
||||||
|
|
||||||
|
void fill_layer_config_ranges_cache();
|
||||||
|
void paste_layers_into_list();
|
||||||
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
|
void paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& volumes);
|
||||||
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
|
void paste_objects_into_list(const std::vector<size_t>& object_idxs);
|
||||||
|
|
||||||
|
|
|
@ -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,8 +721,8 @@ 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() :
|
||||||
(*wxGetApp().model_objects())[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size();
|
(*wxGetApp().model_objects())[selection.get_volume(*selection.get_volume_idxs().begin())->object_idx()]->raw_mesh_bounding_box().size();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,23 +573,13 @@ 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);
|
reload_print();
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
|
// Find an index of a value in a sorted vector, which is in <z-eps, z+eps>.
|
||||||
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "GUI_ObjectManipulation.hpp"
|
#include "GUI_ObjectManipulation.hpp"
|
||||||
|
#include "GUI_ObjectLayers.hpp"
|
||||||
#include "GUI_Utils.hpp"
|
#include "GUI_Utils.hpp"
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
|
@ -618,6 +619,7 @@ struct Sidebar::priv
|
||||||
ObjectList *object_list{ nullptr };
|
ObjectList *object_list{ nullptr };
|
||||||
ObjectManipulation *object_manipulation{ nullptr };
|
ObjectManipulation *object_manipulation{ nullptr };
|
||||||
ObjectSettings *object_settings{ nullptr };
|
ObjectSettings *object_settings{ nullptr };
|
||||||
|
ObjectLayers *object_layers{ nullptr };
|
||||||
ObjectInfo *object_info;
|
ObjectInfo *object_info;
|
||||||
SlicedInfo *sliced_info;
|
SlicedInfo *sliced_info;
|
||||||
|
|
||||||
|
@ -641,6 +643,9 @@ Sidebar::priv::~priv()
|
||||||
|
|
||||||
if (frequently_changed_parameters != nullptr)
|
if (frequently_changed_parameters != nullptr)
|
||||||
delete frequently_changed_parameters;
|
delete frequently_changed_parameters;
|
||||||
|
|
||||||
|
if (object_layers != nullptr)
|
||||||
|
delete object_layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sidebar::priv::show_preset_comboboxes()
|
void Sidebar::priv::show_preset_comboboxes()
|
||||||
|
@ -749,6 +754,11 @@ Sidebar::Sidebar(Plater *parent)
|
||||||
p->object_settings = new ObjectSettings(p->scrolled);
|
p->object_settings = new ObjectSettings(p->scrolled);
|
||||||
p->object_settings->Hide();
|
p->object_settings->Hide();
|
||||||
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
|
p->sizer_params->Add(p->object_settings->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
|
||||||
|
|
||||||
|
// Object Layers
|
||||||
|
p->object_layers = new ObjectLayers(p->scrolled);
|
||||||
|
p->object_layers->Hide();
|
||||||
|
p->sizer_params->Add(p->object_layers->get_sizer(), 0, wxEXPAND | wxTOP, margin_5);
|
||||||
|
|
||||||
// Info boxes
|
// Info boxes
|
||||||
p->object_info = new ObjectInfo(p->scrolled);
|
p->object_info = new ObjectInfo(p->scrolled);
|
||||||
|
@ -942,6 +952,7 @@ void Sidebar::msw_rescale()
|
||||||
p->object_list->msw_rescale();
|
p->object_list->msw_rescale();
|
||||||
p->object_manipulation->msw_rescale();
|
p->object_manipulation->msw_rescale();
|
||||||
p->object_settings->msw_rescale();
|
p->object_settings->msw_rescale();
|
||||||
|
p->object_layers->msw_rescale();
|
||||||
|
|
||||||
p->object_info->msw_rescale();
|
p->object_info->msw_rescale();
|
||||||
|
|
||||||
|
@ -963,6 +974,11 @@ ObjectSettings* Sidebar::obj_settings()
|
||||||
return p->object_settings;
|
return p->object_settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectLayers* Sidebar::obj_layers()
|
||||||
|
{
|
||||||
|
return p->object_layers;
|
||||||
|
}
|
||||||
|
|
||||||
wxScrolledWindow* Sidebar::scrolled_panel()
|
wxScrolledWindow* Sidebar::scrolled_panel()
|
||||||
{
|
{
|
||||||
return p->scrolled;
|
return p->scrolled;
|
||||||
|
@ -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(); }
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,61 +1074,68 @@ void Selection::render_center(bool gizmo_is_dragging) const
|
||||||
}
|
}
|
||||||
#endif // ENABLE_RENDER_SELECTION_CENTER
|
#endif // ENABLE_RENDER_SELECTION_CENTER
|
||||||
|
|
||||||
void Selection::render_sidebar_hints(const std::string& sidebar_field) const
|
void Selection::render_sidebar_hints(const std::string& sidebar_field, const Shader& shader) const
|
||||||
{
|
{
|
||||||
if (sidebar_field.empty())
|
if (sidebar_field.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
if (!boost::starts_with(sidebar_field, "layer"))
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
{
|
||||||
|
shader.start_using();
|
||||||
|
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||||
|
glsafe(::glEnable(GL_LIGHTING));
|
||||||
|
}
|
||||||
|
|
||||||
glsafe(::glEnable(GL_LIGHTING));
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
|
|
||||||
glsafe(::glPushMatrix());
|
glsafe(::glPushMatrix());
|
||||||
|
|
||||||
const Vec3d& center = get_bounding_box().center();
|
if (!boost::starts_with(sidebar_field, "layer"))
|
||||||
|
|
||||||
if (is_single_full_instance() && ! wxGetApp().obj_manipul()->get_world_coordinates())
|
|
||||||
{
|
{
|
||||||
glsafe(::glTranslated(center(0), center(1), center(2)));
|
const Vec3d& center = get_bounding_box().center();
|
||||||
if (!boost::starts_with(sidebar_field, "position"))
|
|
||||||
|
if (is_single_full_instance() && !wxGetApp().obj_manipul()->get_world_coordinates())
|
||||||
{
|
{
|
||||||
Transform3d orient_matrix = Transform3d::Identity();
|
glsafe(::glTranslated(center(0), center(1), center(2)));
|
||||||
if (boost::starts_with(sidebar_field, "scale"))
|
if (!boost::starts_with(sidebar_field, "position"))
|
||||||
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
|
||||||
else if (boost::starts_with(sidebar_field, "rotation"))
|
|
||||||
{
|
{
|
||||||
if (boost::ends_with(sidebar_field, "x"))
|
Transform3d orient_matrix = Transform3d::Identity();
|
||||||
|
if (boost::starts_with(sidebar_field, "scale"))
|
||||||
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
else if (boost::ends_with(sidebar_field, "y"))
|
else if (boost::starts_with(sidebar_field, "rotation"))
|
||||||
{
|
{
|
||||||
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
|
if (boost::ends_with(sidebar_field, "x"))
|
||||||
if (rotation(0) == 0.0)
|
|
||||||
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
else
|
else if (boost::ends_with(sidebar_field, "y"))
|
||||||
orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
|
{
|
||||||
|
const Vec3d& rotation = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation();
|
||||||
|
if (rotation(0) == 0.0)
|
||||||
|
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
|
else
|
||||||
|
orient_matrix.rotate(Eigen::AngleAxisd(rotation(2), Vec3d::UnitZ()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glsafe(::glMultMatrixd(orient_matrix.data()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (is_single_volume() || is_single_modifier())
|
||||||
|
{
|
||||||
|
glsafe(::glTranslated(center(0), center(1), center(2)));
|
||||||
|
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
|
if (!boost::starts_with(sidebar_field, "position"))
|
||||||
|
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
|
||||||
|
|
||||||
glsafe(::glMultMatrixd(orient_matrix.data()));
|
glsafe(::glMultMatrixd(orient_matrix.data()));
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
else if (is_single_volume() || is_single_modifier())
|
|
||||||
{
|
|
||||||
glsafe(::glTranslated(center(0), center(1), center(2)));
|
|
||||||
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
|
||||||
if (!boost::starts_with(sidebar_field, "position"))
|
|
||||||
orient_matrix = orient_matrix * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true);
|
|
||||||
|
|
||||||
glsafe(::glMultMatrixd(orient_matrix.data()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
glsafe(::glTranslated(center(0), center(1), center(2)));
|
|
||||||
if (requires_local_axes())
|
|
||||||
{
|
{
|
||||||
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
glsafe(::glTranslated(center(0), center(1), center(2)));
|
||||||
glsafe(::glMultMatrixd(orient_matrix.data()));
|
if (requires_local_axes())
|
||||||
|
{
|
||||||
|
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
|
||||||
|
glsafe(::glMultMatrixd(orient_matrix.data()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1136,10 +1147,16 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
|
||||||
render_sidebar_scale_hints(sidebar_field);
|
render_sidebar_scale_hints(sidebar_field);
|
||||||
else if (boost::starts_with(sidebar_field, "size"))
|
else if (boost::starts_with(sidebar_field, "size"))
|
||||||
render_sidebar_size_hints(sidebar_field);
|
render_sidebar_size_hints(sidebar_field);
|
||||||
|
else if (boost::starts_with(sidebar_field, "layer"))
|
||||||
|
render_sidebar_layers_hints(sidebar_field);
|
||||||
|
|
||||||
glsafe(::glPopMatrix());
|
glsafe(::glPopMatrix());
|
||||||
|
|
||||||
glsafe(::glDisable(GL_LIGHTING));
|
if (!boost::starts_with(sidebar_field, "layer"))
|
||||||
|
{
|
||||||
|
glsafe(::glDisable(GL_LIGHTING));
|
||||||
|
shader.stop_using();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Selection::requires_local_axes() const
|
bool Selection::requires_local_axes() const
|
||||||
|
@ -1163,7 +1180,7 @@ void Selection::copy_to_clipboard()
|
||||||
dst_object->config = src_object->config;
|
dst_object->config = src_object->config;
|
||||||
dst_object->sla_support_points = src_object->sla_support_points;
|
dst_object->sla_support_points = src_object->sla_support_points;
|
||||||
dst_object->sla_points_status = src_object->sla_points_status;
|
dst_object->sla_points_status = src_object->sla_points_status;
|
||||||
dst_object->layer_height_ranges = src_object->layer_height_ranges;
|
dst_object->layer_config_ranges = src_object->layer_config_ranges; // #ys_FIXME_experiment
|
||||||
dst_object->layer_height_profile = src_object->layer_height_profile;
|
dst_object->layer_height_profile = src_object->layer_height_profile;
|
||||||
dst_object->origin_translation = src_object->origin_translation;
|
dst_object->origin_translation = src_object->origin_translation;
|
||||||
|
|
||||||
|
@ -1709,6 +1726,78 @@ void Selection::render_sidebar_size_hints(const std::string& sidebar_field) cons
|
||||||
render_sidebar_scale_hints(sidebar_field);
|
render_sidebar_scale_hints(sidebar_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) const
|
||||||
|
{
|
||||||
|
static const double Margin = 10.0;
|
||||||
|
|
||||||
|
std::string field = sidebar_field;
|
||||||
|
|
||||||
|
// extract max_z
|
||||||
|
std::string::size_type pos = field.rfind("_");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double max_z = std::stod(field.substr(pos + 1));
|
||||||
|
|
||||||
|
// extract min_z
|
||||||
|
field = field.substr(0, pos);
|
||||||
|
pos = field.rfind("_");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
double min_z = std::stod(field.substr(pos + 1));
|
||||||
|
|
||||||
|
// extract type
|
||||||
|
field = field.substr(0, pos);
|
||||||
|
pos = field.rfind("_");
|
||||||
|
if (pos == std::string::npos)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int type = std::stoi(field.substr(pos + 1));
|
||||||
|
|
||||||
|
const BoundingBoxf3& box = get_bounding_box();
|
||||||
|
|
||||||
|
const float min_x = box.min(0) - Margin;
|
||||||
|
const float max_x = box.max(0) + Margin;
|
||||||
|
const float min_y = box.min(1) - Margin;
|
||||||
|
const float max_y = box.max(1) + Margin;
|
||||||
|
|
||||||
|
// view dependend order of rendering to keep correct transparency
|
||||||
|
bool camera_on_top = wxGetApp().plater()->get_camera().get_theta() <= 90.0f;
|
||||||
|
float z1 = camera_on_top ? min_z : max_z;
|
||||||
|
float z2 = camera_on_top ? max_z : min_z;
|
||||||
|
|
||||||
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
|
glsafe(::glDisable(GL_CULL_FACE));
|
||||||
|
glsafe(::glEnable(GL_BLEND));
|
||||||
|
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
|
||||||
|
|
||||||
|
::glBegin(GL_QUADS);
|
||||||
|
if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2)))
|
||||||
|
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
|
||||||
|
else
|
||||||
|
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
|
||||||
|
::glVertex3f(min_x, min_y, z1);
|
||||||
|
::glVertex3f(max_x, min_y, z1);
|
||||||
|
::glVertex3f(max_x, max_y, z1);
|
||||||
|
::glVertex3f(min_x, max_y, z1);
|
||||||
|
glsafe(::glEnd());
|
||||||
|
|
||||||
|
::glBegin(GL_QUADS);
|
||||||
|
if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1)))
|
||||||
|
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
|
||||||
|
else
|
||||||
|
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
|
||||||
|
::glVertex3f(min_x, min_y, z2);
|
||||||
|
::glVertex3f(max_x, min_y, z2);
|
||||||
|
::glVertex3f(max_x, max_y, z2);
|
||||||
|
::glVertex3f(min_x, max_y, z2);
|
||||||
|
glsafe(::glEnd());
|
||||||
|
|
||||||
|
glsafe(::glEnable(GL_CULL_FACE));
|
||||||
|
glsafe(::glDisable(GL_BLEND));
|
||||||
|
}
|
||||||
|
|
||||||
void Selection::render_sidebar_position_hint(Axis axis) const
|
void Selection::render_sidebar_position_hint(Axis axis) const
|
||||||
{
|
{
|
||||||
m_arrow.set_color(AXES_COLOR[axis], 3);
|
m_arrow.set_color(AXES_COLOR[axis], 3);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,12 +3581,10 @@ 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");
|
||||||
double head_width = m_config->opt_float("support_head_width");
|
double head_width = m_config->opt_float("support_head_width");
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@ namespace Slic3r {
|
||||||
enum class ModelVolumeType : int;
|
enum class ModelVolumeType : int;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef double coordf_t;
|
||||||
|
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
void msw_rescale_menu(wxMenu* menu);
|
void msw_rescale_menu(wxMenu* menu);
|
||||||
#else /* __WXMSW__ */
|
#else /* __WXMSW__ */
|
||||||
|
@ -159,12 +162,14 @@ DECLARE_VARIANT_OBJECT(DataViewBitmapText)
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
enum ItemType {
|
enum ItemType {
|
||||||
itUndef = 0,
|
itUndef = 0,
|
||||||
itObject = 1,
|
itObject = 1,
|
||||||
itVolume = 2,
|
itVolume = 2,
|
||||||
itInstanceRoot = 4,
|
itInstanceRoot = 4,
|
||||||
itInstance = 8,
|
itInstance = 8,
|
||||||
itSettings = 16
|
itSettings = 16,
|
||||||
|
itLayerRoot = 32,
|
||||||
|
itLayer = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ObjectDataViewModelNode;
|
class ObjectDataViewModelNode;
|
||||||
|
@ -177,6 +182,7 @@ class ObjectDataViewModelNode
|
||||||
wxBitmap m_empty_bmp;
|
wxBitmap m_empty_bmp;
|
||||||
size_t m_volumes_cnt = 0;
|
size_t m_volumes_cnt = 0;
|
||||||
std::vector< std::string > m_opt_categories;
|
std::vector< std::string > m_opt_categories;
|
||||||
|
t_layer_height_range m_layer_range = { 0.0f, 0.0f };
|
||||||
|
|
||||||
wxString m_name;
|
wxString m_name;
|
||||||
wxBitmap& m_bmp = m_empty_bmp;
|
wxBitmap& m_bmp = m_empty_bmp;
|
||||||
|
@ -229,6 +235,11 @@ public:
|
||||||
set_action_icon();
|
set_action_icon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ObjectDataViewModelNode(ObjectDataViewModelNode* parent,
|
||||||
|
const t_layer_height_range& layer_range,
|
||||||
|
const int idx = -1,
|
||||||
|
const wxString& extruder = wxEmptyString );
|
||||||
|
|
||||||
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
|
ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type);
|
||||||
|
|
||||||
~ObjectDataViewModelNode()
|
~ObjectDataViewModelNode()
|
||||||
|
@ -318,6 +329,7 @@ public:
|
||||||
ItemType GetType() const { return m_type; }
|
ItemType GetType() const { return m_type; }
|
||||||
void SetIdx(const int& idx);
|
void SetIdx(const int& idx);
|
||||||
int GetIdx() const { return m_idx; }
|
int GetIdx() const { return m_idx; }
|
||||||
|
t_layer_height_range GetLayerRange() const { return m_layer_range; }
|
||||||
|
|
||||||
// use this function only for childrens
|
// use this function only for childrens
|
||||||
void AssignAllVal(ObjectDataViewModelNode& from_node)
|
void AssignAllVal(ObjectDataViewModelNode& from_node)
|
||||||
|
@ -348,7 +360,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set action icons for node
|
// Set action icons for node
|
||||||
void set_action_icon();
|
void set_action_icon();
|
||||||
|
|
||||||
void update_settings_digest_bitmaps();
|
void update_settings_digest_bitmaps();
|
||||||
bool update_settings_digest(const std::vector<std::string>& categories);
|
bool update_settings_digest(const std::vector<std::string>& categories);
|
||||||
|
@ -388,6 +400,11 @@ public:
|
||||||
const bool create_frst_child = true);
|
const bool create_frst_child = true);
|
||||||
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
|
||||||
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
|
wxDataViewItem AddInstanceChild(const wxDataViewItem &parent_item, size_t num);
|
||||||
|
wxDataViewItem AddLayersRoot(const wxDataViewItem &parent_item);
|
||||||
|
wxDataViewItem AddLayersChild( const wxDataViewItem &parent_item,
|
||||||
|
const t_layer_height_range& layer_range,
|
||||||
|
const int extruder = 0,
|
||||||
|
const int index = -1);
|
||||||
wxDataViewItem Delete(const wxDataViewItem &item);
|
wxDataViewItem Delete(const wxDataViewItem &item);
|
||||||
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
|
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
|
||||||
void DeleteAll();
|
void DeleteAll();
|
||||||
|
@ -395,13 +412,18 @@ public:
|
||||||
void DeleteVolumeChildren(wxDataViewItem& parent);
|
void DeleteVolumeChildren(wxDataViewItem& parent);
|
||||||
void DeleteSettings(const wxDataViewItem& parent);
|
void DeleteSettings(const wxDataViewItem& parent);
|
||||||
wxDataViewItem GetItemById(int obj_idx);
|
wxDataViewItem GetItemById(int obj_idx);
|
||||||
|
wxDataViewItem GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type);
|
||||||
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
|
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
|
||||||
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
|
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
|
||||||
|
wxDataViewItem GetItemByLayerId(int obj_idx, int layer_idx);
|
||||||
|
wxDataViewItem GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
|
||||||
|
int GetItemIdByLayerRange(const int obj_idx, const t_layer_height_range& layer_range);
|
||||||
int GetIdByItem(const wxDataViewItem& item) const;
|
int GetIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
|
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
|
||||||
int GetObjectIdByItem(const wxDataViewItem& item) const;
|
int GetObjectIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
int GetVolumeIdByItem(const wxDataViewItem& item) const;
|
||||||
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
int GetInstanceIdByItem(const wxDataViewItem& item) const;
|
||||||
|
int GetLayerIdByItem(const wxDataViewItem& item) const;
|
||||||
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
void GetItemInfo(const wxDataViewItem& item, ItemType& type, int& obj_idx, int& idx);
|
||||||
int GetRowByItem(const wxDataViewItem& item) const;
|
int GetRowByItem(const wxDataViewItem& item) const;
|
||||||
bool IsEmpty() { return m_objects.empty(); }
|
bool IsEmpty() { return m_objects.empty(); }
|
||||||
|
@ -450,6 +472,7 @@ public:
|
||||||
ItemType type) const;
|
ItemType type) const;
|
||||||
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
|
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
|
||||||
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
|
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
|
||||||
|
wxDataViewItem GetLayerRootItem(const wxDataViewItem &item) const;
|
||||||
bool IsSettingsItem(const wxDataViewItem &item) const;
|
bool IsSettingsItem(const wxDataViewItem &item) const;
|
||||||
void UpdateSettingsDigest( const wxDataViewItem &item,
|
void UpdateSettingsDigest( const wxDataViewItem &item,
|
||||||
const std::vector<std::string>& categories);
|
const std::vector<std::string>& categories);
|
||||||
|
@ -465,6 +488,7 @@ public:
|
||||||
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
|
wxBitmap GetVolumeIcon(const Slic3r::ModelVolumeType vol_type,
|
||||||
const bool is_marked = false);
|
const bool is_marked = false);
|
||||||
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
|
void DeleteWarningIcon(const wxDataViewItem& item, const bool unmark_object = false);
|
||||||
|
t_layer_height_range GetLayerRangeByItem(const wxDataViewItem& item) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -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,10 +459,16 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
23
t/print.t
23
t/print.t
|
@ -34,25 +34,30 @@ use Slic3r::Test;
|
||||||
{
|
{
|
||||||
# this represents the aggregate config from presets
|
# this represents the aggregate config from presets
|
||||||
my $config = Slic3r::Config::new_from_defaults;
|
my $config = Slic3r::Config::new_from_defaults;
|
||||||
|
# Define 4 extruders.
|
||||||
|
$config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]);
|
||||||
|
|
||||||
# user adds one object to the plater
|
# user adds one object to the plater
|
||||||
my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config);
|
my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config);
|
||||||
|
|
||||||
# user sets a per-region option
|
# user sets a per-region option
|
||||||
$print->print->objects->[0]->model_object->config->set('fill_density', 100);
|
my $model2 = $model->clone;
|
||||||
$print->print->reload_object(0);
|
$model2->get_object(0)->config->set('fill_density', 100);
|
||||||
|
$print->apply($model2, $config);
|
||||||
|
|
||||||
is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config';
|
is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config';
|
||||||
|
|
||||||
# user exports G-code, thus the default config is reapplied
|
# user exports G-code, thus the default config is reapplied
|
||||||
$print->print->apply_config_perl_tests_only($config);
|
$model2->get_object(0)->config->erase('fill_density');
|
||||||
|
$print->apply($model2, $config);
|
||||||
is $print->print->regions->[0]->config->fill_density, 100, 'apply_config() does not override per-object settings';
|
|
||||||
|
is $print->print->regions->[0]->config->fill_density, 20, 'region config is resetted';
|
||||||
|
|
||||||
# user assigns object extruders
|
# user assigns object extruders
|
||||||
$print->print->objects->[0]->model_object->config->set('extruder', 3);
|
$model2->get_object(0)->config->set('extruder', 3);
|
||||||
$print->print->objects->[0]->model_object->config->set('perimeter_extruder', 2);
|
$model2->get_object(0)->config->set('perimeter_extruder', 2);
|
||||||
$print->print->reload_object(0);
|
$print->apply($model2, $config);
|
||||||
|
|
||||||
is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded';
|
is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded';
|
||||||
is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders';
|
is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders';
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = ();
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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%{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue