Merge remote-tracking branch 'origin/dev_native' into tm_sla_supports

This commit is contained in:
tamasmeszaros 2018-11-07 15:30:41 +01:00
commit 97b3d94760
55 changed files with 1903 additions and 808 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -173,7 +173,7 @@ extern void stl_mirror_xy(stl_file *stl);
extern void stl_mirror_yz(stl_file *stl);
extern void stl_mirror_xz(stl_file *stl);
extern void stl_transform(stl_file *stl, float *trafo3x4);
extern void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t);
extern void stl_transform(stl_file *stl, const Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>& t);
extern void stl_open_merge(stl_file *stl, char *file);
extern void stl_invalidate_shared_vertices(stl_file *stl);
extern void stl_generate_shared_vertices(stl_file *stl);
@ -189,7 +189,7 @@ inline void stl_normalize_vector(stl_normal &normal) {
if (length < 0.000000000001)
normal = stl_normal::Zero();
else
normal *= (1.0 / length);
normal *= float(1.0 / length);
}
inline bool stl_vertex_lower(const stl_vertex &a, const stl_vertex &b) {
return (a(0) != b(0)) ? (a(0) < b(0)) :

View File

@ -155,7 +155,7 @@ void stl_transform(stl_file *stl, float *trafo3x4) {
calculate_normals(stl);
}
void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>& t)
void stl_transform(stl_file *stl, const Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>& t)
{
if (stl->error)
return;
@ -178,7 +178,7 @@ void stl_transform(stl_file *stl, const Eigen::Transform<float, 3, Eigen::Affine
}
Eigen::MatrixXf dst_vertices(3, vertices_count);
dst_vertices = t * src_vertices.colwise().homogeneous();
dst_vertices = t.cast<float>() * src_vertices.colwise().homogeneous();
facet_ptr = stl->facet_start;
v_id = 0;

View File

@ -439,14 +439,16 @@ void ConfigBase::load_from_gcode_file(const std::string &file)
ifs.read(data.data(), data_length);
ifs.close();
load_from_gcode_string(data.data());
size_t key_value_pairs = load_from_gcode_string(data.data());
if (key_value_pairs < 80)
throw std::runtime_error((boost::format("Suspiciously low number of configuration values extracted from %1: %2") % file % key_value_pairs).str());
}
// Load the config keys from the given string.
void ConfigBase::load_from_gcode_string(const char* str)
size_t ConfigBase::load_from_gcode_string(const char* str)
{
if (str == nullptr)
return;
return 0;
// Walk line by line in reverse until a non-configuration key appears.
char *data_start = const_cast<char*>(str);
@ -497,11 +499,8 @@ void ConfigBase::load_from_gcode_string(const char* str)
}
end = start;
}
if (num_key_value_pairs < 90) {
char msg[80];
sprintf(msg, "Suspiciously low number of configuration values extracted: %d", num_key_value_pairs);
throw std::runtime_error(msg);
}
return num_key_value_pairs;
}
void ConfigBase::save(const std::string &file) const

View File

@ -1119,7 +1119,8 @@ public:
void load(const std::string &file);
void load_from_ini(const std::string &file);
void load_from_gcode_file(const std::string &file);
void load_from_gcode_string(const char* str);
// Returns number of key/value pairs extracted.
size_t load_from_gcode_string(const char* str);
void load(const boost::property_tree::ptree &tree);
void save(const std::string &file) const;
@ -1237,6 +1238,7 @@ public:
ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override;
// Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store.
t_config_option_keys keys() const override;
bool empty() const { return options.empty(); }
// Set a value for an opt_key. Returns true if the value did not exist yet.
// This DynamicConfig will take ownership of opt.

View File

@ -247,7 +247,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Only concentric fills are not sorted.
eec->no_sort = f->no_sort();
extrusion_entities_append_paths(
eec->entities, STDMOVE(polylines),
eec->entities, std::move(polylines),
is_bridge ?
erBridgeInfill :
(surface.is_solid() ?

View File

@ -813,7 +813,7 @@ namespace Slic3r {
std::vector<Vec3f> sla_support_points;
for (unsigned int i=0; i<object_data_points.size(); i+=3)
sla_support_points.push_back(Vec3f(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())));
sla_support_points.push_back(Vec3d(std::atof(object_data_points[i+0].c_str()), std::atof(object_data_points[i+1].c_str()), std::atof(object_data_points[i+2].c_str())).cast<float>());
if (!sla_support_points.empty())
m_sla_support_points.insert(IdToSlaSupportPointsMap::value_type(object_id, sla_support_points));
@ -1608,10 +1608,10 @@ namespace Slic3r {
IdToObjectDataMap m_objects_data;
public:
bool save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config);
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
private:
bool _save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config);
bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
bool _add_relationships_file_to_archive(mz_zip_archive& archive);
bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model);
@ -1620,17 +1620,17 @@ namespace Slic3r {
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_sla_support_points_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print);
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);
};
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config)
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config)
{
clear_errors();
return _save_model_to_file(filename, model, print, export_print_config);
return _save_model_to_file(filename, model, config);
}
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config)
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
@ -1685,9 +1685,9 @@ namespace Slic3r {
}
// adds slic3r print config file
if (export_print_config)
if (config != nullptr)
{
if (!_add_print_config_file_to_archive(archive, print))
if (!_add_print_config_file_to_archive(archive, *config))
{
mz_zip_writer_end(&archive);
boost::filesystem::remove(filename);
@ -2017,13 +2017,15 @@ namespace Slic3r {
return true;
}
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print)
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const DynamicPrintConfig &config)
{
char buffer[1024];
sprintf(buffer, "; %s\n\n", header_slic3r_generated().c_str());
std::string out = buffer;
GCode::append_full_config(print, out);
for (const std::string &key : config.keys())
if (key != "compatible_printers")
out += "; " + key + " = " + config.serialize(key) + "\n";
if (!out.empty())
{
@ -2123,13 +2125,13 @@ namespace Slic3r {
return res;
}
bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config)
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
if ((path == nullptr) || (model == nullptr))
return false;
_3MF_Exporter exporter;
bool res = exporter.save_model_to_file(path, *model, *print, export_print_config);
bool res = exporter.save_model_to_file(path, *model, config);
if (!res)
exporter.log_errors();

View File

@ -4,14 +4,14 @@
namespace Slic3r {
class Model;
class Print;
class DynamicPrintConfig;
// Load the content of a 3mf file into the given model and preset bundle.
extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model);
// Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
extern bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config);
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config);
}; // namespace Slic3r

View File

@ -834,9 +834,9 @@ bool load_amf(const char *path, DynamicPrintConfig *config, Model *model)
return false;
}
bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
if ((path == nullptr) || (model == nullptr))
return false;
// forces ".zip.amf" extension
@ -857,11 +857,13 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n";
if (export_print_config)
if (config != nullptr)
{
std::string config = "\n";
GCode::append_full_config(*print, config);
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(config) << "</metadata>\n";
std::string str_config = "\n";
for (const std::string &key : config->keys())
if (key != "compatible_printers")
str_config += "; " + key + " = " + config->serialize(key) + "\n";
stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(str_config) << "</metadata>\n";
}
for (const auto &material : model->materials) {

View File

@ -4,14 +4,14 @@
namespace Slic3r {
class Model;
class Print;
class DynamicPrintConfig;
// Load the content of an amf file into the given model and configuration.
extern bool load_amf(const char *path, DynamicPrintConfig *config, Model *model);
// Save the given model and the config data contained in the given Print into an amf file.
// Save the given model and the config data into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
extern bool store_amf(const char *path, Model *model, Print* print, bool export_print_config);
extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config);
}; // namespace Slic3r

View File

@ -445,7 +445,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
boost::nowide::remove(path_tmp.c_str());
throw std::runtime_error(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
} catch (std::exception &ex) {
} catch (std::exception & /* ex */) {
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
// Close and remove the file.
fclose(file);
@ -601,15 +601,18 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
this->apply_print_config(print.config());
this->set_extruders(print.extruders());
// Initialize colorprint.
m_colorprint_heights = cast<float>(print.config().colorprint_heights.values);
// Initialize autospeed.
{
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
for (auto object : printable_objects) {
for (size_t region_id = 0; region_id < print.regions().size(); ++region_id) {
auto region = print.regions()[region_id];
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const PrintRegion* region = print.regions()[region_id];
for (auto layer : object->layers()) {
auto layerm = layer->regions()[region_id];
const LayerRegion* layerm = layer->regions()[region_id];
if (region->config().get_abs_value("perimeter_speed" ) == 0 ||
region->config().get_abs_value("small_perimeter_speed" ) == 0 ||
region->config().get_abs_value("external_perimeter_speed" ) == 0 ||
@ -673,8 +676,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
auto region = print.regions()[region_id];
for (const PrintRegion* region : print.regions()) {
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
@ -1319,6 +1321,18 @@ void GCode::process_layer(
m_second_layer_things_done = true;
}
// Let's issue a filament change command if requested at this layer.
// In case there are more toolchange requests that weren't done yet and should happen simultaneously, erase them all.
// (Layers can be close to each other, model could have been resliced with bigger layer height, ...).
bool colorprint_change = false;
while (!m_colorprint_heights.empty() && m_colorprint_heights.front()-EPSILON < layer.print_z) {
m_colorprint_heights.erase(m_colorprint_heights.begin());
colorprint_change = true;
}
if (colorprint_change)
gcode += "M600\n";
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
bool extrude_skirt =
@ -1442,7 +1456,7 @@ void GCode::process_layer(
};
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
const LayerRegion *layerm = layer.regions()[region_id];
const LayerRegion *layerm = (region_id < layer.regions().size()) ? layer.regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions()[region_id];

View File

@ -317,6 +317,9 @@ protected:
bool m_second_layer_things_done;
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
// Layer heights for colorprint - updated before the export and erased during the process
// so no toolchange occurs twice.
std::vector<float> m_colorprint_heights;
// Time estimators
GCodeTimeEstimator m_normal_time_estimator;

View File

@ -151,7 +151,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
if (layerm == nullptr)
continue;
@ -479,7 +479,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
@ -557,7 +557,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
unsigned int num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id];
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)

View File

@ -32,9 +32,8 @@ void Layer::make_slices()
slices = m_regions.front()->slices;
} else {
Polygons slices_p;
FOREACH_LAYERREGION(this, layerm) {
polygons_append(slices_p, to_polygons((*layerm)->slices));
}
for (LayerRegion *layerm : m_regions)
polygons_append(slices_p, to_polygons(layerm->slices));
slices = union_ex(slices_p);
}
@ -53,7 +52,7 @@ void Layer::make_slices()
// populate slices vector
for (size_t i : order)
this->slices.expolygons.push_back(STDMOVE(slices[i]));
this->slices.expolygons.push_back(std::move(slices[i]));
}
void Layer::merge_slices()
@ -63,10 +62,9 @@ void Layer::merge_slices()
// but use the non-split islands of a layer. For a single region print, these shall be equal.
m_regions.front()->slices.set(this->slices.expolygons, stInternal);
} else {
FOREACH_LAYERREGION(this, layerm) {
for (LayerRegion *layerm : m_regions)
// without safety offset, artifacts are generated (GH #2494)
(*layerm)->slices.set(union_ex(to_polygons(STDMOVE((*layerm)->slices.surfaces)), true), stInternal);
}
layerm->slices.set(union_ex(to_polygons(std::move(layerm->slices.surfaces)), true), stInternal);
}
}
@ -80,7 +78,7 @@ void Layer::make_perimeters()
// keep track of regions whose perimeters we have already generated
std::set<size_t> done;
FOREACH_LAYERREGION(this, layerm) {
for (LayerRegionPtrs::iterator layerm = m_regions.begin(); layerm != m_regions.end(); ++ layerm) {
size_t region_id = layerm - m_regions.begin();
if (done.find(region_id) != done.end()) continue;
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << ", region " << region_id;
@ -137,7 +135,7 @@ void Layer::make_perimeters()
// Separate the fill surfaces.
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.set(STDMOVE(expp), fill_surfaces.surfaces.front());
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
}
}
}

View File

@ -34,7 +34,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
// in place. However we're now only using its boundaries (which are invariant)
// so we're safe. This guarantees idempotence of prepare_infill() also in case
// that combine_infill() turns some fill_surface into VOID surfaces.
// Polygons fill_boundaries = to_polygons(STDMOVE(this->fill_surfaces));
// Polygons fill_boundaries = to_polygons(std::move(this->fill_surfaces));
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
// Collect polygons per surface type.
std::vector<Polygons> polygons_by_surface;
@ -133,9 +133,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (internal_surface)
// Make a copy as the following line uses the move semantics.
internal.push_back(surface);
polygons_append(fill_boundaries, STDMOVE(surface.expolygon));
polygons_append(fill_boundaries, std::move(surface.expolygon));
} else if (internal_surface)
internal.push_back(STDMOVE(surface));
internal.push_back(std::move(surface));
}
}
@ -192,7 +192,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island]));
}
bridge_bboxes.push_back(get_extents(polys));
bridges_grown.push_back(STDMOVE(polys));
bridges_grown.push_back(std::move(polys));
}
}
@ -243,7 +243,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] != group_id)
continue;
initial.push_back(STDMOVE(bridges[i].expolygon));
initial.push_back(std::move(bridges[i].expolygon));
polygons_append(grown, bridges_grown[i]);
}
// detect bridge direction before merging grown surfaces otherwise adjacent bridges
@ -269,7 +269,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]);
}
fill_boundaries = STDMOVE(to_polygons(fill_boundaries_ex));
fill_boundaries = std::move(to_polygons(fill_boundaries_ex));
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
}
@ -284,7 +284,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
Surfaces new_surfaces;
{
// Merge top and bottom in a single collection.
surfaces_append(top, STDMOVE(bottom));
surfaces_append(top, std::move(bottom));
// Intersect the grown surfaces with the actual fill boundaries.
Polygons bottom_polygons = to_polygons(bottom);
for (size_t i = 0; i < top.size(); ++ i) {
@ -292,11 +292,11 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, STDMOVE(s1));
polygons_append(polys, std::move(s1));
for (size_t j = i + 1; j < top.size(); ++ j) {
Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
polygons_append(polys, std::move(s2));
s2.clear();
}
}
@ -306,7 +306,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
surfaces_append(
new_surfaces,
// Don't use a safety offset as fill_boundaries were already united using the safety offset.
STDMOVE(intersection_ex(polys, fill_boundaries, false)),
std::move(intersection_ex(polys, fill_boundaries, false)),
s1);
}
}
@ -318,20 +318,20 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer)
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, STDMOVE(s1));
polygons_append(polys, std::move(s1));
for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
polygons_append(polys, STDMOVE(s2));
polygons_append(polys, std::move(s2));
s2.clear();
}
}
ExPolygons new_expolys = diff_ex(polys, new_polygons);
polygons_append(new_polygons, to_polygons(new_expolys));
surfaces_append(new_surfaces, STDMOVE(new_expolys), s1);
surfaces_append(new_surfaces, std::move(new_expolys), s1);
}
this->fill_surfaces.surfaces = STDMOVE(new_surfaces);
this->fill_surfaces.surfaces = std::move(new_surfaces);
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");

View File

