Merge remote-tracking branch 'origin/dev_native' into tm_sla_supports
This commit is contained in:
commit
97b3d94760
BIN
resources/icons/support_blocker_.png
Normal file
BIN
resources/icons/support_blocker_.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
resources/icons/support_enforcer_.png
Normal file
BIN
resources/icons/support_enforcer_.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
@ -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)) :
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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() ?
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 ®ion = *print.regions()[region_id];
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)); }
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 ®ion = *m_print->regions()[region_id];
|
||||
for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
|
||||
const PrintRegion ®ion = *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 ®ion = *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 ®ion = *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 ®ion = *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 ®ion = *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 ®ion = *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.)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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, ...)
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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; });
|
||||
}
|
||||
|
@ -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
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -60,7 +60,7 @@ enum ConfigMenuIDs {
|
||||
|
||||
class Tab;
|
||||
|
||||
static wxString dots("…", wxConvUTF8);
|
||||
static wxString dots("…", wxConvUTF8);
|
||||
|
||||
class GUI_App : public wxApp
|
||||
{
|
||||
|
@ -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
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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() {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user