@ -21,26 +21,58 @@ namespace Slic3r {
unsigned int Model::s_auto_extruder_id = 1;
ModelID ModelBase::s_last_id = 0;
size_t ModelBase::s_last_id = 0;
Model::Model(const Model &rhs)
Model& Model::assign_copy(const Model &rhs)
{
*this = rhs;
this->copy_id(rhs);
// copy materials
this->clear_materials();
this->materials = rhs.materials;
for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials) {
// Copy including the ID and m_model.
m.second = new ModelMaterial(*m.second);
m.second->set_model(this);
}
// copy objects
this->clear_objects();
this->objects.reserve(rhs.objects.size());
for (const ModelObject *model_object : rhs.objects) {
// Copy including the ID, leave ID set to invalid (zero).
auto mo = ModelObject::new_copy(*model_object);
mo->set_model(this);
this->objects.emplace_back(mo);
}
return *this;
}
Model& Model::operator=(const Model &rhs)
Model& Model::assign_copy(Model &&rhs)
{
m_id = rhs.m_id;
// copy materials
for (const auto &m : rhs.materials)
this->add_material(m.first, *m.second);
// copy objects
this->objects.reserve(rhs.objects.size());
for (const ModelObject *o : rhs.objects)
this->add_object(*o, true);
this->copy_id(rhs);
// Move materials, adjust the parent pointer.
this->clear_materials();
this->materials = std::move(rhs.materials);
for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials)
m.second->set_model(this);
rhs.materials.clear();
// Move objects, adjust the parent pointer.
this->clear_objects();
this->objects = std::move(rhs.objects);
for (ModelObject *model_object : this->objects)
model_object->set_model(this);
rhs.objects.clear();
return *this;
}
void Model::assign_new_unique_ids_recursive()
{
this->set_new_unique_id();
for (std::pair<const t_model_material_id, ModelMaterial*> &m : this->materials)
m.second->assign_new_unique_ids_recursive();
for (ModelObject *model_object : this->objects)
model_object->assign_new_unique_ids_recursive();
}
Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances)
{
Model model;
@ -150,9 +182,10 @@ ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh
return new_object;
}
ModelObject* Model::add_object(const ModelObject &other, bool copy_volumes)
ModelObject* Model::add_object(const ModelObject &other)
{
ModelObject* new_object = new ModelObject(this, other, copy_volumes);
ModelObject* new_object = ModelObject::new_clone(other);
new_object->set_model(this);
this->objects.push_back(new_object);
return new_object;
}
@ -164,21 +197,36 @@ void Model::delete_object(size_t idx)
this->objects.erase(i);
}
void Model::delete_object(ModelObject* object)
bool Model::delete_object(ModelObject* object)
{
if (object == nullptr)
return;
for (ModelObjectPtrs::iterator it = objects.begin(); it != objects.end(); ++it)
{
ModelObject* obj = *it;
if (obj == object)
{
delete obj;
objects.erase(it);
return;
if (object != nullptr) {
size_t idx = 0;
for (ModelObject *model_object : objects) {
if (model_object == object) {
delete model_object;
objects.erase(objects.begin() + idx);
return true;
}
++ idx;
}
}
return false;
}
bool Model::delete_object(ModelID id)
{
if (id.id != 0) {
size_t idx = 0;
for (ModelObject *model_object : objects) {
if (model_object->id() == id) {
delete model_object;
objects.erase(objects.begin() + idx);
return true;
}
++ idx;
}
}
return false;
}
void Model::clear_objects()
@ -206,6 +254,7 @@ void Model::clear_materials()
ModelMaterial* Model::add_material(t_model_material_id material_id)
{
assert(! material_id.empty());
ModelMaterial* material = this->get_material(material_id);
if (material == nullptr)
material = this->materials[material_id] = new ModelMaterial(this);
@ -214,11 +263,13 @@ ModelMaterial* Model::add_material(t_model_material_id material_id)
ModelMaterial* Model::add_material(t_model_material_id material_id, const ModelMaterial &other)
{
assert(! material_id.empty());
// delete existing material if any
ModelMaterial* material = this->get_material(material_id);
delete material;
// set new material
material = new ModelMaterial(this, other);
material = new ModelMaterial(other);
material->set_model(this);
this->materials[material_id] = material;
return material;
}
@ -421,6 +472,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
object->name = this->objects.front()->name;
//FIXME copy the config etc?
reset_auto_extruder_id();
@ -483,53 +535,90 @@ void Model::reset_auto_extruder_id()
s_auto_extruder_id = 1;
}
ModelObject::ModelObject(Model *model, const ModelObject &rhs, bool copy_volumes) :
m_model(model)
{
this->assign(&rhs, copy_volumes);
}
ModelObject::~ModelObject()
{
this->clear_volumes();
this->clear_instances();
}
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
ModelObject* ModelObject::clone(Model *parent)
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
{
return new ModelObject(parent, *this, true);
}
this->copy_id(rhs);
ModelObject& ModelObject::assign(const ModelObject *rhs, bool copy_volumes)
{
m_id = rhs->m_id;
name = rhs->name;
input_file = rhs->input_file;
config = rhs->config;
sla_support_points = rhs->sla_support_points;
layer_height_ranges = rhs->layer_height_ranges;
layer_height_profile = rhs->layer_height_profile;
layer_height_profile_valid = rhs->layer_height_profile_valid;
origin_translation = rhs->origin_translation;
m_bounding_box = rhs->m_bounding_box;
m_bounding_box_valid = rhs->m_bounding_box_valid;
this->name = rhs.name;
this->input_file = rhs.input_file;
this->config = rhs.config;
this->sla_support_points = rhs.sla_support_points;
this->layer_height_ranges = rhs.layer_height_ranges;
this->layer_height_profile = rhs.layer_height_profile;
this->layer_height_profile_valid = rhs.layer_height_profile_valid;
this->origin_translation = rhs.origin_translation;
m_bounding_box = rhs.m_bounding_box;
m_bounding_box_valid = rhs.m_bounding_box_valid;
volumes.clear();
instances.clear();
if (copy_volumes) {
this->volumes.reserve(rhs->volumes.size());
for (ModelVolume *model_volume : rhs->volumes)
this->add_volume(*model_volume);
this->instances.reserve(rhs->instances.size());
for (const ModelInstance *model_instance : rhs->instances)
this->add_instance(*model_instance);
this->clear_volumes();
this->volumes.reserve(rhs.volumes.size());
for (ModelVolume *model_volume : rhs.volumes) {
this->volumes.emplace_back(new ModelVolume(*model_volume));
this->volumes.back()->set_model_object(this);
}
this->clear_instances();
this->instances.reserve(rhs.instances.size());
for (const ModelInstance *model_instance : rhs.instances) {
this->instances.emplace_back(new ModelInstance(*model_instance));
this->instances.back()->set_model_object(this);
}
return *this;
}
// maintains the m_model pointer
ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
{
this->copy_id(rhs);
this->name = std::move(rhs.name);
this->input_file = std::move(rhs.input_file);
this->config = std::move(rhs.config);
this->sla_support_points = std::move(rhs.sla_support_points);
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
this->layer_height_profile = std::move(rhs.layer_height_profile);
this->layer_height_profile_valid = std::move(rhs.layer_height_profile_valid);
this->origin_translation = std::move(rhs.origin_translation);
m_bounding_box = std::move(rhs.m_bounding_box);
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
this->clear_volumes();
this->volumes = std::move(rhs.volumes);
rhs.volumes.clear();
for (ModelVolume *model_volume : this->volumes)
model_volume->set_model_object(this);
this->clear_instances();
this->instances = std::move(rhs.instances);
rhs.instances.clear();
for (ModelInstance *model_instance : this->instances)
model_instance->set_model_object(this);
return *this;
}
void ModelObject::assign_new_unique_ids_recursive()
{
this->set_new_unique_id();
for (ModelVolume *model_volume : this->volumes)
model_volume->assign_new_unique_ids_recursive();
for (ModelInstance *model_instance : this->instances)
model_instance->assign_new_unique_ids_recursive();
}
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
//ModelObject* ModelObject::clone(Model *parent)
//{
// return new ModelObject(parent, *this, true);
//}
ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
{
ModelVolume* v = new ModelVolume(this, mesh);
@ -554,6 +643,14 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
return v;
}
ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&mesh)
{
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
this->volumes.push_back(v);
this->invalidate_bounding_box();
return v;
}
void ModelObject::delete_volume(size_t idx)
{
ModelVolumePtrs::iterator i = this->volumes.begin() + idx;
@ -624,8 +721,16 @@ const BoundingBoxf3& ModelObject::bounding_box() const
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh m = v->mesh;
m.transform(v->get_matrix());
raw_bbox.merge(m.bounding_box());
}
#else
// mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box());
#endif // ENABLE_MODELVOLUME_TRANSFORM
BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances)
bb.merge(i->transform_bounding_box(raw_bbox));
@ -656,7 +761,15 @@ TriangleMesh ModelObject::raw_mesh() const
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
if (v->is_model_part())
mesh.merge(v->mesh);
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh vol_mesh(v->mesh);
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
#else
mesh.merge(v->mesh);
#endif // ENABLE_MODELVOLUME_TRANSFORM
return mesh;
}
@ -699,12 +812,14 @@ void ModelObject::center_around_origin()
this->translate(shift);
this->origin_translation += shift;
#if !ENABLE_MODELVOLUME_TRANSFORM
if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) {
i->set_offset(i->get_offset() - shift);
}
this->invalidate_bounding_box();
}
#endif // !ENABLE_MODELVOLUME_TRANSFORM
}
void ModelObject::ensure_on_bed()
@ -731,8 +846,7 @@ void ModelObject::translate(double x, double y, double z)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.translate(float(x), float(y), float(z));
v->m_convex_hull.translate(float(x), float(y), float(z));
v->translate(x, y, z);
}
if (m_bounding_box_valid)
@ -743,51 +857,55 @@ void ModelObject::scale(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.scale(versor);
v->m_convex_hull.scale(versor);
v->scale(versor);
}
#if !ENABLE_MODELVOLUME_TRANSFORM
// reset origin translation since it doesn't make sense anymore
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
void ModelObject::rotate(float angle, const Axis& axis)
void ModelObject::rotate(double angle, Axis axis)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.rotate(angle, axis);
v->m_convex_hull.rotate(angle, axis);
v->rotate(angle, axis);
}
center_around_origin();
#if !ENABLE_MODELVOLUME_TRANSFORM
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
void ModelObject::rotate(float angle, const Vec3d& axis)
void ModelObject::rotate(double angle, const Vec3d& axis)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.rotate(angle, axis);
v->m_convex_hull.rotate(angle, axis);
v->rotate(angle, axis);
}
center_around_origin();
#if !ENABLE_MODELVOLUME_TRANSFORM
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
void ModelObject::mirror(const Axis &axis)
void ModelObject::mirror(Axis axis)
{
for (ModelVolume *v : this->volumes)
{
v->mesh.mirror(axis);
v->m_convex_hull.mirror(axis);
v->mirror(axis);
}
#if !ENABLE_MODELVOLUME_TRANSFORM
this->origin_translation = Vec3d::Zero();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
this->invalidate_bounding_box();
}
@ -864,7 +982,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
if (this->volumes.size() > 1) {
// We can't split meshes if there's more than one volume, because
// we can't group the resulting meshes by object afterwards
new_objects->push_back(this);
new_objects->emplace_back(this);
return;
}
@ -876,16 +994,14 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
mesh->repair();
ModelObject* new_object = m_model->add_object(*this, false);
new_object->sla_support_points.clear();
new_object->input_file = "";
ModelVolume* new_volume = new_object->add_volume(*mesh);
new_volume->name = volume->name;
new_volume->config = volume->config;
new_volume->set_type(volume->type());
new_volume->set_material_id(volume->material_id());
new_objects->push_back(new_object);
ModelObject* new_object = m_model->add_object();
new_object->name = this->name;
new_object->config = this->config;
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
new_object->add_volume(*volume, std::move(*mesh));
new_objects->emplace_back(new_object);
delete mesh;
}
@ -918,17 +1034,32 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const
double min_z = DBL_MAX;
ModelInstance* inst = instances[instance_idx];
const Transform3d& m = inst->get_matrix(true);
const Transform3d& mi = inst->get_matrix(true);
for (ModelVolume *v : volumes)
for (const ModelVolume* v : volumes)
{
if (!v->is_model_part())
continue;
#if ENABLE_MODELVOLUME_TRANSFORM
Transform3d mv = mi * v->get_matrix();
const TriangleMesh& hull = v->get_convex_hull();
for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f)
{
const stl_facet* facet = hull.stl.facet_start + f;
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast<double>()));
}
#else
for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f)
{
const stl_facet* facet = v->mesh.stl.facet_start + f;
min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(m * facet->vertex[2].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast<double>()));
min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast<double>()));
}
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
return min_z + inst->get_offset(Z);
@ -945,7 +1076,11 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3
unsigned int inside_outside = 0;
for (const ModelVolume *vol : this->volumes)
if (vol->is_model_part()) {
#if ENABLE_MODELVOLUME_TRANSFORM
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix());
#else
BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix());
#endif // ENABLE_MODELVOLUME_TRANSFORM
if (print_volume.contains(bb))
inside_outside |= INSIDE;
else if (print_volume.intersects(bb))
@ -1007,9 +1142,9 @@ void ModelObject::print_info() const
void ModelVolume::set_material_id(t_model_material_id material_id)
{
m_material_id = material_id;
// ensure m_material_id references an existing material
(void)this->object->get_model()->add_material(material_id);
if (! material_id.empty())
this->object->get_model()->add_material(material_id);
}
ModelMaterial* ModelVolume::material() const
@ -1020,17 +1155,17 @@ ModelMaterial* ModelVolume::material() const
void ModelVolume::set_material(t_model_material_id material_id, const ModelMaterial &material)
{
m_material_id = material_id;
(void)this->object->get_model()->add_material(material_id, material);
if (! material_id.empty())
this->object->get_model()->add_material(material_id, material);
}
ModelMaterial* ModelVolume::assign_unique_material()
#if ENABLE_MODELVOLUME_TRANSFORM
void ModelVolume::translate_geometry(const Vec3d& displacement)
{
Model* model = this->get_object()->get_model();
// as material-id "0" is reserved by the AMF spec we start from 1
m_material_id = 1 + model->materials.size(); // watchout for implicit cast
return model->add_material(m_material_id);
mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
}
#endif // ENABLE_MODELVOLUME_TRANSFORM
void ModelVolume::calculate_convex_hull()
{
@ -1108,15 +1243,66 @@ size_t ModelVolume::split(unsigned int max_extruders)
return idx;
}
void ModelVolume::translate(double x, double y, double z)
{
translate(Vec3d(x, y, z));
}
void ModelVolume::translate(const Vec3d& displacement)
{
#if ENABLE_MODELVOLUME_TRANSFORM
set_offset(get_offset() + displacement);
#else
mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2));
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::scale(const Vec3d& scaling_factors)
{
#if ENABLE_MODELVOLUME_TRANSFORM
set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors));
#else
mesh.scale(scaling_factors);
m_convex_hull.scale(scaling_factors);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::rotate(double angle, Axis axis)
{
#if ENABLE_MODELVOLUME_TRANSFORM
switch (axis)
{
case X: { rotate(angle, Vec3d::UnitX()); break; }
case Y: { rotate(angle, Vec3d::UnitY()); break; }
case Z: { rotate(angle, Vec3d::UnitZ()); break; }
}
#else
mesh.rotate(angle, axis);
m_convex_hull.rotate(angle, axis);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::rotate(double angle, const Vec3d& axis)
{
#if ENABLE_MODELVOLUME_TRANSFORM
set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix()));
#else
mesh.rotate(angle, axis);
m_convex_hull.rotate(angle, axis);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
void ModelVolume::mirror(Axis axis)
{
#if ENABLE_MODELVOLUME_TRANSFORM
Vec3d mirror = get_mirror();
switch (axis)
{
case X: { mirror(0) *= -1.0; break; }
case Y: { mirror(1) *= -1.0; break; }
case Z: { mirror(2) *= -1.0; break; }
}
set_mirror(mirror);
#else
mesh.mirror(axis);
m_convex_hull.mirror(axis);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
#if !ENABLE_MODELVOLUME_TRANSFORM
@ -1174,14 +1360,14 @@ void ModelInstance::set_mirror(Axis axis, double mirror)
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
mesh->transform(get_matrix(dont_translate).cast<float>());
mesh->transform(get_matrix(dont_translate));
}
BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const
{
// Rotate around mesh origin.
TriangleMesh copy(*mesh);
copy.transform(get_matrix(true, false, true, true).cast<float>());
copy.transform(get_matrix(true, false, true, true));
BoundingBoxf3 bbox = copy.bounding_box();
if (!empty(bbox)) {
@ -1189,10 +1375,10 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
for (unsigned int i = 0; i < 3; ++i)
{
#if ENABLE_MODELVOLUME_TRANSFORM
if (std::abs(m_transformation.get_scaling_factor((Axis)i)-1.0) > EPSILON)
if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON)
{
bbox.min(i) *= m_transformation.get_scaling_factor((Axis)i);
bbox.max(i) *= m_transformation.get_scaling_factor((Axis)i);
bbox.min(i) *= get_scaling_factor((Axis)i);
bbox.max(i) *= get_scaling_factor((Axis)i);
#else
if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON)
{
@ -1205,8 +1391,8 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
// Translate the bounding box.
if (! dont_translate) {
#if ENABLE_MODELVOLUME_TRANSFORM
bbox.min += m_transformation.get_offset();
bbox.max += m_transformation.get_offset();
bbox.min += get_offset();
bbox.max += get_offset();
#else
bbox.min += this->m_offset;
bbox.max += this->m_offset;
@ -1230,9 +1416,9 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
{
#if ENABLE_MODELVOLUME_TRANSFORM
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
polygon->rotate(m_transformation.get_rotation(Z)); // rotate around polygon origin
polygon->rotate(get_rotation(Z)); // rotate around polygon origin
// CHECK_ME -> Is the following correct ?
polygon->scale(m_transformation.get_scaling_factor(X), m_transformation.get_scaling_factor(Y)); // scale around polygon origin
polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin
#else
// CHECK_ME -> Is the following correct or it should take in account all three rotations ?
polygon->rotate(this->m_rotation(2)); // rotate around polygon origin

View File

@ -22,21 +22,35 @@ class ModelInstance;
class ModelMaterial;
class ModelObject;
class ModelVolume;
class PresetBundle;
class Print;
typedef std::string t_model_material_id;
typedef std::string t_model_material_attribute;
typedef std::map<t_model_material_attribute,std::string> t_model_material_attributes;
typedef std::map<t_model_material_attribute, std::string> t_model_material_attributes;
typedef std::map<t_model_material_id,ModelMaterial*> ModelMaterialMap;
typedef std::map<t_model_material_id, ModelMaterial*> ModelMaterialMap;
typedef std::vector<ModelObject*> ModelObjectPtrs;
typedef std::vector<ModelVolume*> ModelVolumePtrs;
typedef std::vector<ModelInstance*> ModelInstancePtrs;
// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial.
// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject)
typedef size_t ModelID;
// Valid IDs are strictly positive (non zero).
// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t
// for parameter overload.
struct ModelID
{
ModelID(size_t id) : id(id) {}
bool operator==(const ModelID &rhs) const { return this->id == rhs.id; }
bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; }
bool operator< (const ModelID &rhs) const { return this->id < rhs.id; }
bool operator> (const ModelID &rhs) const { return this->id > rhs.id; }
bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; }
bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; }
size_t id;
};
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
@ -45,22 +59,69 @@ typedef size_t ModelID;
class ModelBase
{
public:
ModelID id() const { return m_id; }
ModelID id() const { return m_id; }
protected:
// Constructor to be only called by derived classes.
ModelBase() {}
ModelID m_id = generate_new_id();
// Constructors to be only called by derived classes.
// Default constructor to assign a unique ID.
ModelBase() : m_id(generate_new_id()) {}
// Constructor with ignored int parameter to assign an invalid ID, to be replaced
// by an existing ID copied from elsewhere.
ModelBase(int) : m_id(ModelID(0)) {}
// Use with caution!
void set_new_unique_id() { m_id = generate_new_id(); }
void set_invalid_id() { m_id = 0; }
// Use with caution!
void copy_id(const ModelBase &rhs) { m_id = rhs.id(); }
// Override this method if a ModelBase derived class owns other ModelBase derived instances.
void assign_new_unique_ids_recursive() { this->set_new_unique_id(); }
private:
static inline ModelID generate_new_id() { return s_last_id ++; }
static ModelID s_last_id;
ModelID m_id;
static inline ModelID generate_new_id() { return ModelID(++ s_last_id); }
static size_t s_last_id;
};
#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \
/* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \
/* to make a private copy for background processing. */ \
static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \
static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \
static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \
static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \
TYPE& assign_copy(const TYPE &rhs); \
TYPE& assign_copy(TYPE &&rhs); \
/* Copy a TYPE, generate new IDs. The front end will use this call. */ \
static TYPE* new_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
auto obj = new TYPE(-1); \
obj->assign_clone(rhs); \
return obj; \
} \
TYPE make_clone(const TYPE &rhs) { \
/* Default constructor assigning an invalid ID. */ \
TYPE obj(-1); \
obj.assign_clone(rhs); \
return obj; \
} \
TYPE& assign_clone(const TYPE &rhs) { \
this->assign_copy(rhs); \
this->assign_new_unique_ids_recursive(); \
return *this; \
}
#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \
private: \
/* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \
explicit TYPE(int) : ModelBase(-1) {}; \
void assign_new_unique_ids_recursive();
// Material, which may be shared across multiple ModelObjects of a single Model.
class ModelMaterial : public ModelBase
{
friend class Model;
public:
// Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose.
t_model_material_attributes attributes;
@ -71,14 +132,22 @@ public:
void apply(const t_model_material_attributes &attributes)
{ this->attributes.insert(attributes.begin(), attributes.end()); }
protected:
friend class Model;
// Constructor, which assigns a new unique ID.
ModelMaterial(Model *model) : m_model(model) {}
// Copy constructor copies the ID and m_model!
ModelMaterial(const ModelMaterial &rhs) = default;
void set_model(Model *model) { m_model = model; }
private:
// Parent, owning this material.
Model *m_model;
ModelMaterial(Model *model) : m_model(model) {}
ModelMaterial(Model *model, const ModelMaterial &other) : attributes(other.attributes), config(other.config), m_model(model) {}
explicit ModelMaterial(ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &rhs) = delete;
ModelMaterial() = delete;
ModelMaterial(ModelMaterial &&rhs) = delete;
ModelMaterial& operator=(const ModelMaterial &rhs) = delete;
ModelMaterial& operator=(ModelMaterial &&rhs) = delete;
};
// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials),
@ -119,15 +188,12 @@ public:
when user expects that. */
Vec3d origin_translation;
// Assign a ModelObject to this object while keeping the original pointer to the parent Model.
// Make a deep copy.
ModelObject& assign(const ModelObject *rhs, bool copy_volumes = true);
Model* get_model() const { return m_model; };
ModelVolume* add_volume(const TriangleMesh &mesh);
ModelVolume* add_volume(TriangleMesh &&mesh);
ModelVolume* add_volume(const ModelVolume &volume);
ModelVolume* add_volume(const ModelVolume &volume, TriangleMesh &&mesh);
void delete_volume(size_t idx);
void clear_volumes();
bool is_multiparts() const { return volumes.size() > 1; }
@ -163,9 +229,10 @@ public:
void translate(double x, double y, double z);
void scale(const Vec3d &versor);
void scale(const double s) { this->scale(Vec3d(s, s, s)); }
void rotate(float angle, const Axis &axis);
void rotate(float angle, const Vec3d& axis);
void mirror(const Axis &axis);
void scale(double x, double y, double z) { this->scale(Vec3d(x, y, z)); }
void rotate(double angle, Axis axis);
void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis);
size_t materials_count() const;
size_t facets_count() const;
bool needed_repair() const;
@ -184,22 +251,27 @@ public:
protected:
friend class Print;
// Clone this ModelObject including its volumes and instances, keep the IDs of the copies equal to the original.
// Called by Print::apply() to clone the Model / ModelObject hierarchy to the back end for background processing.
ModelObject* clone(Model *parent);
void set_model(Model *model) { m_model = model; }
// Called by Print::apply() to set the model pointer after making a copy.
void set_model(Model *model) { m_model = model; }
private:
ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
ModelObject(Model *model, const ModelObject &rhs, bool copy_volumes = true);
explicit ModelObject(ModelObject &rhs) = delete;
~ModelObject();
ModelObject& operator=(ModelObject &rhs) = default;
// Parent object, owning this ModelObject.
Model *m_model;
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); }
explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; }
ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; }
MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject)
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject)
// Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized.
Model *m_model = nullptr;
// Bounding box, cached.
mutable BoundingBoxf3 m_bounding_box;
mutable bool m_bounding_box_valid;
};
@ -208,8 +280,6 @@ private:
// ModelVolume instances are owned by a ModelObject.
class ModelVolume : public ModelBase
{
friend class ModelObject;
public:
std::string name;
// The triangular model.
@ -226,9 +296,6 @@ public:
SUPPORT_BLOCKER,
};
// Clone this ModelVolume, keep the ID identical, set the parent to the cloned volume.
ModelVolume* clone(ModelObject *parent) { return new ModelVolume(parent, *this); }
// A parent object owning this modifier volume.
ModelObject* get_object() const { return this->object; };
Type type() const { return m_type; }
@ -246,11 +313,19 @@ public:
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
size_t split(unsigned int max_extruders);
void translate(double x, double y, double z);
void translate(double x, double y, double z) { translate(Vec3d(x, y, z)); }
void translate(const Vec3d& displacement);
void scale(const Vec3d& scaling_factors);
void scale(double x, double y, double z) { scale(Vec3d(x, y, z)); }
void scale(double s) { scale(Vec3d(s, s, s)); }
void rotate(double angle, Axis axis);
void rotate(double angle, const Vec3d& axis);
void mirror(Axis axis);
#if ENABLE_MODELVOLUME_TRANSFORM
void translate_geometry(const Vec3d& displacement);
#endif // ENABLE_MODELVOLUME_TRANSFORM
ModelMaterial* assign_unique_material();
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
@ -289,6 +364,13 @@ public:
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
#endif // ENABLE_MODELVOLUME_TRANSFORM
protected:
friend class Print;
friend class ModelObject;
explicit ModelVolume(ModelVolume &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
private:
// Parent object owning this ModelVolume.
ModelObject* object;
@ -306,24 +388,45 @@ private:
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) :
mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
#if ENABLE_MODELVOLUME_TRANSFORM
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
ModelBase(other), // copy the ID
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation)
{
this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
#else
// Copying an existing volume, therefore this volume will get a copy of the ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other) :
ModelBase(other), // copy the ID
name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
{
if (! other.material_id().empty())
this->set_material_id(other.material_id());
}
// Providing a new mesh, therefore this volume will get a new unique ID assigned.
ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
{
if (! other.material_id().empty())
this->set_material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
#endif // ENABLE_MODELVOLUME_TRANSFORM
explicit ModelVolume(ModelVolume &rhs) = delete;
ModelVolume& operator=(ModelVolume &rhs) = delete;
};
@ -340,8 +443,6 @@ public:
Num_BedStates
};
friend class ModelObject;
private:
#if ENABLE_MODELVOLUME_TRANSFORM
Geometry::Transformation m_transformation;
@ -430,22 +531,33 @@ public:
bool is_printable() const { return print_volume_state == PVS_Inside; }
protected:
friend class Print;
friend class ModelObject;
explicit ModelInstance(const ModelInstance &rhs) = default;
void set_model_object(ModelObject *model_object) { object = model_object; }
private:
// Parent object, owning this instance.
ModelObject* object;
#if ENABLE_MODELVOLUME_TRANSFORM
ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {}
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {}
#else
ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
explicit ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {}
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {}
#endif // ENABLE_MODELVOLUME_TRANSFORM
explicit ModelInstance(ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &rhs) = delete;
ModelInstance() = delete;
explicit ModelInstance(ModelInstance &&rhs) = delete;
ModelInstance& operator=(const ModelInstance &rhs) = delete;
ModelInstance& operator=(ModelInstance &&rhs) = delete;
};
// The print bed content.
@ -460,30 +572,39 @@ class Model : public ModelBase
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
ModelMaterialMap materials;
ModelMaterialMap materials;
// Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation).
ModelObjectPtrs objects;
ModelObjectPtrs objects;
// Default constructor assigns a new ID to the model.
Model() {}
Model(const Model &rhs);
Model& operator=(const Model &rhs);
~Model() { this->clear_objects(); this->clear_materials(); }
// XXX: use fs::path ?
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
/* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */
Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); }
explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); }
Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; }
Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; }
MODELBASE_DERIVED_COPY_MOVE_CLONE(Model)
static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true);
static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true);
/// Repair the ModelObjects of the current Model.
/// This function calls repair function on each TriangleMesh of each model object volume
void repair();
void repair();
// Add a new ModelObject to this Model, generate a new ID for this ModelObject.
ModelObject* add_object();
ModelObject* add_object(const char *name, const char *path, const TriangleMesh &mesh);
ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh);
ModelObject* add_object(const ModelObject &other, bool copy_volumes = true);
void delete_object(size_t idx);
void delete_object(ModelObject* object);
void clear_objects();
ModelObject* add_object(const ModelObject &other);
void delete_object(size_t idx);
bool delete_object(ModelID id);
bool delete_object(ModelObject* object);
void clear_objects();
ModelMaterial* add_material(t_model_material_id material_id);
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
@ -492,9 +613,9 @@ public:
return (i == this->materials.end()) ? nullptr : i->second;
}
void delete_material(t_model_material_id material_id);
void clear_materials();
bool add_default_instances();
void delete_material(t_model_material_id material_id);
void clear_materials();
bool add_default_instances();
// Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
// Set the print_volume_state of PrintObject::instances,
@ -520,8 +641,14 @@ public:
static unsigned int get_auto_extruder_id(unsigned int max_extruders);
static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
static void reset_auto_extruder_id();
private:
MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model)
};
#undef MODELBASE_DERIVED_COPY_MOVE_CLONE
#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE
}
#endif

View File

@ -58,7 +58,7 @@ inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); }
inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(v(0), v(1), z); }
inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); }
inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); }
inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }

View File

@ -124,6 +124,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"between_objects_gcode",
"bridge_acceleration",
"bridge_fan_speed",
"colorprint_heights",
"cooling",
"default_acceleration",
"deretract_speed",
@ -603,7 +604,7 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old
size_t i_old, i_new;
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
const ModelVolume &mv_old = *model_object_old.volumes[i_old];
const ModelVolume &mv_new = *model_object_new.volumes[i_old];
const ModelVolume &mv_new = *model_object_new.volumes[i_new];
if (mv_old.type() != type) {
++ i_old;
continue;
@ -615,8 +616,12 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old
if (mv_old.id() != mv_new.id())
return true;
//FIXME test for the content of the mesh!
//FIXME test for the transformation matrices!
++ i_old;
#if ENABLE_MODELVOLUME_TRANSFORM
if (!mv_old.get_matrix().isApprox(mv_new.get_matrix()))
return true;
#endif // ENABLE_MODELVOLUME_TRANSFORM
++i_old;
++ i_new;
}
for (; i_old < model_object_old.volumes.size(); ++ i_old) {
@ -634,7 +639,7 @@ static inline bool model_volume_list_changed(const ModelObject &model_object_old
return false;
}
static inline void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src)
void Print::model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src)
{
// 1) Delete the support volumes from model_object_dst.
{
@ -650,8 +655,10 @@ static inline void model_volume_list_update_supports(ModelObject &model_object_d
}
// 2) Copy the support volumes from model_object_src to the end of model_object_dst.
for (ModelVolume *vol : model_object_src.volumes) {
if (vol->is_support_modifier())
model_object_dst.volumes.emplace_back(vol->clone(&model_object_dst));
if (vol->is_support_modifier()) {
model_object_dst.volumes.emplace_back(new ModelVolume(*vol));
model_object_dst.volumes.back()->set_model_object(&model_object_dst);
}
}
}
@ -709,8 +716,60 @@ static std::vector<PrintInstances> print_objects_from_model_object(const ModelOb
return std::vector<PrintInstances>(trafos.begin(), trafos.end());
}
#ifdef _DEBUG
// Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique.
static inline void check_model_ids_validity(const Model &model)
{
std::set<ModelID> ids;
auto check = [&ids](ModelID id) {
assert(id.id > 0);
assert(ids.find(id) == ids.end());
ids.insert(id);
};
for (const ModelObject *model_object : model.objects) {
check(model_object->id());
for (const ModelVolume *model_volume : model_object->volumes)
check(model_volume->id());
for (const ModelInstance *model_instance : model_object->instances)
check(model_instance->id());
}
for (const auto mm : model.materials)
check(mm.second->id());
}
static inline void check_model_ids_equal(const Model &model1, const Model &model2)
{
// Verify whether the IDs of model1 and model match.
assert(model1.objects.size() == model2.objects.size());
for (size_t idx_model = 0; idx_model < model2.objects.size(); ++ idx_model) {
const ModelObject &model_object1 = *model1.objects[idx_model];
const ModelObject &model_object2 = * model2.objects[idx_model];
assert(model_object1.id() == model_object2.id());
assert(model_object1.volumes.size() == model_object2.volumes.size());
assert(model_object1.instances.size() == model_object2.instances.size());
for (size_t i = 0; i < model_object1.volumes.size(); ++ i)
assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id());
for (size_t i = 0; i < model_object1.instances.size(); ++ i)
assert(model_object1.instances[i]->id() == model_object2.instances[i]->id());
}
assert(model1.materials.size() == model2.materials.size());
{
auto it1 = model1.materials.begin();
auto it2 = model2.materials.begin();
for (; it1 != model1.materials.end(); ++ it1, ++ it2) {
assert(it1->first == it2->first); // compare keys
assert(it1->second->id() == it2->second->id());
}
}
}
#endif /* _DEBUG */
Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &config_in)
{
#ifdef _DEBUG
check_model_ids_validity(model);
#endif /* _DEBUG */
// Make a copy of the config, normalize it.
DynamicPrintConfig config(config_in);
config.normalize();
@ -774,7 +833,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
for (PrintRegion *region : m_regions)
delete region;
m_regions.clear();
m_model = model;
m_model.assign_copy(model);
for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else {
@ -789,7 +848,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
model_object_status.emplace(model.objects[i]->id(), ModelObjectStatus::New);
m_model.objects.emplace_back(model.objects[i]->clone(&m_model));
m_model.objects.emplace_back(ModelObject::new_copy(*model.objects[i]));
m_model.objects.back()->set_model(&m_model);
}
} else {
// Reorder the objects, add new objects.
@ -806,7 +866,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
auto it = std::lower_bound(model_objects_old.begin(), model_objects_old.end(), mobj, by_id_lower);
if (it == model_objects_old.end() || (*it)->id() != mobj->id()) {
// New ModelObject added.
m_model.objects.emplace_back((*it)->clone(&m_model));
m_model.objects.emplace_back(ModelObject::new_copy(*mobj));
m_model.objects.back()->set_model(&m_model);
model_object_status.emplace(mobj->id(), ModelObjectStatus::New);
} else {
// Existing ModelObject re-added (possibly moved in the list).
@ -899,8 +960,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
update_apply_status(it->print_object->invalidate_all_steps());
const_cast<PrintObjectStatus&>(*it).status = PrintObjectStatus::Deleted;
}
// Copy content of the ModelObject including its ID, reset the parent.
model_object.assign(&model_object_new);
// Copy content of the ModelObject including its ID, do not change the parent.
model_object.assign_copy(model_object_new);
} else if (support_blockers_differ || support_enforcers_differ) {
// First stop background processing before shuffling or deleting the ModelVolumes in the ModelObject's list.
m_cancel_callback();
@ -933,8 +994,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
model_object.clear_instances();
for (const ModelInstance *model_instance : model_object_new.instances)
model_object.add_instance(*model_instance);
model_object.instances.reserve(model_object_new.instances.size());
for (const ModelInstance *model_instance : model_object_new.instances) {
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
}
}
@ -1136,6 +1200,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
object->update_layer_height_profile();
this->update_object_placeholders();
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
#endif /* _DEBUG */
return static_cast<ApplyStatus>(apply_status);
}

View File

@ -564,6 +564,9 @@ private:
void _make_wipe_tower();
void _simplify_slices(double distance);
// Declared here to have access to Model / ModelObject / ModelInstance
static void model_volume_list_update_supports(ModelObject &model_object_dst, const ModelObject &model_object_src);
PrintState<PrintStep, psCount> m_state;
// Mutex used for synchronization of the worker thread with the UI thread:
// The mutex will be used to guard the worker thread against entering a stage
@ -601,12 +604,6 @@ private:
friend class PrintObject;
};
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->m_layers, layer)
#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->m_regions, layerm)
}
#endif

View File

@ -192,6 +192,12 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->default_value = new ConfigOptionBool(false);
def = this->add("colorprint_heights", coFloats);
def->label = L("Colorprint height");
def->tooltip = L("Heights at which a filament change is to occur. ");
def->cli = "colorprint-heights=f@";
def->default_value = new ConfigOptionFloats { };
def = this->add("compatible_printers", coStrings);
def->label = L("Compatible printers");
def->mode = comAdvanced;

View File

@ -704,6 +704,7 @@ public:
ConfigOptionInts bridge_fan_speed;
ConfigOptionFloat brim_width;
ConfigOptionBool complete_objects;
ConfigOptionFloats colorprint_heights;
ConfigOptionBools cooling;
ConfigOptionFloat default_acceleration;
ConfigOptionInts disable_fan_first_layers;
@ -781,6 +782,7 @@ protected:
OPT_PTR(bridge_fan_speed);
OPT_PTR(brim_width);
OPT_PTR(complete_objects);
OPT_PTR(colorprint_heights);
OPT_PTR(cooling);
OPT_PTR(default_acceleration);
OPT_PTR(disable_fan_first_layers);

View File

@ -170,8 +170,8 @@ void PrintObject::make_perimeters()
// merge slices if they were split into types
if (this->typed_slices) {
FOREACH_LAYER(this, layer_it) {
(*layer_it)->merge_slices();
for (Layer *layer : m_layers) {
layer->merge_slices();
m_print->throw_if_canceled();
}
this->typed_slices = false;
@ -184,8 +184,8 @@ void PrintObject::make_perimeters()
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
@ -313,7 +313,7 @@ void PrintObject::prepare_infill()
// Debugging output.
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("6_discover_vertical_shells-final");
@ -332,7 +332,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("7_discover_horizontal_shells-final");
@ -351,7 +351,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("8_clip_surfaces-final");
@ -370,7 +370,7 @@ void PrintObject::prepare_infill()
m_print->throw_if_canceled();
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("9_prepare_infill-final");
@ -633,7 +633,7 @@ void PrintObject::detect_surfaces_type()
// should be visible.
bool interface_shells = m_config.interface_shells.value;
for (int idx_region = 0; idx_region < m_print->m_regions.size(); ++ idx_region) {
for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start";
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (Layer *layer : m_layers)
@ -818,7 +818,7 @@ void PrintObject::process_external_surfaces()
{
BOOST_LOG_TRIVIAL(info) << "Processing external surfaces...";
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start";
@ -851,13 +851,13 @@ void PrintObject::discover_vertical_shells()
Polygons holes;
};
std::vector<DiscoverVerticalShellsCacheEntry> cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry());
bool top_bottom_surfaces_all_regions = m_print->regions().size() > 1 && ! m_config.interface_shells.value;
bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value;
if (top_bottom_surfaces_all_regions) {
// This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness
// is calculated over all materials.
// Is the "ensure vertical wall thickness" applicable to any region?
bool has_extra_layers = false;
for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) {
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
const PrintRegion &region = *m_print->get_region(idx_region);
if (region.config().ensure_vertical_shell_thickness.value &&
(region.config().top_solid_layers.value > 1 || region.config().bottom_solid_layers.value > 1)) {
@ -874,7 +874,7 @@ void PrintObject::discover_vertical_shells()
tbb::blocked_range<size_t>(0, m_layers.size(), grain_size),
[this, &cache_top_botom_regions](const tbb::blocked_range<size_t>& range) {
const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge };
const size_t num_regions = m_print->regions().size();
const size_t num_regions = this->region_volumes.size();
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
m_print->throw_if_canceled();
const Layer &layer = *m_layers[idx_layer];
@ -935,7 +935,7 @@ void PrintObject::discover_vertical_shells()
BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - end : cache top / bottom";
}
for (size_t idx_region = 0; idx_region < m_print->regions().size(); ++ idx_region) {
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) {
PROFILE_BLOCK(discover_vertical_shells_region);
const PrintRegion &region = *m_print->get_region(idx_region);
@ -1227,7 +1227,7 @@ void PrintObject::bridge_over_infill()
{
BOOST_LOG_TRIVIAL(info) << "Bridge over infill...";
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
// skip bridging in case there are no voids
@ -1243,9 +1243,10 @@ void PrintObject::bridge_over_infill()
*this
);
FOREACH_LAYER(this, layer_it) {
for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) {
// skip first layer
if (layer_it == m_layers.begin()) continue;
if (layer_it == m_layers.begin())
continue;
Layer* layer = *layer_it;
LayerRegion* layerm = layer->m_regions[region_id];
@ -1271,8 +1272,8 @@ void PrintObject::bridge_over_infill()
// iterate through regions and collect internal surfaces
Polygons lower_internal;
FOREACH_LAYERREGION(lower_layer, lower_layerm_it)
(*lower_layerm_it)->fill_surfaces.filter_by_type(stInternal, &lower_internal);
for (LayerRegion *lower_layerm : lower_layer->m_regions)
lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal);
// intersect such lower internal surfaces with the candidate solid surfaces
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
@ -1443,14 +1444,14 @@ void PrintObject::_slice()
layer->lower_layer = prev;
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id)
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
layer->add_region(this->print()->regions()[region_id]);
prev = layer;
}
}
// Slice all non-modifier volumes.
for (size_t region_id = 0; region_id < this->print()->regions().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;
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, false);
m_print->throw_if_canceled();
@ -1462,14 +1463,14 @@ void PrintObject::_slice()
}
// Slice all modifier volumes.
if (this->print()->regions().size() > 1) {
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
if (this->region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
std::vector<ExPolygons> expolygons_by_layer = this->_slice_region(region_id, slice_zs, true);
m_print->throw_if_canceled();
// loop through the other regions and 'steal' the slices belonging to this one
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
for (size_t other_region_id = 0; other_region_id < this->print()->regions().size(); ++ other_region_id) {
for (size_t other_region_id = 0; other_region_id < this->region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
@ -1496,7 +1497,7 @@ void PrintObject::_slice()
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers";
while (! m_layers.empty()) {
const Layer *layer = m_layers.back();
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id)
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id)
if (layer->m_regions[region_id] != nullptr && ! layer->m_regions[region_id]->slices.empty())
// Non empty layer.
goto end;
@ -1601,9 +1602,17 @@ std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z,
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh;
for (const ModelVolume *v : volumes)
mesh.merge(v->mesh);
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh vol_mesh(v->mesh);
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
#else
mesh.merge(v->mesh);
#endif // ENABLE_MODELVOLUME_TRANSFORM
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo.cast<float>());
mesh.transform(m_trafo);
// apply XY shift
mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), 0);
// perform actual slicing
@ -1734,8 +1743,8 @@ void PrintObject::_make_perimeters()
// merge slices if they were split into types
if (this->typed_slices) {
FOREACH_LAYER(this, layer_it)
(*layer_it)->merge_slices();
for (Layer *layer : m_layers)
layer->merge_slices();
this->typed_slices = false;
this->invalidate_step(posPrepareInfill);
}
@ -1747,7 +1756,7 @@ void PrintObject::_make_perimeters()
// but we don't generate any extra perimeter if fill density is zero, as they would be floating
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion &region = *m_print->regions()[region_id];
if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
@ -1919,7 +1928,7 @@ void PrintObject::discover_horizontal_shells()
{
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (int i = 0; i < int(m_layers.size()); ++ i) {
m_print->throw_if_canceled();
LayerRegion *layerm = m_layers[i]->regions()[region_id];
@ -2093,7 +2102,7 @@ void PrintObject::discover_horizontal_shells()
} // for each region
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
for (const Layer *layer : m_layers) {
const LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
@ -2109,7 +2118,7 @@ void PrintObject::discover_horizontal_shells()
void PrintObject::combine_infill()
{
// Work on each region separately.
for (size_t region_id = 0; region_id < this->print()->regions().size(); ++ region_id) {
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
const PrintRegion *region = this->print()->regions()[region_id];
const int every = region->config().infill_every_layers.value;
if (every < 2 || region->config().fill_density == 0.)

View File

@ -2450,7 +2450,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
// Transform loops into ExtrusionPath objects.
extrusion_entities_append_paths(
top_contact_layer.extrusions,
STDMOVE(loop_lines),
std::move(loop_lines),
erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height);
}
@ -2827,7 +2827,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing()));
extrusion_entities_append_paths(
support_layer.support_fills.entities,
to_polylines(STDMOVE(to_infill_polygons)),
to_polylines(std::move(to_infill_polygons)),
erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height);
}
if (! to_infill.empty()) {
@ -2841,7 +2841,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
STDMOVE(to_infill),
std::move(to_infill),
// Filler and its parameters
filler, float(support_density),
// Extrusion parameters
@ -3037,14 +3037,14 @@ void PrintObjectSupportMaterial::generate_toolpaths(
to_infill = offset_ex(to_infill, - 0.4 * float(flow.scaled_spacing()));
extrusion_entities_append_paths(
base_layer.extrusions,
to_polylines(STDMOVE(to_infill_polygons)),
to_polylines(std::move(to_infill_polygons)),
erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height);
}
fill_expolygons_generate_paths(
// Destination
base_layer.extrusions,
// Regions to fill
STDMOVE(to_infill),
std::move(to_infill),
// Filler and its parameters
filler, density,
// Extrusion parameters

View File

@ -18,7 +18,7 @@
// Uses a unique opengl context
#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0)
// Disable synchronization of unselected instances
#define DISABLE_INSTANCES_SYNCH (1 && ENABLE_1_42_0)
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0)
// Modified camera target behavior
#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0)
// Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume

View File

@ -272,9 +272,9 @@ void TriangleMesh::rotate(float angle, const Vec3d& axis)
if (angle == 0.f)
return;
Vec3f axis_norm = axis.cast<float>().normalized();
Transform3f m = Transform3f::Identity();
m.rotate(Eigen::AngleAxisf(angle, axis_norm));
Vec3d axis_norm = axis.normalized();
Transform3d m = Transform3d::Identity();
m.rotate(Eigen::AngleAxisd(angle, axis_norm));
stl_transform(&stl, m);
}
@ -290,7 +290,7 @@ void TriangleMesh::mirror(const Axis &axis)
stl_invalidate_shared_vertices(&this->stl);
}
void TriangleMesh::transform(const Transform3f& t)
void TriangleMesh::transform(const Transform3d& t)
{
stl_transform(&stl, t);
}

View File

@ -49,7 +49,7 @@ public:
void mirror_x() { this->mirror(X); }
void mirror_y() { this->mirror(Y); }
void mirror_z() { this->mirror(Z); }
void transform(const Transform3f& t);
void transform(const Transform3d& t);
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;

View File

@ -47,19 +47,6 @@ typedef double coordf_t;
#define scale_(val) ((val) / SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON)
// Which C++ version is supported?
// For example, could optimized functions with move semantics be used?
#if __cplusplus==201402L
#define SLIC3R_CPPVER 14
#define STDMOVE(WHAT) std::move(WHAT)
#elif __cplusplus==201103L
#define SLIC3R_CPPVER 11
#define STDMOVE(WHAT) std::move(WHAT)
#else
#define SLIC3R_CPPVER 0
#define STDMOVE(WHAT) (WHAT)
#endif
#define SLIC3R_DEBUG_OUT_PATH_PREFIX "out/"
inline std::string debug_out_path(const char *name, ...)

View File

@ -186,7 +186,7 @@ int main(int argc, char **argv)
else
// Remove the previous extension and add .3mf extention.
outfile = outfile.substr(0, outfile.find_last_of('.')) + ".3mf";
store_3mf(outfile.c_str(), &model, nullptr, false);
store_3mf(outfile.c_str(), &model, nullptr);
boost::nowide::cout << "File file exported to " << outfile << std::endl;
} else if (cli_config.cut > 0) {
model.repair();

View File

@ -193,6 +193,7 @@ const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f };
const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f };
const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f };
const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f };
const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLVolume::GLVolume(float r, float g, float b, float a)
#if ENABLE_MODELVOLUME_TRANSFORM
@ -211,6 +212,7 @@ GLVolume::GLVolume(float r, float g, float b, float a)
, composite_id(-1)
, extruder_id(0)
, selected(false)
, disabled(false)
, is_active(true)
, zoom_to_volumes(true)
, shader_outside_printer_detection_enabled(false)
@ -252,6 +254,8 @@ void GLVolume::set_render_color()
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (disabled)
set_render_color(DISABLED_COLOR, 4);
else if (is_outside && shader_outside_printer_detection_enabled)
set_render_color(OUTSIDE_COLOR, 4);
else
@ -723,7 +727,11 @@ std::vector<int> GLVolumeCollection::load_object(
for (int instance_idx : instance_idxs) {
const ModelInstance *instance = model_object->instances[instance_idx];
#if ENABLE_MODELVOLUME_TRANSFORM
const TriangleMesh& mesh = model_volume->mesh;
#else
TriangleMesh mesh = model_volume->mesh;
#endif // ENABLE_MODELVOLUME_TRANSFORM
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
@ -758,7 +766,8 @@ std::vector<int> GLVolumeCollection::load_object(
v.is_modifier = ! model_volume->is_model_part();
v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
#if ENABLE_MODELVOLUME_TRANSFORM
v.set_transformation(instance->get_transformation());
v.set_instance_transformation(instance->get_transformation());
v.set_volume_transformation(model_volume->get_transformation());
#else
v.set_offset(instance->get_offset());
v.set_rotation(instance->get_rotation());
@ -833,7 +842,11 @@ int GLVolumeCollection::load_wipe_tower_preview(
else
v.indexed_vertex_array.load_mesh_flat_shading(mesh);
#if ENABLE_MODELVOLUME_TRANSFORM
v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0));
#else
v.set_offset(Vec3d(pos_x, pos_y, 0.0));
#endif // ENABLE_MODELVOLUME_TRANSFORM
// finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry().
v.bounding_box = v.indexed_vertex_array.bounding_box();

View File

@ -249,13 +249,15 @@ public:
static const float HOVER_COLOR[4];
static const float OUTSIDE_COLOR[4];
static const float SELECTED_OUTSIDE_COLOR[4];
static const float DISABLED_COLOR[4];
GLVolume(float r = 1.f, float g = 1.f, float b = 1.f, float a = 1.f);
GLVolume(const float *rgba) : GLVolume(rgba[0], rgba[1], rgba[2], rgba[3]) {}
private:
#if ENABLE_MODELVOLUME_TRANSFORM
Geometry::Transformation m_transformation;
Geometry::Transformation m_instance_transformation;
Geometry::Transformation m_volume_transformation;
#else
// Offset of the volume to be rendered.
Vec3d m_offset;
@ -294,6 +296,8 @@ public:
int extruder_id;
// Is this object selected?
bool selected;
// Is this object disabled from selection?
bool disabled;
// Whether or not this volume is active for rendering
bool is_active;
// Whether or not to use this volume when applying zoom_to_volumes()
@ -329,32 +333,59 @@ public:
void set_render_color();
#if ENABLE_MODELVOLUME_TRANSFORM
const Geometry::Transformation& get_transformation() const { return m_transformation; }
void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; set_bounding_boxes_as_dirty(); }
const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; }
void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); }
const Vec3d& get_offset() const { return m_transformation.get_offset(); }
double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
const Vec3d& get_instance_offset() const { return m_instance_transformation.get_offset(); }
double get_instance_offset(Axis axis) const { return m_instance_transformation.get_offset(axis); }
void set_offset(const Vec3d& offset) { m_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
void set_offset(Axis axis, double offset) { m_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
void set_instance_offset(const Vec3d& offset) { m_instance_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
void set_instance_offset(Axis axis, double offset) { m_instance_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
const Vec3d& get_rotation() const { return m_transformation.get_rotation(); }
double get_rotation(Axis axis) const { return m_transformation.get_rotation(axis); }
const Vec3d& get_instance_rotation() const { return m_instance_transformation.get_rotation(); }
double get_instance_rotation(Axis axis) const { return m_instance_transformation.get_rotation(axis); }
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
void set_instance_rotation(const Vec3d& rotation) { m_instance_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
void set_instance_rotation(Axis axis, double rotation) { m_instance_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
Vec3d get_instance_scaling_factor() const { return m_instance_transformation.get_scaling_factor(); }
double get_instance_scaling_factor(Axis axis) const { return m_instance_transformation.get_scaling_factor(axis); }
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
void set_scaling_factor(Axis axis, double scaling_factor) { m_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
void set_instance_scaling_factor(const Vec3d& scaling_factor) { m_instance_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
void set_instance_scaling_factor(Axis axis, double scaling_factor) { m_instance_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
const Vec3d& get_mirror() const { return m_transformation.get_mirror(); }
double get_mirror(Axis axis) const { return m_transformation.get_mirror(axis); }
const Vec3d& get_instance_mirror() const { return m_instance_transformation.get_mirror(); }
double get_instance_mirror(Axis axis) const { return m_instance_transformation.get_mirror(axis); }
void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); }
void set_instance_mirror(const Vec3d& mirror) { m_instance_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
void set_instance_mirror(Axis axis, double mirror) { m_instance_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); }
const Geometry::Transformation& get_volume_transformation() const { return m_volume_transformation; }
void set_volume_transformation(const Geometry::Transformation& transformation) { m_volume_transformation = transformation; set_bounding_boxes_as_dirty(); }
const Vec3d& get_volume_offset() const { return m_volume_transformation.get_offset(); }
double get_volume_offset(Axis axis) const { return m_volume_transformation.get_offset(axis); }
void set_volume_offset(const Vec3d& offset) { m_volume_transformation.set_offset(offset); set_bounding_boxes_as_dirty(); }
void set_volume_offset(Axis axis, double offset) { m_volume_transformation.set_offset(axis, offset); set_bounding_boxes_as_dirty(); }
const Vec3d& get_volume_rotation() const { return m_volume_transformation.get_rotation(); }
double get_volume_rotation(Axis axis) const { return m_volume_transformation.get_rotation(axis); }
void set_volume_rotation(const Vec3d& rotation) { m_volume_transformation.set_rotation(rotation); set_bounding_boxes_as_dirty(); }
void set_volume_rotation(Axis axis, double rotation) { m_volume_transformation.set_rotation(axis, rotation); set_bounding_boxes_as_dirty(); }
Vec3d get_volume_scaling_factor() const { return m_volume_transformation.get_scaling_factor(); }
double get_volume_scaling_factor(Axis axis) const { return m_volume_transformation.get_scaling_factor(axis); }
void set_volume_scaling_factor(const Vec3d& scaling_factor) { m_volume_transformation.set_scaling_factor(scaling_factor); set_bounding_boxes_as_dirty(); }
void set_volume_scaling_factor(Axis axis, double scaling_factor) { m_volume_transformation.set_scaling_factor(axis, scaling_factor); set_bounding_boxes_as_dirty(); }
const Vec3d& get_volume_mirror() const { return m_volume_transformation.get_mirror(); }
double get_volume_mirror(Axis axis) const { return m_volume_transformation.get_mirror(axis); }
void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); }
void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); }
#else
const Vec3d& get_rotation() const;
void set_rotation(const Vec3d& rotation);
@ -378,7 +409,7 @@ public:
int instance_idx() const { return this->composite_id % 1000; }
#if ENABLE_MODELVOLUME_TRANSFORM
const Transform3d& world_matrix() const { return m_transformation.get_matrix(); }
Transform3d world_matrix() const { return m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); }
#else
const Transform3f& world_matrix() const;
#endif // ENABLE_MODELVOLUME_TRANSFORM

View File

@ -1,6 +1,7 @@
#include "BackgroundSlicingProcess.hpp"
#include "GUI_App.hpp"
#include <wx/app.h>
#include <wx/event.h>
#include <wx/panel.h>
#include <wx/stdpaths.h>
@ -103,6 +104,15 @@ void BackgroundSlicingProcess::thread_proc()
// End of the background processing thread. The UI thread should join m_thread now.
}
void BackgroundSlicingProcess::thread_proc_safe()
{
try {
this->thread_proc();
} catch (...) {
wxTheApp->OnUnhandledException();
}
}
void BackgroundSlicingProcess::join_background_thread()
{
std::unique_lock<std::mutex> lck(m_mutex);
@ -127,7 +137,7 @@ bool BackgroundSlicingProcess::start()
if (m_state == STATE_INITIAL) {
// The worker thread is not running yet. Start it.
assert(! m_thread.joinable());
m_thread = std::thread([this]{this->thread_proc();});
m_thread = std::thread([this]{this->thread_proc_safe();});
// Wait until the worker thread is ready to execute the background processing task.
m_condition.wait(lck, [this](){ return m_state == STATE_IDLE; });
}

View File

@ -80,6 +80,7 @@ public:
private:
void thread_proc();
void thread_proc_safe();
void join_background_thread();
// To be called by Print::apply() through the Print::m_cancel_callback to stop the background
// processing before changing any data of running or finalized milestones.

File diff suppressed because it is too large Load Diff

View File

@ -358,9 +358,14 @@ public:
enum EMode : unsigned char
{
#if ENABLE_MODELVOLUME_TRANSFORM
Volume,
Instance
#else
Volume,
Instance,
Object
#endif // ENABLE_MODELVOLUME_TRANSFORM
};
enum EType : unsigned char
@ -368,9 +373,13 @@ public:
Invalid,
Empty,
WipeTower,
Modifier,
SingleModifier,
MultipleModifier,
SingleVolume,
MultipleVolume,
SingleFullObject,
SingleFullInstance,
MultipleFullInstance,
Mixed
};
@ -378,21 +387,57 @@ public:
struct VolumeCache
{
private:
#if ENABLE_MODELVOLUME_TRANSFORM
struct TransformCache
{
Vec3d position;
Vec3d rotation;
Vec3d scaling_factor;
Transform3d rotation_matrix;
Transform3d scale_matrix;
TransformCache();
explicit TransformCache(const Geometry::Transformation& transform);
};
TransformCache m_volume;
TransformCache m_instance;
#else
Vec3d m_position;
Vec3d m_rotation;
Vec3d m_scaling_factor;
Transform3d m_rotation_matrix;
Transform3d m_scale_matrix;
#endif // ENABLE_MODELVOLUME_TRANSFORM
public:
#if ENABLE_MODELVOLUME_TRANSFORM
VolumeCache() {}
VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform);
#else
VolumeCache();
VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor);
#endif // ENABLE_MODELVOLUME_TRANSFORM
#if ENABLE_MODELVOLUME_TRANSFORM
const Vec3d& get_volume_position() const { return m_volume.position; }
const Vec3d& get_volume_rotation() const { return m_volume.rotation; }
const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; }
const Transform3d& get_volume_rotation_matrix() const { return m_volume.rotation_matrix; }
const Transform3d& get_volume_scale_matrix() const { return m_volume.scale_matrix; }
const Vec3d& get_instance_position() const { return m_instance.position; }
const Vec3d& get_instance_rotation() const { return m_instance.rotation; }
const Vec3d& get_instance_scaling_factor() const { return m_instance.scaling_factor; }
const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; }
const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; }
#else
const Vec3d& get_position() const { return m_position; }
const Vec3d& get_rotation() const { return m_rotation; }
const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; }
const Transform3d& get_scale_matrix() const { return m_scale_matrix; }
#endif // ENABLE_MODELVOLUME_TRANSFORM
};
typedef std::map<unsigned int, VolumeCache> VolumesCache;
@ -435,14 +480,14 @@ public:
void add_instance(unsigned int object_idx, unsigned int instance_idx, bool as_single_selection = true);
void remove_instance(unsigned int object_idx, unsigned int instance_idx);
void add_volume(unsigned int object_idx, unsigned int volume_idx, bool as_single_selection = true);
void add_volume(unsigned int object_idx, unsigned int volume_idx, int instance_idx, bool as_single_selection = true);
void remove_volume(unsigned int object_idx, unsigned int volume_idx);
void clear();
bool is_empty() const { return m_type == Empty; }
bool is_wipe_tower() const { return m_type == WipeTower; }
bool is_modifier() const { return m_type == Modifier; }
bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); }
bool is_single_full_instance() const;
bool is_single_full_object() const { return m_type == SingleFullObject; }
bool is_mixed() const { return m_type == Mixed; }
@ -455,6 +500,9 @@ public:
int get_object_idx() const;
// Returns the instance id if the selection is from a single object and from a single instance, otherwise is -1
int get_instance_idx() const;
// Returns the indices of selected instances.
// Can only be called if selection is from a single object.
const InstanceIdxsList& get_instance_idxs() const;
const IndicesList& get_volume_idxs() const { return m_list; }
const GLVolume* get_volume(unsigned int volume_idx) const;
@ -466,13 +514,14 @@ public:
void translate(const Vec3d& displacement);
void rotate(const Vec3d& rotation);
void flattening_rotate(const Vec3d& normal);
void scale(const Vec3d& scale);
void mirror(Axis axis);
void translate(unsigned int object_idx, const Vec3d& displacement);
void translate(unsigned int object_idx, unsigned int instance_idx, const Vec3d& displacement);
void render(bool show_indirect_selection) const;
void render() const;
private:
void _update_valid();
@ -486,9 +535,10 @@ public:
void _remove_object(unsigned int object_idx);
void _calc_bounding_box() const;
void _render_selected_volumes() const;
void _render_unselected_instances() const;
void _render_synchronized_volumes() const;
void _render_bounding_box(const BoundingBoxf3& box, float* color) const;
void _synchronize_unselected_instances();
void _synchronize_unselected_volumes();
};
private:
@ -556,7 +606,7 @@ private:
Vec3d get_rotation() const;
void set_rotation(const Vec3d& rotation);
Vec3d get_flattening_rotation() const;
Vec3d get_flattening_normal() const;
void set_flattening_data(const ModelObject* model_object);

View File

@ -720,7 +720,11 @@ void GLGizmoScale3D::on_process_double_click()
void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
{
bool single_instance = selection.is_single_full_instance();
#if ENABLE_MODELVOLUME_TRANSFORM
Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>();
#else
Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast<float>() : 100.0f * m_scale.cast<float>();
#endif // ENABLE_MODELVOLUME_TRANSFORM
if ((single_instance && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging)
set_tooltip("X: " + format(scale(0), 4) + "%");
@ -762,10 +766,18 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const
#endif // ENABLE_MODELVOLUME_TRANSFORM
// gets angles from first selected volume
#if ENABLE_MODELVOLUME_TRANSFORM
angles = v->get_instance_rotation();
#else
angles = v->get_rotation();
#endif // ENABLE_MODELVOLUME_TRANSFORM
// consider rotation+mirror only components of the transform for offsets
#if ENABLE_MODELVOLUME_TRANSFORM
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
#else
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror());
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
else
box = selection.get_bounding_box();
@ -1165,38 +1177,41 @@ void GLGizmoFlatten::on_start_dragging(const GLCanvas3D::Selection& selection)
void GLGizmoFlatten::on_render(const GLCanvas3D::Selection& selection) const
{
// the dragged_offset is a vector measuring where was the object moved
// with the gizmo being on. This is reset in set_flattening_data and
// does not work correctly when there are multiple copies.
// The planes are rendered incorrectly when the object is being moved. We better won't render anything in that case.
// This indeed has a better solution (to be implemented when there is more time)
Vec3d dragged_offset(Vec3d::Zero());
if (m_starting_center == Vec3d::Zero())
m_starting_center = selection.get_bounding_box().center();
dragged_offset = selection.get_bounding_box().center() - m_starting_center;
if (dragged_offset.norm() > 0.001)
return;
::glEnable(GL_BLEND);
::glEnable(GL_DEPTH_TEST);
::glDisable(GL_CULL_FACE);
for (int i=0; i<(int)m_planes.size(); ++i) {
if (i == m_hover_id)
::glColor4f(0.9f, 0.9f, 0.9f, 0.75f);
else
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
if (selection.is_from_single_object()) {
const std::set<int>& instances_list = selection.get_instance_idxs();
int instance_idx = selection.get_instance_idx();
if ((instance_idx != -1) && (m_model_object != nullptr))
{
if (!instances_list.empty() && m_model_object) {
for (const int instance_idx : instances_list) {
Transform3d m = m_model_object->instances[instance_idx]->get_matrix();
m.pretranslate(dragged_offset);
::glPushMatrix();
::glMultMatrixd(m.data());
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
::glVertex3dv(vertex.data());
for (int i=0; i<(int)m_planes.size(); ++i) {
if (i == m_hover_id)
::glColor4f(0.9f, 0.9f, 0.9f, 0.75f);
else
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
m.pretranslate(dragged_offset);
::glPushMatrix();
::glMultMatrixd(m.data());
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
::glVertex3dv(vertex.data());
::glEnd();
::glPopMatrix();
}
}
::glEnd();
::glPopMatrix();
}
}
@ -1208,22 +1223,21 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio
{
::glEnable(GL_DEPTH_TEST);
::glDisable(GL_CULL_FACE);
for (unsigned int i = 0; i < m_planes.size(); ++i)
{
::glColor3f(1.0f, 1.0f, picking_color_component(i));
int instance_idx = selection.get_instance_idx();
if ((instance_idx != -1) && (m_model_object != nullptr))
{
::glPushMatrix();
::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data());
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
{
::glVertex3dv(vertex.data());
if (selection.is_from_single_object()) {
const std::set<int>& instances_list = selection.get_instance_idxs();
if (!instances_list.empty() && m_model_object) {
for (const int instance_idx : instances_list) {
for (int i=0; i<(int)m_planes.size(); ++i) {
::glColor3f(1.0f, 1.0f, picking_color_component(i));
::glPushMatrix();
::glMultMatrixd(m_model_object->instances[instance_idx]->get_matrix().data());
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
::glVertex3dv(vertex.data());
::glEnd();
::glPopMatrix();
}
}
::glEnd();
::glPopMatrix();
}
}
@ -1233,9 +1247,10 @@ void GLGizmoFlatten::on_render_for_picking(const GLCanvas3D::Selection& selectio
void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
{
m_starting_center = Vec3d::Zero();
bool object_changed = m_model_object != model_object;
m_model_object = model_object;
if (is_plane_update_necessary())
if (object_changed && is_plane_update_necessary())
update_planes();
}
@ -1243,7 +1258,15 @@ void GLGizmoFlatten::update_planes()
{
TriangleMesh ch;
for (const ModelVolume* vol : m_model_object->volumes)
#if ENABLE_MODELVOLUME_TRANSFORM
{
TriangleMesh vol_ch = vol->get_convex_hull();
vol_ch.transform(vol->get_matrix());
ch.merge(vol_ch);
}
#else
ch.merge(vol->get_convex_hull());
#endif // ENABLE_MODELVOLUME_TRANSFORM
ch = ch.convex_hull_3d();
@ -1438,20 +1461,14 @@ bool GLGizmoFlatten::is_plane_update_necessary() const
return false;
}
Vec3d GLGizmoFlatten::get_flattening_rotation() const
Vec3d GLGizmoFlatten::get_flattening_normal() const
{
// calculates the rotations in model space, taking in account the scaling factors
Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m = m_model_object->instances.front()->get_matrix(true, true).matrix().block(0, 0, 3, 3).inverse().transpose();
Eigen::Quaterniond q;
Vec3d angles = Geometry::extract_euler_angles(q.setFromTwoVectors(m * m_normal, -Vec3d::UnitZ()).toRotationMatrix());
Vec3d out = m_normal;
m_normal = Vec3d::Zero();
m_starting_center = Vec3d::Zero();
return angles;
return out;
}
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent)
: GLGizmoBase(parent), m_starting_center(Vec3d::Zero())
{
@ -1478,11 +1495,14 @@ bool GLGizmoSlaSupports::on_init()
void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object)
{
m_starting_center = Vec3d::Zero();
m_model_object = model_object;
m_model_object_matrix = model_object->instances.front()->get_matrix();
if (is_mesh_update_necessary())
update_mesh();
if (model_object != nullptr)
{
m_starting_center = Vec3d::Zero();
m_model_object = model_object;
m_model_object_matrix = model_object->instances.front()->get_matrix();
if (is_mesh_update_necessary())
update_mesh();
}
}
void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const

View File

@ -359,6 +359,7 @@ private:
std::vector<PlaneData> m_planes;
mutable Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
std::vector<const Transform3d*> instances_matrices;
void update_planes();
bool is_plane_update_necessary() const;
@ -367,12 +368,12 @@ public:
explicit GLGizmoFlatten(GLCanvas3D& parent);
void set_flattening_data(const ModelObject* model_object);
Vec3d get_flattening_rotation() const;
Vec3d get_flattening_normal() const;
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return selection.is_single_full_instance(); }
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const { return (selection.is_from_single_object() && !selection.is_wipe_tower() && !selection.is_modifier()); }
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
virtual void on_update(const Linef3& mouse_ray, const Point* mouse_pos) {}
virtual void on_render(const GLCanvas3D::Selection& selection) const;

View File

@ -60,7 +60,7 @@ enum ConfigMenuIDs {
class Tab;
static wxString dots("", wxConvUTF8);
static wxString dots("", wxConvUTF8);
class GUI_App : public wxApp
{

View File

@ -36,11 +36,11 @@ ObjectList::ObjectList(wxWindow* parent) :
CATEGORY_ICON[L("Advanced")] = wxBitmap(from_u8(var("wand.png")), wxBITMAP_TYPE_PNG);
}
init_icons();
// create control
create_objects_ctrl();
init_icons();
// describe control behavior
Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) {
selection_changed();
@ -213,17 +213,27 @@ void ObjectList::update_extruder_in_config(const wxString& selection)
void ObjectList::init_icons()
{
m_bmp_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
m_bmp_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
m_bmp_modifiermesh = wxBitmap(from_u8(var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
m_bmp_solidmesh = wxBitmap(from_u8(var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
m_bmp_support_enforcer = wxBitmap(from_u8(var("support_enforcer_.png")), wxBITMAP_TYPE_PNG);
m_bmp_support_blocker = wxBitmap(from_u8(var("support_blocker_.png")), wxBITMAP_TYPE_PNG);
m_bmp_vector.reserve(4); // bitmaps for different types of parts
m_bmp_vector.push_back(&m_bmp_solidmesh); // Add part
m_bmp_vector.push_back(&m_bmp_modifiermesh); // Add modifier
m_bmp_vector.push_back(&m_bmp_support_enforcer); // Add support enforcer
m_bmp_vector.push_back(&m_bmp_support_blocker); // Add support blocker
m_objects_model->SetVolumeBitmaps(m_bmp_vector);
// init icon for manifold warning
m_bmp_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
m_bmp_manifold_warning = wxBitmap(from_u8(var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
// init bitmap for "Split to sub-objects" context menu
m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG);
m_bmp_split = wxBitmap(from_u8(var("split.png")), wxBITMAP_TYPE_PNG);
// init bitmap for "Add Settings" context menu
m_bmp_cog = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
m_bmp_cog = wxBitmap(from_u8(var("cog.png")), wxBITMAP_TYPE_PNG);
}
@ -377,7 +387,8 @@ void ObjectList::on_drop(wxDataViewEvent &event)
wxDataViewItem item(event.GetItem());
// only allow drops for item, not containers
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
if (m_selected_object_id < 0 ||
item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->GetItemType(item) != itVolume) {
event.Veto();
return;
@ -518,16 +529,24 @@ void ObjectList::get_settings_choice(wxMenu *menu, int id, bool is_part)
wxGetApp().obj_manipul()->update_settings_list();
}
void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id) {
void ObjectList::menu_item_add_generic(wxMenuItem* &menu, int id, const int type) {
auto sub_menu = new wxMenu;
const wxString menu_load = _(L("Load")) +" "+ dots;
sub_menu->Append(new wxMenuItem(sub_menu, id++, menu_load));
sub_menu->AppendSeparator();
std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") };
for (auto& item : menu_items)
sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item)));
sub_menu->Append(new wxMenuItem(sub_menu, id++, _(item)));
#ifndef __WXMSW__
sub_menu->Bind(wxEVT_MENU, [this, sub_menu](wxEvent &event) {
load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString());
sub_menu->Bind(wxEVT_MENU, [sub_menu, type, menu_load, this](wxEvent &event) {
auto selection = sub_menu->GetLabel(event.GetId());
if (selection == menu_load)
load_subobject(type);
else
load_generic_subobject(selection.ToStdString(), type);
});
#endif //no __WXMSW__
@ -552,55 +571,53 @@ wxMenuItem* ObjectList::menu_item_settings(wxMenu* menu, int id, const bool is_p
wxMenu* ObjectList::create_add_part_popupmenu()
{
wxMenu *menu = new wxMenu;
std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") };
// Note: id accords to type of the sub-object, so sequence of the menu items is important
std::vector<std::string> menu_object_types_items = {L("Add part"), // ~ModelVolume::MODEL_PART
L("Add modifier"), // ~ModelVolume::PARAMETER_MODIFIER
L("Add support enforcer"), // ~ModelVolume::SUPPORT_ENFORCER
L("Add support bloker") }; // ~ModelVolume::SUPPORT_BLOCKER
const int obj_types_count = menu_object_types_items.size();
const int generics_count = 5; // "Load ...", "Box", "Cylinder", "Sphere", "Slab"
wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size() + 4 + 2);
wxWindowID config_id_base = NewControlId(generics_count*obj_types_count + 2);
int i = 0;
for (auto& item : menu_items) {
auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item));
menu_item->SetBitmap(i == 0 ? m_bmp_solidmesh : m_bmp_modifiermesh);
if (item == "Add generic")
menu_item_add_generic(menu_item, config_id_base + i);
// Add first 4 menu items
for (int type = 0; type < obj_types_count; type++) {
auto& item = menu_object_types_items[type];
auto menu_item = new wxMenuItem(menu, config_id_base + type, _(item));
menu_item->SetBitmap(*m_bmp_vector[type]);
menu_item_add_generic(menu_item, config_id_base + type*generics_count, type);
menu->Append(menu_item);
i++;
}
// Split object to parts
menu->AppendSeparator();
auto menu_item = menu_item_split(menu, config_id_base + i + 4);
auto menu_item = menu_item_split(menu, config_id_base + obj_types_count * generics_count);
menu->Append(menu_item);
menu_item->Enable(is_splittable_object(false));
// Settings
menu->AppendSeparator();
// Append settings popupmenu
menu->Append(menu_item_settings(menu, config_id_base + i + 5, false));
menu->Append(menu_item_settings(menu, config_id_base + obj_types_count * generics_count+1, false));
menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) {
switch (event.GetId() - config_id_base) {
case 0:
load_subobject();
break;
case 1:
load_subobject(true);
break;
case 2:
case 3:
case 4:
case 5:
case 6:
#ifdef __WXMSW__
load_lambda(menu->GetLabel(event.GetId()).ToStdString());
#endif // __WXMSW__
break;
case 7: //3:
menu->Bind(wxEVT_MENU, [config_id_base, menu, obj_types_count, generics_count, this](wxEvent &event) {
auto selection = event.GetId() - config_id_base;
if ( selection == 0 * generics_count || // ~ModelVolume::MODEL_PART
selection == 1 * generics_count || // ~ModelVolume::PARAMETER_MODIFIER
selection == 2 * generics_count || // ~ModelVolume::SUPPORT_ENFORCER
selection == 3 * generics_count ) // ~ModelVolume::SUPPORT_BLOCKER
load_subobject(int(selection / generics_count));
else if ( selection == obj_types_count * generics_count)
split(false);
break;
default:
#ifdef __WXMSW__
else if ( selection > obj_types_count * generics_count) // "Add Settings" is selected
get_settings_choice(menu, event.GetId(), false);
else // Some generic model is selected
load_generic_subobject(menu->GetLabel(event.GetId()).ToStdString(), int(selection / generics_count));
#endif // __WXMSW__
break;
}
});
return menu;
@ -609,24 +626,33 @@ wxMenu* ObjectList::create_add_part_popupmenu()
wxMenu* ObjectList::create_part_settings_popupmenu()
{
wxMenu *menu = new wxMenu;
wxWindowID config_id_base = wxWindow::NewControlId(2);
wxWindowID config_id_base = NewControlId(3);
auto menu_item = menu_item_split(menu, config_id_base);
menu->Append(menu_item);
menu_item->Enable(is_splittable_object(true));
// Append change part type
menu->AppendSeparator();
menu->Append(new wxMenuItem(menu, config_id_base + 1, _(L("Change type"))));
// Append settings popupmenu
menu->Append(menu_item_settings(menu, config_id_base + 1, true));
menu->AppendSeparator();
menu_item = menu_item_settings(menu, config_id_base + 2, true);
menu->Append(menu_item);
menu_item->Enable(get_selected_model_volume()->type() <= ModelVolume::PARAMETER_MODIFIER);
menu->Bind(wxEVT_MENU, [config_id_base, menu, this](wxEvent &event) {
switch (event.GetId() - config_id_base) {
case 0:
split(true);
break;
default:{
case 1:
change_part_type();
break;
default:
get_settings_choice(menu, event.GetId(), true);
break; }
break;
}
});
@ -655,31 +681,21 @@ wxMenu* ObjectList::create_add_settings_popupmenu(bool is_part)
return menu;
}
// Load SubObjects (parts and modifiers)
void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* = false*/)
void ObjectList::load_subobject(int type)
{
auto item = GetSelection();
if (!item)
return;
int obj_idx = -1;
if (m_objects_model->GetParent(item) == wxDataViewItem(0))
obj_idx = m_objects_model->GetIdByItem(item);
else
if (!item || m_objects_model->GetParent(item) != wxDataViewItem(0))
return;
int obj_idx = m_objects_model->GetIdByItem(item);
if (obj_idx < 0) return;
wxArrayString part_names;
if (is_lambda)
load_lambda((*m_objects)[obj_idx], part_names, is_modifier);
else
load_part((*m_objects)[obj_idx], part_names, is_modifier);
load_part((*m_objects)[obj_idx], part_names, type);
parts_changed(obj_idx);
for (int i = 0; i < part_names.size(); ++i) {
const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i),
is_modifier ? m_bmp_modifiermesh : m_bmp_solidmesh);
const wxDataViewItem sel_item = m_objects_model->AddVolumeChild(item, part_names.Item(i), /**m_bmp_vector[*/type/*]*/);
if (i == part_names.size() - 1)
select_item(sel_item);
@ -688,11 +704,12 @@ void ObjectList::load_subobject(bool is_modifier /*= false*/, bool is_lambda/* =
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
// selection_changed();
#endif //no __WXOSX__//__WXMSW__
}
void ObjectList::load_part( ModelObject* model_object,
wxArrayString& part_names,
const bool is_modifier)
int type)
{
wxWindow* parent = wxGetApp().tab_panel()->GetPage(0);
@ -717,19 +734,27 @@ void ObjectList::load_part( ModelObject* model_object,
if (model_object->origin_translation != Vec3d::Zero())
{
object->center_around_origin();
#if !ENABLE_MODELVOLUME_TRANSFORM
object->ensure_on_bed();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
delta = model_object->origin_translation - object->origin_translation;
}
for (auto volume : object->volumes) {
#if ENABLE_MODELVOLUME_TRANSFORM
Vec3d shift = volume->mesh.bounding_box().center();
volume->translate_geometry(-shift);
volume->translate(delta + shift);
#endif // ENABLE_MODELVOLUME_TRANSFORM
auto new_volume = model_object->add_volume(*volume);
new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
boost::filesystem::path(input_file).filename().string();
new_volume->set_type(static_cast<ModelVolume::Type>(type));
new_volume->name = boost::filesystem::path(input_file).filename().string();
part_names.Add(new_volume->name);
#if !ENABLE_MODELVOLUME_TRANSFORM
if (delta != Vec3d::Zero())
new_volume->translate(delta);
#endif // !ENABLE_MODELVOLUME_TRANSFORM
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
@ -738,98 +763,46 @@ void ObjectList::load_part( ModelObject* model_object,
}
}
}
}
void ObjectList::load_lambda( ModelObject* model_object,
wxArrayString& part_names,
const bool is_modifier)
void ObjectList::load_generic_subobject(const std::string& type_name, const int type)
{
auto dlg = new LambdaObjectDialog(GetMainWindow());
if (dlg->ShowModal() == wxID_CANCEL) {
m_parts_changed = false;
return;
}
std::string name = "lambda-";
TriangleMesh mesh;
auto params = dlg->ObjectParameters();
switch (params.type)
{
case LambdaTypeBox:{
mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]);
name += "Box";
break; }
case LambdaTypeCylinder:{
mesh = make_cylinder(params.cyl_r, params.cyl_h);
name += "Cylinder";
break; }
case LambdaTypeSphere:{
mesh = make_sphere(params.sph_rho);
name += "Sphere";
break; }
case LambdaTypeSlab:{
const auto& size = model_object->bounding_box().size();
mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h);
// box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z);
name += "Slab";
break; }
default:
break;
}
mesh.repair();
auto new_volume = model_object->add_volume(mesh);
new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
new_volume->name = name;
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
part_names.Add(name);
m_parts_changed = true;
}
void ObjectList::load_lambda(const std::string& type_name)
{
if (m_selected_object_id < 0) return;
auto dlg = new LambdaObjectDialog(GetMainWindow(), type_name);
if (dlg->ShowModal() == wxID_CANCEL)
return;
const auto obj_idx = get_selected_obj_idx();
if (obj_idx < 0) return;
const std::string name = "lambda-" + type_name;
TriangleMesh mesh;
const auto params = dlg->ObjectParameters();
auto& bed_shape = wxGetApp().preset_bundle->printers.get_edited_preset().config.option<ConfigOptionPoints>("bed_shape")->values;
const auto& sz = BoundingBoxf(bed_shape).size();
const auto side = 0.1 * std::max(sz(0), sz(1));
if (type_name == _("Box"))
mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]);
mesh = make_cube(side, side, side);
else if (type_name == _("Cylinder"))
mesh = make_cylinder(params.cyl_r, params.cyl_h);
mesh = make_cylinder(0.5*side, side);
else if (type_name == _("Sphere"))
mesh = make_sphere(params.sph_rho);
mesh = make_sphere(side, PI/18);
else if (type_name == _("Slab")) {
const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size();
mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h);
const auto& size = (*m_objects)[obj_idx]->bounding_box().size();
mesh = make_cube(size(0)*1.5, size(1)*1.5, size(2)*0.5);
// box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z);
mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, 0);
}
mesh.repair();
auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh);
new_volume->set_type(ModelVolume::PARAMETER_MODIFIER);
auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh);
new_volume->set_type(static_cast<ModelVolume::Type>(type));
new_volume->name = name;
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
m_parts_changed = true;
parts_changed(m_selected_object_id);
parts_changed(obj_idx);
select_item(m_objects_model->AddVolumeChild(GetSelection(),
name, m_bmp_modifiermesh));
select_item(m_objects_model->AddVolumeChild(GetSelection(), name, type));
#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
selection_changed();
#endif //no __WXOSX__ //__WXMSW__
@ -927,8 +900,10 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
void ObjectList::split(const bool split_part)
{
const auto item = GetSelection();
if (!item || m_selected_object_id < 0)
const int obj_idx = get_selected_obj_idx();
if (!item || obj_idx < 0)
return;
ModelVolume* volume;
if (!get_volume_by_item(split_part, item, volume)) return;
DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
@ -938,52 +913,54 @@ void ObjectList::split(const bool split_part)
return;
}
auto model_object = (*m_objects)[m_selected_object_id];
auto model_object = (*m_objects)[obj_idx];
if (split_part) {
auto parent = m_objects_model->GetParent(item);
m_objects_model->DeleteChildren(parent);
auto parent = m_objects_model->GetTopParent(item);
if (parent)
m_objects_model->DeleteVolumeChildren(parent);
else
parent = item;
for (auto id = 0; id < model_object->volumes.size(); id++)
m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name,
model_object->volumes[id]->is_modifier() ? m_bmp_modifiermesh : m_bmp_solidmesh,
model_object->volumes[id]->config.has("extruder") ?
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
for (auto id = 0; id < model_object->volumes.size(); id++) {
const auto vol_item = m_objects_model->AddVolumeChild(parent, model_object->volumes[id]->name,
model_object->volumes[id]->is_modifier() ?
ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART,
model_object->volumes[id]->config.has("extruder") ?
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
// add settings to the part, if it has those
auto opt_keys = model_object->volumes[id]->config.keys();
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
select_item(m_objects_model->AddSettingsChild(vol_item));
Collapse(vol_item);
}
}
if (parent == item)
Expand(parent);
}
else {
for (auto id = 0; id < model_object->volumes.size(); id++)
m_objects_model->AddVolumeChild(item, model_object->volumes[id]->name,
m_bmp_solidmesh,
model_object->volumes[id]->config.has("extruder") ?
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
Expand(item);
}
m_parts_changed = true;
parts_changed(m_selected_object_id);
// restores selection
_3DScene::get_canvas(wxGetApp().canvas3D())->get_selection().add_object(m_selected_object_id);
parts_changed(obj_idx);
}
bool ObjectList::get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume)
{
if (!item || m_selected_object_id < 0)
auto obj_idx = get_selected_obj_idx();
if (!item || obj_idx < 0)
return false;
const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
// object is selected
if (volume_id < 0) {
if (split_part) return false;
volume = (*m_objects)[m_selected_object_id]->volumes[0];
if ( split_part || (*m_objects)[obj_idx]->volumes.size() > 1 )
return false;
volume = (*m_objects)[obj_idx]->volumes[0];
}
// volume is selected
else
volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
if (volume)
return true;
return false;
volume = (*m_objects)[obj_idx]->volumes[volume_id];
return true;
}
bool ObjectList::is_splittable_object(const bool split_part)
@ -991,20 +968,14 @@ bool ObjectList::is_splittable_object(const bool split_part)
const wxDataViewItem item = GetSelection();
if (!item) return false;
wxDataViewItemArray children;
if (!split_part && m_objects_model->GetChildren(item, children) > 0)
return false;
ModelVolume* volume;
if (!get_volume_by_item(split_part, item, volume) || !volume)
return false;
TriangleMeshPtrs meshptrs = volume->mesh.split();
bool splittable = meshptrs.size() > 1;
for (TriangleMesh* m : meshptrs)
{
delete m;
}
for (TriangleMesh* m : meshptrs) { delete m; }
return splittable;
}
@ -1017,7 +988,7 @@ void ObjectList::part_settings_changed()
void ObjectList::parts_changed(int obj_idx)
{
wxGetApp().plater()->changed_object(get_selected_obj_idx());
wxGetApp().plater()->changed_object(obj_idx);
m_parts_changed = false;
}
@ -1105,19 +1076,28 @@ void ObjectList::add_object_to_list(size_t obj_idx)
m_objects_model->SetValue(variant, item, 0);
}
// add volumes to the object
if (model_object->volumes.size() > 1) {
for (auto id = 0; id < model_object->volumes.size(); id++)
m_objects_model->AddVolumeChild(item,
model_object->volumes[id]->name,
m_bmp_solidmesh,
ModelVolume::MODEL_PART,
model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
false);
Expand(item);
}
// add instances to the object, if it has those
if (model_object->instances.size()>1)
increase_object_instances(obj_idx, model_object->instances.size());
// add settings to the object, if it has those
auto opt_keys = model_object->config.keys();
if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) {
select_item(m_objects_model->AddSettingsChild(item));
Collapse(item);
}
#ifndef __WXOSX__
selection_changed();
#endif //__WXMSW__
@ -1227,11 +1207,18 @@ void ObjectList::update_selections()
auto& selection = _3DScene::get_canvas(wxGetApp().canvas3D())->get_selection();
wxDataViewItemArray sels;
for (auto idx: selection.get_volume_idxs())
{
const auto gl_vol = selection.get_volume(idx);
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
if (selection.is_single_full_object()) {
for (auto idx : selection.get_volume_idxs()) {
const auto gl_vol = selection.get_volume(idx);
sels.Add(m_objects_model->GetItemByVolumeId(gl_vol->object_idx(), gl_vol->volume_idx()));
}
}
else if (selection.is_single_full_instance()) {
for (auto idx : selection.get_instance_idxs()) {
sels.Add(m_objects_model->GetItemByInstanceId(selection.get_object_idx(), idx));
}
}
select_items(sels);
}
@ -1256,7 +1243,7 @@ void ObjectList::update_selections_on_canvas()
if (m_objects_model->GetItemType(item) == itVolume) {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetParent(item));
const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
selection.add_volume(obj_idx, vol_idx, as_single_selection);
selection.add_volume(obj_idx, vol_idx, 0, as_single_selection);
}
else if (m_objects_model->GetItemType(item) == itInstance) {
const int obj_idx = m_objects_model->GetIdByItem(m_objects_model->GetTopParent(item));
@ -1334,5 +1321,53 @@ void ObjectList::fix_multiselection_conflicts()
m_prevent_list_events = false;
}
ModelVolume* ObjectList::get_selected_model_volume()
{
auto item = GetSelection();
if (!item || m_objects_model->GetItemType(item) != itVolume)
return nullptr;
const auto vol_idx = m_objects_model->GetVolumeIdByItem(item);
const auto obj_idx = get_selected_obj_idx();
if (vol_idx < 0 || obj_idx < 0)
return nullptr;
return (*m_objects)[obj_idx]->volumes[vol_idx];
}
void ObjectList::change_part_type()
{
ModelVolume* volume = get_selected_model_volume();
if (!volume)
return;
const auto type = volume->type();
const wxString names[] = { "Part", "Modifier", "Support Enforcer", "Support Blocker" };
auto new_type = wxGetSingleChoiceIndex("Type: ", _(L("Select type of part")), wxArrayString(4, names), type);
if (new_type == type || new_type < 0)
return;
const auto item = GetSelection();
volume->set_type(static_cast<ModelVolume::Type>(new_type));
m_objects_model->SetVolumeType(item, new_type);
m_parts_changed = true;
parts_changed(get_selected_obj_idx());
// Update settings showing, if we have it
//(we show additional settings for Part and Modifier and hide it for Support Blocker/Enforcer)
const auto settings_item = m_objects_model->GetSettingsItem(item);
if (settings_item &&
new_type == ModelVolume::SUPPORT_ENFORCER || new_type == ModelVolume::SUPPORT_BLOCKER) {
m_objects_model->Delete(settings_item);
}
else if (!settings_item &&
new_type == ModelVolume::MODEL_PART || new_type == ModelVolume::PARAMETER_MODIFIER) {
select_item(m_objects_model->AddSettingsChild(item));
}
}
} //namespace GUI
} //namespace Slic3r

View File

@ -28,10 +28,14 @@ class ObjectList : public wxDataViewCtrl
wxBitmap m_bmp_modifiermesh;
wxBitmap m_bmp_solidmesh;
wxBitmap m_bmp_support_enforcer;
wxBitmap m_bmp_support_blocker;
wxBitmap m_bmp_manifold_warning;
wxBitmap m_bmp_cog;
wxBitmap m_bmp_split;
std::vector<wxBitmap*> m_bmp_vector;
int m_selected_object_id = -1;
bool m_prevent_list_events = false; // We use this flag to avoid circular event handling Select()
// happens to fire a wxEVT_LIST_ITEM_SELECTED on OSX, whose event handler
@ -78,17 +82,16 @@ public:
void on_drop(wxDataViewEvent &event);
void get_settings_choice(wxMenu *menu, int id, bool is_part);
void menu_item_add_generic(wxMenuItem* &menu, int id);
void menu_item_add_generic(wxMenuItem* &menu, int id, const int type);
wxMenuItem* menu_item_split(wxMenu* menu, int id);
wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part);
wxMenu* create_add_part_popupmenu();
wxMenu* create_part_settings_popupmenu();
wxMenu* create_add_settings_popupmenu(bool is_part);
void load_subobject(bool is_modifier = false, bool is_lambda = false);
void load_part(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier);
void load_lambda(ModelObject* model_object, wxArrayString& part_names, const bool is_modifier);
void load_lambda(const std::string& type_name);
void load_subobject(int type);
void load_part(ModelObject* model_object, wxArrayString& part_names, int type);
void load_generic_subobject(const std::string& type_name, const int type);
void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config();
void del_instances_from_object(const int obj_idx);
@ -142,6 +145,9 @@ public:
void select_all();
// correct current selections to avoid of the possible conflicts
void fix_multiselection_conflicts();
ModelVolume* get_selected_model_volume();
void change_part_type();
};

View File

@ -268,6 +268,9 @@ void ObjectManipulation::update_settings_list()
void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection)
{
#if ENABLE_MODELVOLUME_TRANSFORM
if (selection.is_single_full_instance())
#else
if (selection.is_single_full_object())
{
auto obj_idx = selection.get_object_idx();
@ -284,30 +287,49 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele
reset_settings_value();
}
else if (selection.is_single_full_instance())
#endif // ENABLE_MODELVOLUME_TRANSFORM
{
// all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
#if ENABLE_MODELVOLUME_TRANSFORM
update_position_value(volume->get_instance_offset());
update_rotation_value(volume->get_instance_rotation());
update_scale_value(volume->get_instance_scaling_factor());
#else
update_position_value(volume->get_offset());
update_rotation_value(volume->get_rotation());
update_scale_value(volume->get_scaling_factor());
#endif // ENABLE_MODELVOLUME_TRANSFORM
m_og->enable();
}
else if (selection.is_wipe_tower())
{
// the selection contains a single volume
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
#if ENABLE_MODELVOLUME_TRANSFORM
update_position_value(volume->get_volume_offset());
update_rotation_value(volume->get_volume_rotation());
update_scale_value(volume->get_volume_scaling_factor());
#else
update_position_value(volume->get_offset());
update_rotation_value(volume->get_rotation());
update_scale_value(volume->get_scaling_factor());
#endif // ENABLE_MODELVOLUME_TRANSFORM
m_og->enable();
}
else if (selection.is_modifier())
{
// the selection contains a single volume
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
#if ENABLE_MODELVOLUME_TRANSFORM
update_position_value(volume->get_volume_offset());
update_rotation_value(volume->get_volume_rotation());
update_scale_value(volume->get_volume_scaling_factor());
#else
update_position_value(volume->get_offset());
update_rotation_value(volume->get_rotation());
update_scale_value(volume->get_scaling_factor());
#endif // ENABLE_MODELVOLUME_TRANSFORM
m_og->enable();
}
else

View File

@ -1,5 +1,6 @@
#include "../../libslic3r/libslic3r.h"
#include "GUI_Preview.hpp"
#include "GUI_App.hpp"
#include "GUI.hpp"
#include "AppConfig.hpp"
#include "3DScene.hpp"
@ -22,6 +23,7 @@
namespace Slic3r {
namespace GUI {
Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, Print* print, GCodePreviewData* gcode_preview_data)
: m_canvas(nullptr)
, m_double_slider_sizer(nullptr)
@ -482,6 +484,11 @@ void Preview::create_double_slider()
if (IsShown())
m_canvas->Refresh();
});
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
auto& config = wxGetApp().preset_bundle->project_config;
((config.option<ConfigOptionFloats>("colorprint_heights"))->values) = (m_slider->GetTicksValues());
});
}
void Preview::update_double_slider(bool force_sliders_full_range)
@ -495,6 +502,11 @@ void Preview::update_double_slider(bool force_sliders_full_range)
m_slider->SetMaxValue(layers_z.size() - 1);
m_slider->SetSliderValues(values);
const auto& config = wxGetApp().preset_bundle->project_config;
const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values;
m_slider->SetTicksValues(ticks_from_config);
set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high);
}
@ -515,6 +527,15 @@ void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values,
break;
}
}
// All ticks that would end up outside the slider range should be erased.
// TODO: this should probably be placed into more appropriate part of code,
// this way it relies on the Preview tab being active.
auto& config = wxGetApp().preset_bundle->project_config;
std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values;
ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(),
[values](double val) { return values.back().second < val; }),
ticks_from_config.end());
}
void Preview::set_double_slider_thumbs(const bool force_sliders_full_range,

View File

@ -49,15 +49,15 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
def.type = coFloat;
def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("L");
def.label = L("Length");
Option option(def, "l");
optgroup->append_single_option_line(option);
def.label = L("W");
def.label = L("Width");
option = Option(def, "w");
optgroup->append_single_option_line(option);
def.label = L("H");
def.label = L("Height");
option = Option(def, "h");
optgroup->append_single_option_line(option);
}
@ -112,7 +112,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
def.type = coFloat;
def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("H");
def.label = L("Height");
auto option = Option(def, "slab_h");
optgroup->append_single_option_line(option);

View File

@ -596,8 +596,7 @@ void MainFrame::load_config_file(wxString file/* = wxEmptyString*/)
// if (Slic3r::GUI::catch_error(this))
// return;
}
for (auto tab : m_options_tabs )
tab.second->load_current_preset();
wxGetApp().load_current_presets();
wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file;
}
@ -659,8 +658,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
}
// Load the currently selected preset into the GUI, update the preset selection box.
for (auto tab : m_options_tabs)
tab.second->load_current_preset();
wxGetApp().load_current_presets();
const auto message = wxString::Format(_(L("%d presets successfully imported.")), presets_imported);
Slic3r::GUI::show_info(this, message, "Info");

View File

@ -22,18 +22,18 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
// is the normal type.
if (opt.gui_type.compare("select") == 0) {
} else if (opt.gui_type.compare("select_open") == 0) {
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("color") == 0) {
m_fields.emplace(id, STDMOVE(ColourPicker::Create<ColourPicker>(parent(), opt, id)));
m_fields.emplace(id, std::move(ColourPicker::Create<ColourPicker>(parent(), opt, id)));
} else if (opt.gui_type.compare("f_enum_open") == 0 ||
opt.gui_type.compare("i_enum_open") == 0 ||
opt.gui_type.compare("i_enum_closed") == 0) {
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id)));
} else if (opt.gui_type.compare("slider") == 0) {
m_fields.emplace(id, STDMOVE(SliderCtrl::Create<SliderCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(SliderCtrl::Create<SliderCtrl>(parent(), opt, id)));
} else if (opt.gui_type.compare("i_spin") == 0) { // Spinctrl
} else if (opt.gui_type.compare("legend") == 0) { // StaticText
m_fields.emplace(id, STDMOVE(StaticText::Create<StaticText>(parent(), opt, id)));
m_fields.emplace(id, std::move(StaticText::Create<StaticText>(parent(), opt, id)));
} else {
switch (opt.type) {
case coFloatOrPercent:
@ -43,21 +43,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co
case coPercents:
case coString:
case coStrings:
m_fields.emplace(id, STDMOVE(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(TextCtrl::Create<TextCtrl>(parent(), opt, id)));
break;
case coBool:
case coBools:
m_fields.emplace(id, STDMOVE(CheckBox::Create<CheckBox>(parent(), opt, id)));
m_fields.emplace(id, std::move(CheckBox::Create<CheckBox>(parent(), opt, id)));
break;
case coInt:
case coInts:
m_fields.emplace(id, STDMOVE(SpinCtrl::Create<SpinCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(SpinCtrl::Create<SpinCtrl>(parent(), opt, id)));
break;
case coEnum:
m_fields.emplace(id, STDMOVE(Choice::Create<Choice>(parent(), opt, id)));
m_fields.emplace(id, std::move(Choice::Create<Choice>(parent(), opt, id)));
break;
case coPoints:
m_fields.emplace(id, STDMOVE(PointCtrl::Create<PointCtrl>(parent(), opt, id)));
m_fields.emplace(id, std::move(PointCtrl::Create<PointCtrl>(parent(), opt, id)));
break;
case coNone: break;
default:

View File

@ -1154,16 +1154,25 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_
try {
if (type_3mf || type_zip_amf) {
DynamicPrintConfig config;
config.apply(FullPrintConfig::defaults());
model = Slic3r::Model::read_from_archive(path.string(), &config, false);
Preset::normalize(config);
wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config));
for (const auto &kv : main_frame->options_tabs()) { kv.second->load_current_preset(); }
{
DynamicPrintConfig config_loaded;
model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, false);
if (! config_loaded.empty()) {
// Based on the printer technology field found in the loaded config, select the base for the config,
PrinterTechnology printer_technology = Preset::printer_technology(config_loaded);
config.apply(printer_technology == ptFFF ?
static_cast<const ConfigBase&>(FullPrintConfig::defaults()) :
static_cast<const ConfigBase&>(SLAFullPrintConfig::defaults()));
// and place the loaded config over the base.
config += std::move(config_loaded);
}
}
if (! config.empty()) {
Preset::normalize(config);
wxGetApp().preset_bundle->load_config_model(filename.string(), std::move(config));
wxGetApp().load_current_presets();
}
wxGetApp().app_config->update_config_dir(path.parent_path().string());
// forces the update of the config here, or it will invalidate the imported layer heights profile if done using the timer
// and if the config contains a "layer_height" different from the current defined one
// TODO:
// $self->async_apply_config;
} else {
model = Slic3r::Model::read_from_file(path.string(), nullptr, false);
for (auto obj : model.objects)
@ -1230,7 +1239,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path> &input_
std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects)
{
const BoundingBoxf bed_shape = bed_shape_bb();
#if !ENABLE_MODELVOLUME_TRANSFORM
const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast<double>(), 0.0);
#endif // !ENABLE_MODELVOLUME_TRANSFORM
const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast<double>(), 1.0);
bool need_arrange = false;
@ -1249,8 +1260,12 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode
// add a default instance and center object around origin
object->center_around_origin(); // also aligns object to Z = 0
auto *instance = object->add_instance();
ModelInstance* instance = object->add_instance();
#if ENABLE_MODELVOLUME_TRANSFORM
instance->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -object->origin_translation(2)));
#else
instance->set_offset(bed_center);
#endif // ENABLE_MODELVOLUME_TRANSFORM
}
const Vec3d size = object->bounding_box().size();
@ -1438,7 +1453,9 @@ void Plater::priv::mirror(Axis axis)
void Plater::priv::arrange()
{
this->background_process.stop();
main_frame->app_controller()->arrange_model();
this->schedule_background_process();
// ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
// when parts don't fit in print bed
@ -1471,13 +1488,7 @@ void Plater::priv::split_object()
{
unsigned int counter = 1;
for (ModelObject* m : new_objects)
{
m->name = current_model_object->name + "_" + std::to_string(counter++);
for (ModelInstance* i : current_model_object->instances)
{
m->add_instance(*i);
}
}
remove(obj_idx);
@ -2108,7 +2119,8 @@ void Plater::export_amf()
wxString path = dialog->GetPath();
auto path_cstr = path.c_str();
if (Slic3r::store_amf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) {
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config();
if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) {
// Success
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
} else {
@ -2127,7 +2139,8 @@ void Plater::export_3mf()
wxString path = dialog->GetPath();
auto path_cstr = path.c_str();
if (Slic3r::store_3mf(path_cstr, &p->model, &p->print, dialog->get_checkbox_value())) {
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config();
if (Slic3r::store_3mf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) {
// Success
p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path));
} else {
@ -2256,7 +2269,9 @@ void Plater::changed_object(int obj_idx)
if (list->is_parts_changed()) {
// recenter and re - align to Z = 0
auto model_object = p->model.objects[obj_idx];
#if !ENABLE_MODELVOLUME_TRANSFORM
model_object->center_around_origin();
#endif // !ENABLE_MODELVOLUME_TRANSFORM
model_object->ensure_on_bed();
_3DScene::reload_scene(p->canvas3D, false);
}

View File

@ -170,10 +170,12 @@ void Preset::set_num_extruders(DynamicPrintConfig &config, unsigned int num_extr
{
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : Preset::nozzle_options()) {
if (key == "default_filament_profile")
continue;
auto *opt = config.option(key, false);
assert(opt != nullptr);
assert(opt->is_vector());
if (opt != nullptr && opt->is_vector() && key != "default_filament_profile")
if (opt != nullptr && opt->is_vector())
static_cast<ConfigOptionVectorBase*>(opt)->resize(num_extruders, defaults.option(key));
}
}
@ -202,8 +204,7 @@ void Preset::normalize(DynamicPrintConfig &config)
// The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
for (const std::string &key : { "filament_settings_id" }) {
auto *opt = config.option(key, false);
assert(opt != nullptr);
assert(opt->type() == coStrings);
assert(opt == nullptr || opt->type() == coStrings);
if (opt != nullptr && opt->type() == coStrings)
static_cast<ConfigOptionStrings*>(opt)->values.resize(n, std::string());
}
@ -534,7 +535,8 @@ Preset& PresetCollection::load_external_preset(
cfg.apply_only(config, cfg.keys(), true);
// Is there a preset already loaded with the name stored inside the config?
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
if (it != m_presets.end() && it->name == original_name && profile_print_params_same(it->config, cfg)) {
bool found = it != m_presets.end() && it->name == original_name;
if (found && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select)
this->select_preset(it - m_presets.begin());
@ -542,7 +544,7 @@ Preset& PresetCollection::load_external_preset(
}
// Update the "inherits" field.
std::string &inherits = Preset::inherits(cfg);
if (it != m_presets.end() && inherits.empty()) {
if (found && inherits.empty()) {
// There is a profile with the same name already loaded. Should we update the "inherits" field?
if (it->vendor == nullptr)
inherits = it->inherits();

View File

@ -36,6 +36,7 @@
namespace Slic3r {
static std::vector<std::string> s_project_options {
"colorprint_heights",
"wiping_volumes_extruders",
"wiping_volumes_matrix"
};
@ -617,18 +618,6 @@ void PresetBundle::load_config_file(const std::string &path)
}
}
void PresetBundle::load_config_string(const char* str, const char* source_filename)
{
if (str != nullptr)
{
DynamicPrintConfig config;
config.apply(FullPrintConfig::defaults());
config.load_from_gcode_string(str);
Preset::normalize(config);
load_config_file_config((source_filename == nullptr) ? "" : source_filename, true, std::move(config));
}
}
// Load a config file from a boost property_tree. This is a private method called from load_config_file.
void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config)
{
@ -676,7 +665,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
compatible_printers_condition = compatible_printers_condition_values[idx];
if (is_external)
presets.load_external_preset(name_or_path, name,
config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_id") : "printer_settings_id", true),
config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_settings_id") : "printer_settings_id", true),
config);
else
presets.load_preset(presets.path_from_name(name), name, config).save();

View File

@ -84,11 +84,6 @@ public:
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
void load_config_file(const std::string &path);
// Load an external config source containing the print, filament and printer presets.
// The given string must contain the full set of parameters (same as those exported to gcode).
// If the string is parsed successfully, its print / filament / printer profiles will be activated.
void load_config_string(const char* str, const char* source_filename = nullptr);
// Load a config bundle file, into presets and store the loaded presets into separate files
// of the local configuration directory.
// Load settings into the provided settings instance.

View File

@ -108,7 +108,7 @@ RammingPanel::RammingPanel(wxWindow* parent, const std::string& parameters)
m_widget_time->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value
m_widget_volume->Bind(wxEVT_CHAR,[](wxKeyEvent&){}); // do nothing - prevents the user to change the value
Bind(EVT_WIPE_TOWER_CHART_CHANGED,[this](wxCommandEvent&) {m_widget_volume->SetValue(m_chart->get_volume()); m_widget_time->SetValue(m_chart->get_time());} );
Refresh(this);
Refresh(true); // erase background
}
void RammingPanel::line_parameters_changed() {

View File

@ -9,6 +9,9 @@
#include <wx/numformatter.h>
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
#include "Model.hpp"
wxDEFINE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description,
std::function<void(wxCommandEvent& event)> cb, const std::string& icon, wxEvtHandler* event_handler)
@ -400,10 +403,9 @@ bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector<std:
m_opt_categories = categories;
m_name = wxEmptyString;
// m_icon = m_empty_icon;
m_bmp = m_empty_bmp;
std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;//Slic3r::GUI::get_category_icon();
std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;
for (auto& cat : m_opt_categories)
m_name += cat + "; ";
@ -453,29 +455,34 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name)
wxDataViewItem PrusaObjectDataViewModel::AddVolumeChild(const wxDataViewItem &parent_item,
const wxString &name,
const wxBitmap& icon,
const int volume_type,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
{
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
if (!root) return wxDataViewItem(0);
const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
// because of istance_root is a last item of the object
int insert_position = root->GetChildCount() - 1;
if (insert_position < 0 || root->GetNthChild(insert_position)->m_type != itInstanceRoot)
insert_position = -1;
if (create_frst_child && root->m_volumes_cnt == 0)
{
const auto bmp_solid_mesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);
const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, bmp_solid_mesh, extruder_str, 0);
root->Append(node);
const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, *m_volume_bmps[0], extruder_str, 0);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
root->m_volumes_cnt++;
if (insert_position > 0) insert_position++;
}
const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, root->m_volumes_cnt);
root->Append(node);
const auto node = new PrusaObjectDataViewModelNode(root, name, *m_volume_bmps[volume_type], extruder_str, root->m_volumes_cnt);
insert_position < 0 ? root->Append(node) : root->Insert(node, insert_position);
// notify control
const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
@ -499,15 +506,13 @@ wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem &
int get_istances_root_idx(PrusaObjectDataViewModelNode *parent_node)
{
int inst_root_id = -1;
int stop_search_i = parent_node->GetChildCount();
if (stop_search_i > 2) stop_search_i = 2;
for (int i = 0; i < stop_search_i; ++i)
if (parent_node->GetNthChild(i)->m_type & itInstanceRoot) {
inst_root_id = i;
break;
}
return inst_root_id;
// because of istance_root is a last item of the object
const int inst_root_idx = parent_node->GetChildCount()-1;
if (inst_root_idx < 0 || parent_node->GetNthChild(inst_root_idx)->m_type == itInstanceRoot)
return inst_root_idx;
return -1;
}
wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &parent_item, size_t num)
@ -524,8 +529,7 @@ wxDataViewItem PrusaObjectDataViewModel::AddInstanceChild(const wxDataViewItem &
const wxDataViewItem inst_root_item((void*)inst_root_node);
if (inst_root_id < 0) {
const unsigned insert_pos = parent_node->GetChildCount() == 0 || parent_node->GetNthChild(0)->m_type != itSettings ? 0 : 1;
parent_node->Insert(inst_root_node, insert_pos);
parent_node->Append(inst_root_node);
// notify control
ItemAdded(parent_item, inst_root_item);
if (num == 1) num++;
@ -563,6 +567,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
auto id = node_parent->GetChildren().Index(node);
auto idx = node->GetIdx();
node_parent->GetChildren().Remove(node);
if (node->m_type == itVolume)
node_parent->m_volumes_cnt--;
if (id > 0) {
if(id == node_parent->GetChildCount()) id--;
ret_item = wxDataViewItem(node_parent->GetChildren().Item(id));
@ -692,6 +700,9 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent)
auto item = wxDataViewItem(node);
children.RemoveAt(id);
if (node->m_type == itVolume)
root->m_volumes_cnt--;
// free the node
delete node;
@ -705,6 +716,39 @@ void PrusaObjectDataViewModel::DeleteChildren(wxDataViewItem& parent)
#endif //__WXGTK__
}
void PrusaObjectDataViewModel::DeleteVolumeChildren(wxDataViewItem& parent)
{
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent.GetID();
if (!root) // happens if item.IsOk()==false
return;
// first remove the node from the parent's array of children;
// NOTE: MyObjectTreeModelNodePtrArray is only an array of _pointers_
// thus removing the node from it doesn't result in freeing it
auto& children = root->GetChildren();
for (int id = root->GetChildCount() - 1; id >= 0; --id)
{
auto node = children[id];
if (node->m_type != itVolume)
continue;
auto item = wxDataViewItem(node);
children.RemoveAt(id);
root->m_volumes_cnt--;
// free the node
delete node;
// notify control
ItemDeleted(parent, item);
}
// set m_containet to FALSE if parent has no child
#ifndef __WXGTK__
root->m_container = false;
#endif //__WXGTK__
}
wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx)
{
if (obj_idx >= m_objects.size())
@ -718,7 +762,7 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx)
wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx)
{
if (obj_idx >= m_objects.size()) {
if (obj_idx >= m_objects.size() || obj_idx < 0) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
@ -740,6 +784,25 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volu
return wxDataViewItem(0);
}
wxDataViewItem PrusaObjectDataViewModel::GetItemByInstanceId(int obj_idx, int inst_idx)
{
if (obj_idx >= m_objects.size() || obj_idx < 0) {
printf("Error! Out of objects range.\n");
return wxDataViewItem(0);
}
auto instances_item = GetInstanceRootItem(wxDataViewItem(m_objects[obj_idx]));
if (!instances_item)
return wxDataViewItem(0);
auto parent = (PrusaObjectDataViewModelNode*)instances_item.GetID();;
for (size_t i = 0; i < parent->GetChildCount(); i++)
if (parent->GetNthChild(i)->m_idx == inst_idx)
return wxDataViewItem(parent->GetNthChild(i));
return wxDataViewItem(0);
}
int PrusaObjectDataViewModel::GetIdByItem(const wxDataViewItem& item)
{
wxASSERT(item.IsOk());
@ -1015,21 +1078,33 @@ ItemType PrusaObjectDataViewModel::GetItemType(const wxDataViewItem &item) const
return node->m_type;
}
wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const
wxDataViewItem PrusaObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_item, ItemType type) const
{
if (!item.IsOk())
if (!parent_item.IsOk())
return wxDataViewItem(0);
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)parent_item.GetID();
if (node->GetChildCount() == 0)
return wxDataViewItem(0);
if (node->GetNthChild(0)->m_type == itSettings)
return wxDataViewItem((void*)node->GetNthChild(0));
for (int i = 0; i < node->GetChildCount(); i++) {
if (node->GetNthChild(i)->m_type == type)
return wxDataViewItem((void*)node->GetNthChild(i));
}
return wxDataViewItem(0);
}
wxDataViewItem PrusaObjectDataViewModel::GetSettingsItem(const wxDataViewItem &item) const
{
return GetItemByType(item, itSettings);
}
wxDataViewItem PrusaObjectDataViewModel::GetInstanceRootItem(const wxDataViewItem &item) const
{
return GetItemByType(item, itInstanceRoot);
}
bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
{
if (!item.IsOk())
@ -1050,6 +1125,16 @@ void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item,
ItemChanged(item);
}
void PrusaObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const int type)
{
if (!item.IsOk() || GetItemType(item) != itVolume)
return;
PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
node->SetBitmap(*m_volume_bmps[type]);
ItemChanged(item);
}
//-----------------------------------------------------------------------------
// PrusaDataViewBitmapText
//-----------------------------------------------------------------------------
@ -1106,7 +1191,6 @@ wxSize PrusaBitmapTextRenderer::GetSize() const
// ----------------------------------------------------------------------------
// PrusaDoubleSlider
// ----------------------------------------------------------------------------
PrusaDoubleSlider::PrusaDoubleSlider(wxWindow *parent,
wxWindowID id,
int lowerValue,
@ -1288,6 +1372,34 @@ double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) cons
return m_values[selection == ssLower ? m_lower_value : m_higher_value].second;
}
std::vector<double> PrusaDoubleSlider::GetTicksValues() const
{
std::vector<double> values;
if (!m_values.empty())
for (auto tick : m_ticks)
values.push_back(m_values[tick].second);
return values;
}
void PrusaDoubleSlider::SetTicksValues(const std::vector<double>& heights)
{
if (m_values.empty())
return;
m_ticks.clear();
unsigned int i = 0;
for (auto h : heights) {
while (i < m_values.size() && m_values[i].second - 1e-6 < h)
++i;
if (i == m_values.size())
return;
m_ticks.insert(i);
}
}
void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
{
const double step = get_scroll_step();
@ -1712,10 +1824,13 @@ void PrusaDoubleSlider::action_tick(const TicksAction action)
m_ticks.insert(tick);
else if (it != m_ticks.end() && action == taDel)
m_ticks.erase(tick);
else
else {
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
return;
}
}
wxPostEvent(this->GetParent(), wxCommandEvent(wxCUSTOMEVT_TICKSCHANGED));
Refresh();
Update();
}

View File

@ -424,7 +424,9 @@ private:
class PrusaObjectDataViewModel :public wxDataViewModel
{
std::vector<PrusaObjectDataViewModelNode*> m_objects;
std::vector<PrusaObjectDataViewModelNode*> m_objects;
std::vector<wxBitmap*> m_volume_bmps;
public:
PrusaObjectDataViewModel();
~PrusaObjectDataViewModel();
@ -432,7 +434,7 @@ public:
wxDataViewItem Add(const wxString &name);
wxDataViewItem AddVolumeChild(const wxDataViewItem &parent_item,
const wxString &name,
const wxBitmap& icon,
const int volume_type,
const int extruder = 0,
const bool create_frst_child = true);
wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
@ -441,8 +443,10 @@ public:
wxDataViewItem DeleteLastInstance(const wxDataViewItem &parent_item, size_t num);
void DeleteAll();
void DeleteChildren(wxDataViewItem& parent);
void DeleteVolumeChildren(wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
wxDataViewItem GetItemByInstanceId(int obj_idx, int inst_idx);
int GetIdByItem(const wxDataViewItem& item);
int GetIdByItemAndType(const wxDataViewItem& item, const ItemType type) const;
int GetVolumeIdByItem(const wxDataViewItem& item) const;
@ -488,9 +492,14 @@ public:
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
ItemType GetItemType(const wxDataViewItem &item) const ;
wxDataViewItem GetItemByType(const wxDataViewItem &parent_item, ItemType type) const;
wxDataViewItem GetSettingsItem(const wxDataViewItem &item) const;
wxDataViewItem GetInstanceRootItem(const wxDataViewItem &item) const;
bool IsSettingsItem(const wxDataViewItem &item) const;
void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories);
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
void SetVolumeType(const wxDataViewItem &item, const int type);
};
// ----------------------------------------------------------------------------
@ -613,6 +622,9 @@ private:
// PrusaDoubleSlider
// ----------------------------------------------------------------------------
// custom message the slider sends to its parent to notify a tick-change:
wxDECLARE_EVENT(wxCUSTOMEVT_TICKSCHANGED, wxEvent);
enum SelectedSlider {
ssUndef,
ssLower,
@ -623,6 +635,7 @@ enum TicksAction{
taAdd,
taDel
};
class PrusaDoubleSlider : public wxControl
{
public:
@ -660,6 +673,8 @@ public:
m_values = values;
}
void ChangeOneLayerLock();
std::vector<double> GetTicksValues() const;
void SetTicksValues(const std::vector<double>& heights);
void OnPaint(wxPaintEvent& ) { render();}
void OnLeftDown(wxMouseEvent& event);

View File

@ -32,7 +32,7 @@
%name{_add_object} Ref<ModelObject> add_object();
Ref<ModelObject> _add_object_clone(ModelObject* other, bool copy_volumes = true)
%code%{ RETVAL = THIS->add_object(*other, copy_volumes); %};
%code%{ auto ptr = THIS->add_object(*other); if (! copy_volumes) ptr->clear_volumes(); RETVAL = ptr; %};
void delete_object(size_t idx);
void clear_objects();
size_t objects_count()
@ -286,8 +286,6 @@ ModelMaterial::attributes()
%code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
size_t split(unsigned int max_extruders);
ModelMaterial* assign_unique_material();
};

View File

@ -44,8 +44,6 @@ _constant()
int region_count()
%code%{ RETVAL = THIS->print()->regions().size(); %};
int region_volumes_count()
%code%{ RETVAL = THIS->region_volumes.size(); %};
Ref<Print> print();
Ref<ModelObject> model_object();
Ref<StaticPrintConfig> config()